sync
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31        
32     this.addEvents({
33         /**
34          * @event childrenrendered
35          * Fires when the children have been rendered..
36          * @param {Roo.bootstrap.Component} this
37          */
38         "childrenrendered" : true
39         
40         
41         
42     });
43     
44     
45 };
46
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
48     
49     
50     allowDomMove : false, // to stop relocations in parent onRender...
51     
52     cls : false,
53     
54     style : false,
55     
56     autoCreate : false,
57     
58     tooltip : null,
59     /**
60      * Initialize Events for the element
61      */
62     initEvents : function() { },
63     
64     xattr : false,
65     
66     parentId : false,
67     
68     can_build_overlaid : true,
69     
70     container_method : false,
71     
72     dataId : false,
73     
74     name : false,
75     
76     parent: function() {
77         // returns the parent component..
78         return Roo.ComponentMgr.get(this.parentId)
79         
80         
81     },
82     
83     // private
84     onRender : function(ct, position)
85     {
86        // Roo.log("Call onRender: " + this.xtype);
87         
88         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
89         
90         if(this.el){
91             if (this.el.attr('xtype')) {
92                 this.el.attr('xtypex', this.el.attr('xtype'));
93                 this.el.dom.removeAttribute('xtype');
94                 
95                 this.initEvents();
96             }
97             
98             return;
99         }
100         
101          
102         
103         var cfg = Roo.apply({},  this.getAutoCreate());
104         
105         cfg.id = this.id || Roo.id();
106         
107         // fill in the extra attributes 
108         if (this.xattr && typeof(this.xattr) =='object') {
109             for (var i in this.xattr) {
110                 cfg[i] = this.xattr[i];
111             }
112         }
113         
114         if(this.dataId){
115             cfg.dataId = this.dataId;
116         }
117         
118         if (this.cls) {
119             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
120         }
121         
122         if (this.style) { // fixme needs to support more complex style data.
123             cfg.style = this.style;
124         }
125         
126         if(this.name){
127             cfg.name = this.name;
128         }
129         
130         this.el = ct.createChild(cfg, position);
131         
132         if (this.tooltip) {
133             this.tooltipEl().attr('tooltip', this.tooltip);
134         }
135         
136         if(this.tabIndex !== undefined){
137             this.el.dom.setAttribute('tabIndex', this.tabIndex);
138         }
139         
140         this.initEvents();
141         
142     },
143     /**
144      * Fetch the element to add children to
145      * @return {Roo.Element} defaults to this.el
146      */
147     getChildContainer : function()
148     {
149         return this.el;
150     },
151     /**
152      * Fetch the element to display the tooltip on.
153      * @return {Roo.Element} defaults to this.el
154      */
155     tooltipEl : function()
156     {
157         return this.el;
158     },
159         
160     addxtype  : function(tree,cntr)
161     {
162         var cn = this;
163         
164         cn = Roo.factory(tree);
165         //Roo.log(['addxtype', cn]);
166            
167         cn.parentType = this.xtype; //??
168         cn.parentId = this.id;
169         
170         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
171         if (typeof(cn.container_method) == 'string') {
172             cntr = cn.container_method;
173         }
174         
175         
176         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
177         
178         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
179         
180         var build_from_html =  Roo.XComponent.build_from_html;
181           
182         var is_body  = (tree.xtype == 'Body') ;
183           
184         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
185           
186         var self_cntr_el = Roo.get(this[cntr](false));
187         
188         // do not try and build conditional elements 
189         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
190             return false;
191         }
192         
193         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
194             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
195                 return this.addxtypeChild(tree,cntr, is_body);
196             }
197             
198             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
199                 
200             if(echild){
201                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
202             }
203             
204             Roo.log('skipping render');
205             return cn;
206             
207         }
208         
209         var ret = false;
210         if (!build_from_html) {
211             return false;
212         }
213         
214         // this i think handles overlaying multiple children of the same type
215         // with the sam eelement.. - which might be buggy..
216         while (true) {
217             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
218             
219             if (!echild) {
220                 break;
221             }
222             
223             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
224                 break;
225             }
226             
227             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
228         }
229        
230         return ret;
231     },
232     
233     
234     addxtypeChild : function (tree, cntr, is_body)
235     {
236         Roo.debug && Roo.log('addxtypeChild:' + cntr);
237         var cn = this;
238         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
239         
240         
241         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
242                     (typeof(tree['flexy:foreach']) != 'undefined');
243           
244         
245         
246          skip_children = false;
247         // render the element if it's not BODY.
248         if (!is_body) {
249            
250             cn = Roo.factory(tree);
251            
252             cn.parentType = this.xtype; //??
253             cn.parentId = this.id;
254             
255             var build_from_html =  Roo.XComponent.build_from_html;
256             
257             
258             // does the container contain child eleemnts with 'xtype' attributes.
259             // that match this xtype..
260             // note - when we render we create these as well..
261             // so we should check to see if body has xtype set.
262             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
263                
264                 var self_cntr_el = Roo.get(this[cntr](false));
265                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
266                 if (echild) { 
267                     //Roo.log(Roo.XComponent.build_from_html);
268                     //Roo.log("got echild:");
269                     //Roo.log(echild);
270                 }
271                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
272                 // and are not displayed -this causes this to use up the wrong element when matching.
273                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
274                 
275                 
276                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
277                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
278                   
279                   
280                   
281                     cn.el = echild;
282                   //  Roo.log("GOT");
283                     //echild.dom.removeAttribute('xtype');
284                 } else {
285                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
286                     Roo.debug && Roo.log(self_cntr_el);
287                     Roo.debug && Roo.log(echild);
288                     Roo.debug && Roo.log(cn);
289                 }
290             }
291            
292             
293            
294             // if object has flexy:if - then it may or may not be rendered.
295             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
296                 // skip a flexy if element.
297                 Roo.debug && Roo.log('skipping render');
298                 Roo.debug && Roo.log(tree);
299                 if (!cn.el) {
300                     Roo.debug && Roo.log('skipping all children');
301                     skip_children = true;
302                 }
303                 
304              } else {
305                  
306                 // actually if flexy:foreach is found, we really want to create 
307                 // multiple copies here...
308                 //Roo.log('render');
309                 //Roo.log(this[cntr]());
310                 // some elements do not have render methods.. like the layouts...
311                 cn.render && cn.render(this[cntr](true));
312              }
313             // then add the element..
314         }
315         
316         
317         // handle the kids..
318         
319         var nitems = [];
320         /*
321         if (typeof (tree.menu) != 'undefined') {
322             tree.menu.parentType = cn.xtype;
323             tree.menu.triggerEl = cn.el;
324             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
325             
326         }
327         */
328         if (!tree.items || !tree.items.length) {
329             cn.items = nitems;
330             //Roo.log(["no children", this]);
331             
332             return cn;
333         }
334          
335         var items = tree.items;
336         delete tree.items;
337         
338         //Roo.log(items.length);
339             // add the items..
340         if (!skip_children) {    
341             for(var i =0;i < items.length;i++) {
342               //  Roo.log(['add child', items[i]]);
343                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
344             }
345         }
346         
347         cn.items = nitems;
348         
349         //Roo.log("fire childrenrendered");
350         
351         cn.fireEvent('childrenrendered', this);
352         
353         return cn;
354     },
355     /**
356      * Show a component - removes 'hidden' class
357      */
358     show : function()
359     {
360         if (this.el) {
361             this.el.removeClass('hidden');
362         }
363     },
364     /**
365      * Hide a component - adds 'hidden' class
366      */
367     hide: function()
368     {
369         if (this.el && !this.el.hasClass('hidden')) {
370             this.el.addClass('hidden');
371         }
372         
373     }
374 });
375
376  /*
377  * - LGPL
378  *
379  * Body
380  *
381  */
382
383 /**
384  * @class Roo.bootstrap.Body
385  * @extends Roo.bootstrap.Component
386  * Bootstrap Body class
387  *
388  * @constructor
389  * Create a new body
390  * @param {Object} config The config object
391  */
392
393 Roo.bootstrap.Body = function(config){
394
395     config = config || {};
396
397     Roo.bootstrap.Body.superclass.constructor.call(this, config);
398     this.el = Roo.get(config.el ? config.el : document.body );
399     if (this.cls && this.cls.length) {
400         Roo.get(document.body).addClass(this.cls);
401     }
402 };
403
404 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
405
406     is_body : true,// just to make sure it's constructed?
407
408         autoCreate : {
409         cls: 'container'
410     },
411     onRender : function(ct, position)
412     {
413        /* Roo.log("Roo.bootstrap.Body - onRender");
414         if (this.cls && this.cls.length) {
415             Roo.get(document.body).addClass(this.cls);
416         }
417         // style??? xttr???
418         */
419     }
420
421
422
423
424 });
425 /*
426  * - LGPL
427  *
428  * button group
429  * 
430  */
431
432
433 /**
434  * @class Roo.bootstrap.ButtonGroup
435  * @extends Roo.bootstrap.Component
436  * Bootstrap ButtonGroup class
437  * @cfg {String} size lg | sm | xs (default empty normal)
438  * @cfg {String} align vertical | justified  (default none)
439  * @cfg {String} direction up | down (default down)
440  * @cfg {Boolean} toolbar false | true
441  * @cfg {Boolean} btn true | false
442  * 
443  * 
444  * @constructor
445  * Create a new Input
446  * @param {Object} config The config object
447  */
448
449 Roo.bootstrap.ButtonGroup = function(config){
450     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
451 };
452
453 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
454     
455     size: '',
456     align: '',
457     direction: '',
458     toolbar: false,
459     btn: true,
460
461     getAutoCreate : function(){
462         var cfg = {
463             cls: 'btn-group',
464             html : null
465         };
466         
467         cfg.html = this.html || cfg.html;
468         
469         if (this.toolbar) {
470             cfg = {
471                 cls: 'btn-toolbar',
472                 html: null
473             };
474             
475             return cfg;
476         }
477         
478         if (['vertical','justified'].indexOf(this.align)!==-1) {
479             cfg.cls = 'btn-group-' + this.align;
480             
481             if (this.align == 'justified') {
482                 console.log(this.items);
483             }
484         }
485         
486         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
487             cfg.cls += ' btn-group-' + this.size;
488         }
489         
490         if (this.direction == 'up') {
491             cfg.cls += ' dropup' ;
492         }
493         
494         return cfg;
495     }
496    
497 });
498
499  /*
500  * - LGPL
501  *
502  * button
503  * 
504  */
505
506 /**
507  * @class Roo.bootstrap.Button
508  * @extends Roo.bootstrap.Component
509  * Bootstrap Button class
510  * @cfg {String} html The button content
511  * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default 
512  * @cfg {String} size ( lg | sm | xs)
513  * @cfg {String} tag ( a | input | submit)
514  * @cfg {String} href empty or href
515  * @cfg {Boolean} disabled default false;
516  * @cfg {Boolean} isClose default false;
517  * @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)
518  * @cfg {String} badge text for badge
519  * @cfg {String} theme default 
520  * @cfg {Boolean} inverse 
521  * @cfg {Boolean} toggle 
522  * @cfg {String} ontext text for on toggle state
523  * @cfg {String} offtext text for off toggle state
524  * @cfg {Boolean} defaulton 
525  * @cfg {Boolean} preventDefault  default true
526  * @cfg {Boolean} removeClass remove the standard class..
527  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
528  * 
529  * @constructor
530  * Create a new button
531  * @param {Object} config The config object
532  */
533
534
535 Roo.bootstrap.Button = function(config){
536     Roo.bootstrap.Button.superclass.constructor.call(this, config);
537     this.weightClass = ["btn-default", 
538                        "btn-primary", 
539                        "btn-success", 
540                        "btn-info", 
541                        "btn-warning",
542                        "btn-danger",
543                        "btn-link"
544                       ],  
545     this.addEvents({
546         // raw events
547         /**
548          * @event click
549          * When a butotn is pressed
550          * @param {Roo.bootstrap.Button} this
551          * @param {Roo.EventObject} e
552          */
553         "click" : true,
554          /**
555          * @event toggle
556          * After the button has been toggles
557          * @param {Roo.EventObject} e
558          * @param {boolean} pressed (also available as button.pressed)
559          */
560         "toggle" : true
561     });
562 };
563
564 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
565     html: false,
566     active: false,
567     weight: '',
568     size: '',
569     tag: 'button',
570     href: '',
571     disabled: false,
572     isClose: false,
573     glyphicon: '',
574     badge: '',
575     theme: 'default',
576     inverse: false,
577     
578     toggle: false,
579     ontext: 'ON',
580     offtext: 'OFF',
581     defaulton: true,
582     preventDefault: true,
583     removeClass: false,
584     name: false,
585     target: false,
586     
587     
588     pressed : null,
589      
590     
591     getAutoCreate : function(){
592         
593         var cfg = {
594             tag : 'button',
595             cls : 'roo-button',
596             html: ''
597         };
598         
599         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
600             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
601             this.tag = 'button';
602         } else {
603             cfg.tag = this.tag;
604         }
605         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
606         
607         if (this.toggle == true) {
608             cfg={
609                 tag: 'div',
610                 cls: 'slider-frame roo-button',
611                 cn: [
612                     {
613                         tag: 'span',
614                         'data-on-text':'ON',
615                         'data-off-text':'OFF',
616                         cls: 'slider-button',
617                         html: this.offtext
618                     }
619                 ]
620             };
621             
622             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
623                 cfg.cls += ' '+this.weight;
624             }
625             
626             return cfg;
627         }
628         
629         if (this.isClose) {
630             cfg.cls += ' close';
631             
632             cfg["aria-hidden"] = true;
633             
634             cfg.html = "&times;";
635             
636             return cfg;
637         }
638         
639          
640         if (this.theme==='default') {
641             cfg.cls = 'btn roo-button';
642             
643             //if (this.parentType != 'Navbar') {
644             this.weight = this.weight.length ?  this.weight : 'default';
645             //}
646             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
647                 
648                 cfg.cls += ' btn-' + this.weight;
649             }
650         } else if (this.theme==='glow') {
651             
652             cfg.tag = 'a';
653             cfg.cls = 'btn-glow roo-button';
654             
655             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
656                 
657                 cfg.cls += ' ' + this.weight;
658             }
659         }
660    
661         
662         if (this.inverse) {
663             this.cls += ' inverse';
664         }
665         
666         
667         if (this.active) {
668             cfg.cls += ' active';
669         }
670         
671         if (this.disabled) {
672             cfg.disabled = 'disabled';
673         }
674         
675         if (this.items) {
676             Roo.log('changing to ul' );
677             cfg.tag = 'ul';
678             this.glyphicon = 'caret';
679         }
680         
681         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
682          
683         //gsRoo.log(this.parentType);
684         if (this.parentType === 'Navbar' && !this.parent().bar) {
685             Roo.log('changing to li?');
686             
687             cfg.tag = 'li';
688             
689             cfg.cls = '';
690             cfg.cn =  [{
691                 tag : 'a',
692                 cls : 'roo-button',
693                 html : this.html,
694                 href : this.href || '#'
695             }];
696             if (this.menu) {
697                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
698                 cfg.cls += ' dropdown';
699             }   
700             
701             delete cfg.html;
702             
703         }
704         
705        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
706         
707         if (this.glyphicon) {
708             cfg.html = ' ' + cfg.html;
709             
710             cfg.cn = [
711                 {
712                     tag: 'span',
713                     cls: 'glyphicon glyphicon-' + this.glyphicon
714                 }
715             ];
716         }
717         
718         if (this.badge) {
719             cfg.html += ' ';
720             
721             cfg.tag = 'a';
722             
723 //            cfg.cls='btn roo-button';
724             
725             cfg.href=this.href;
726             
727             var value = cfg.html;
728             
729             if(this.glyphicon){
730                 value = {
731                             tag: 'span',
732                             cls: 'glyphicon glyphicon-' + this.glyphicon,
733                             html: this.html
734                         };
735                 
736             }
737             
738             cfg.cn = [
739                 value,
740                 {
741                     tag: 'span',
742                     cls: 'badge',
743                     html: this.badge
744                 }
745             ];
746             
747             cfg.html='';
748         }
749         
750         if (this.menu) {
751             cfg.cls += ' dropdown';
752             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
753         }
754         
755         if (cfg.tag !== 'a' && this.href !== '') {
756             throw "Tag must be a to set href.";
757         } else if (this.href.length > 0) {
758             cfg.href = this.href;
759         }
760         
761         if(this.removeClass){
762             cfg.cls = '';
763         }
764         
765         if(this.target){
766             cfg.target = this.target;
767         }
768         
769         return cfg;
770     },
771     initEvents: function() {
772        // Roo.log('init events?');
773 //        Roo.log(this.el.dom);
774         // add the menu...
775         
776         if (typeof (this.menu) != 'undefined') {
777             this.menu.parentType = this.xtype;
778             this.menu.triggerEl = this.el;
779             this.addxtype(Roo.apply({}, this.menu));
780         }
781
782
783        if (this.el.hasClass('roo-button')) {
784             this.el.on('click', this.onClick, this);
785        } else {
786             this.el.select('.roo-button').on('click', this.onClick, this);
787        }
788        
789        if(this.removeClass){
790            this.el.on('click', this.onClick, this);
791        }
792        
793        this.el.enableDisplayMode();
794         
795     },
796     onClick : function(e)
797     {
798         if (this.disabled) {
799             return;
800         }
801         
802         
803         Roo.log('button on click ');
804         if(this.preventDefault){
805             e.preventDefault();
806         }
807         if (this.pressed === true || this.pressed === false) {
808             this.pressed = !this.pressed;
809             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
810             this.fireEvent('toggle', this, e, this.pressed);
811         }
812         
813         
814         this.fireEvent('click', this, e);
815     },
816     
817     /**
818      * Enables this button
819      */
820     enable : function()
821     {
822         this.disabled = false;
823         this.el.removeClass('disabled');
824     },
825     
826     /**
827      * Disable this button
828      */
829     disable : function()
830     {
831         this.disabled = true;
832         this.el.addClass('disabled');
833     },
834      /**
835      * sets the active state on/off, 
836      * @param {Boolean} state (optional) Force a particular state
837      */
838     setActive : function(v) {
839         
840         this.el[v ? 'addClass' : 'removeClass']('active');
841     },
842      /**
843      * toggles the current active state 
844      */
845     toggleActive : function()
846     {
847        var active = this.el.hasClass('active');
848        this.setActive(!active);
849        
850         
851     },
852     setText : function(str)
853     {
854         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
855     },
856     getText : function()
857     {
858         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
859     },
860     hide: function() {
861        
862      
863         this.el.hide();   
864     },
865     show: function() {
866        
867         this.el.show();   
868     },
869     setWeight : function(str)
870     {
871           this.el.removeClass(this.weightClass);
872         this.el.addClass('btn-' + str);        
873     }
874     
875     
876 });
877
878  /*
879  * - LGPL
880  *
881  * column
882  * 
883  */
884
885 /**
886  * @class Roo.bootstrap.Column
887  * @extends Roo.bootstrap.Component
888  * Bootstrap Column class
889  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
890  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
891  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
892  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
893  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
894  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
895  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
896  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
897  *
898  * 
899  * @cfg {Boolean} hidden (true|false) hide the element
900  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
901  * @cfg {String} fa (ban|check|...) font awesome icon
902  * @cfg {Number} fasize (1|2|....) font awsome size
903
904  * @cfg {String} icon (info-sign|check|...) glyphicon name
905
906  * @cfg {String} html content of column.
907  * 
908  * @constructor
909  * Create a new Column
910  * @param {Object} config The config object
911  */
912
913 Roo.bootstrap.Column = function(config){
914     Roo.bootstrap.Column.superclass.constructor.call(this, config);
915 };
916
917 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
918     
919     xs: false,
920     sm: false,
921     md: false,
922     lg: false,
923     xsoff: false,
924     smoff: false,
925     mdoff: false,
926     lgoff: false,
927     html: '',
928     offset: 0,
929     alert: false,
930     fa: false,
931     icon : false,
932     hidden : false,
933     fasize : 1,
934     
935     getAutoCreate : function(){
936         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
937         
938         cfg = {
939             tag: 'div',
940             cls: 'column'
941         };
942         
943         var settings=this;
944         ['xs','sm','md','lg'].map(function(size){
945             //Roo.log( size + ':' + settings[size]);
946             
947             if (settings[size+'off'] !== false) {
948                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
949             }
950             
951             if (settings[size] === false) {
952                 return;
953             }
954             
955             if (!settings[size]) { // 0 = hidden
956                 cfg.cls += ' hidden-' + size;
957                 return;
958             }
959             cfg.cls += ' col-' + size + '-' + settings[size];
960             
961         });
962         
963         if (this.hidden) {
964             cfg.cls += ' hidden';
965         }
966         
967         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
968             cfg.cls +=' alert alert-' + this.alert;
969         }
970         
971         
972         if (this.html.length) {
973             cfg.html = this.html;
974         }
975         if (this.fa) {
976             var fasize = '';
977             if (this.fasize > 1) {
978                 fasize = ' fa-' + this.fasize + 'x';
979             }
980             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
981             
982             
983         }
984         if (this.icon) {
985             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
986         }
987         
988         return cfg;
989     }
990    
991 });
992
993  
994
995  /*
996  * - LGPL
997  *
998  * page container.
999  * 
1000  */
1001
1002
1003 /**
1004  * @class Roo.bootstrap.Container
1005  * @extends Roo.bootstrap.Component
1006  * Bootstrap Container class
1007  * @cfg {Boolean} jumbotron is it a jumbotron element
1008  * @cfg {String} html content of element
1009  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1010  * @cfg {String} panel (primary|success|info|warning|danger) render as panel  - type - primary/success.....
1011  * @cfg {String} header content of header (for panel)
1012  * @cfg {String} footer content of footer (for panel)
1013  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1014  * @cfg {String} tag (header|aside|section) type of HTML tag.
1015  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1016  * @cfg {String} fa font awesome icon
1017  * @cfg {String} icon (info-sign|check|...) glyphicon name
1018  * @cfg {Boolean} hidden (true|false) hide the element
1019  * @cfg {Boolean} expandable (true|false) default false
1020  * @cfg {Boolean} expanded (true|false) default true
1021  * @cfg {String} rheader contet on the right of header
1022  * @cfg {Boolean} clickable (true|false) default false
1023
1024  *     
1025  * @constructor
1026  * Create a new Container
1027  * @param {Object} config The config object
1028  */
1029
1030 Roo.bootstrap.Container = function(config){
1031     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1032     
1033     this.addEvents({
1034         // raw events
1035          /**
1036          * @event expand
1037          * After the panel has been expand
1038          * 
1039          * @param {Roo.bootstrap.Container} this
1040          */
1041         "expand" : true,
1042         /**
1043          * @event collapse
1044          * After the panel has been collapsed
1045          * 
1046          * @param {Roo.bootstrap.Container} this
1047          */
1048         "collapse" : true,
1049         /**
1050          * @event click
1051          * When a element is chick
1052          * @param {Roo.bootstrap.Container} this
1053          * @param {Roo.EventObject} e
1054          */
1055         "click" : true
1056     });
1057 };
1058
1059 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1060     
1061     jumbotron : false,
1062     well: '',
1063     panel : '',
1064     header: '',
1065     footer : '',
1066     sticky: '',
1067     tag : false,
1068     alert : false,
1069     fa: false,
1070     icon : false,
1071     expandable : false,
1072     rheader : '',
1073     expanded : true,
1074     clickable: false,
1075   
1076      
1077     getChildContainer : function() {
1078         
1079         if(!this.el){
1080             return false;
1081         }
1082         
1083         if (this.panel.length) {
1084             return this.el.select('.panel-body',true).first();
1085         }
1086         
1087         return this.el;
1088     },
1089     
1090     
1091     getAutoCreate : function(){
1092         
1093         var cfg = {
1094             tag : this.tag || 'div',
1095             html : '',
1096             cls : ''
1097         };
1098         if (this.jumbotron) {
1099             cfg.cls = 'jumbotron';
1100         }
1101         
1102         
1103         
1104         // - this is applied by the parent..
1105         //if (this.cls) {
1106         //    cfg.cls = this.cls + '';
1107         //}
1108         
1109         if (this.sticky.length) {
1110             
1111             var bd = Roo.get(document.body);
1112             if (!bd.hasClass('bootstrap-sticky')) {
1113                 bd.addClass('bootstrap-sticky');
1114                 Roo.select('html',true).setStyle('height', '100%');
1115             }
1116              
1117             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1118         }
1119         
1120         
1121         if (this.well.length) {
1122             switch (this.well) {
1123                 case 'lg':
1124                 case 'sm':
1125                     cfg.cls +=' well well-' +this.well;
1126                     break;
1127                 default:
1128                     cfg.cls +=' well';
1129                     break;
1130             }
1131         }
1132         
1133         if (this.hidden) {
1134             cfg.cls += ' hidden';
1135         }
1136         
1137         
1138         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1139             cfg.cls +=' alert alert-' + this.alert;
1140         }
1141         
1142         var body = cfg;
1143         
1144         if (this.panel.length) {
1145             cfg.cls += ' panel panel-' + this.panel;
1146             cfg.cn = [];
1147             if (this.header.length) {
1148                 
1149                 var h = [];
1150                 
1151                 if(this.expandable){
1152                     
1153                     cfg.cls = cfg.cls + ' expandable';
1154                     
1155                     h.push({
1156                         tag: 'i',
1157                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1158                     });
1159                     
1160                 }
1161                 
1162                 h.push(
1163                     {
1164                         tag: 'span',
1165                         cls : 'panel-title',
1166                         html : (this.expandable ? '&nbsp;' : '') + this.header
1167                     },
1168                     {
1169                         tag: 'span',
1170                         cls: 'panel-header-right',
1171                         html: this.rheader
1172                     }
1173                 );
1174                 
1175                 cfg.cn.push({
1176                     cls : 'panel-heading',
1177                     style : this.expandable ? 'cursor: pointer' : '',
1178                     cn : h
1179                 });
1180                 
1181             }
1182             
1183             body = false;
1184             cfg.cn.push({
1185                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1186                 html : this.html
1187             });
1188             
1189             
1190             if (this.footer.length) {
1191                 cfg.cn.push({
1192                     cls : 'panel-footer',
1193                     html : this.footer
1194                     
1195                 });
1196             }
1197             
1198         }
1199         
1200         if (body) {
1201             body.html = this.html || cfg.html;
1202             // prefix with the icons..
1203             if (this.fa) {
1204                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1205             }
1206             if (this.icon) {
1207                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1208             }
1209             
1210             
1211         }
1212         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1213             cfg.cls =  'container';
1214         }
1215         
1216         return cfg;
1217     },
1218     
1219     initEvents: function() 
1220     {
1221         if(this.expandable){
1222             var headerEl = this.headerEl();
1223         
1224             if(headerEl){
1225                 headerEl.on('click', this.onToggleClick, this);
1226             }
1227         }
1228         
1229         if(this.clickable){
1230             this.el.on('click', this.onClick, this);
1231         }
1232         
1233     },
1234     
1235     onToggleClick : function()
1236     {
1237         var headerEl = this.headerEl();
1238         
1239         if(!headerEl){
1240             return;
1241         }
1242         
1243         if(this.expanded){
1244             this.collapse();
1245             return;
1246         }
1247         
1248         this.expand();
1249     },
1250     
1251     expand : function()
1252     {
1253         if(this.fireEvent('expand', this)) {
1254             
1255             this.expanded = true;
1256             
1257             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1258             
1259             this.el.select('.panel-body',true).first().removeClass('hide');
1260             
1261             var toggleEl = this.toggleEl();
1262
1263             if(!toggleEl){
1264                 return;
1265             }
1266
1267             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1268         }
1269         
1270     },
1271     
1272     collapse : function()
1273     {
1274         if(this.fireEvent('collapse', this)) {
1275             
1276             this.expanded = false;
1277             
1278             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1279             this.el.select('.panel-body',true).first().addClass('hide');
1280         
1281             var toggleEl = this.toggleEl();
1282
1283             if(!toggleEl){
1284                 return;
1285             }
1286
1287             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1288         }
1289     },
1290     
1291     toggleEl : function()
1292     {
1293         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1294             return;
1295         }
1296         
1297         return this.el.select('.panel-heading .fa',true).first();
1298     },
1299     
1300     headerEl : function()
1301     {
1302         if(!this.el || !this.panel.length || !this.header.length){
1303             return;
1304         }
1305         
1306         return this.el.select('.panel-heading',true).first()
1307     },
1308     
1309     bodyEl : function()
1310     {
1311         if(!this.el || !this.panel.length){
1312             return;
1313         }
1314         
1315         return this.el.select('.panel-body',true).first()
1316     },
1317     
1318     titleEl : function()
1319     {
1320         if(!this.el || !this.panel.length || !this.header.length){
1321             return;
1322         }
1323         
1324         return this.el.select('.panel-title',true).first();
1325     },
1326     
1327     setTitle : function(v)
1328     {
1329         var titleEl = this.titleEl();
1330         
1331         if(!titleEl){
1332             return;
1333         }
1334         
1335         titleEl.dom.innerHTML = v;
1336     },
1337     
1338     getTitle : function()
1339     {
1340         
1341         var titleEl = this.titleEl();
1342         
1343         if(!titleEl){
1344             return '';
1345         }
1346         
1347         return titleEl.dom.innerHTML;
1348     },
1349     
1350     setRightTitle : function(v)
1351     {
1352         var t = this.el.select('.panel-header-right',true).first();
1353         
1354         if(!t){
1355             return;
1356         }
1357         
1358         t.dom.innerHTML = v;
1359     },
1360     
1361     onClick : function(e)
1362     {
1363         e.preventDefault();
1364         
1365         this.fireEvent('click', this, e);
1366     }
1367    
1368 });
1369
1370  /*
1371  * - LGPL
1372  *
1373  * image
1374  * 
1375  */
1376
1377
1378 /**
1379  * @class Roo.bootstrap.Img
1380  * @extends Roo.bootstrap.Component
1381  * Bootstrap Img class
1382  * @cfg {Boolean} imgResponsive false | true
1383  * @cfg {String} border rounded | circle | thumbnail
1384  * @cfg {String} src image source
1385  * @cfg {String} alt image alternative text
1386  * @cfg {String} href a tag href
1387  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1388  * @cfg {String} xsUrl xs image source
1389  * @cfg {String} smUrl sm image source
1390  * @cfg {String} mdUrl md image source
1391  * @cfg {String} lgUrl lg image source
1392  * 
1393  * @constructor
1394  * Create a new Input
1395  * @param {Object} config The config object
1396  */
1397
1398 Roo.bootstrap.Img = function(config){
1399     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1400     
1401     this.addEvents({
1402         // img events
1403         /**
1404          * @event click
1405          * The img click event for the img.
1406          * @param {Roo.EventObject} e
1407          */
1408         "click" : true
1409     });
1410 };
1411
1412 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1413     
1414     imgResponsive: true,
1415     border: '',
1416     src: 'about:blank',
1417     href: false,
1418     target: false,
1419     xsUrl: '',
1420     smUrl: '',
1421     mdUrl: '',
1422     lgUrl: '',
1423
1424     getAutoCreate : function()
1425     {   
1426         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1427             return this.createSingleImg();
1428         }
1429         
1430         var cfg = {
1431             tag: 'div',
1432             cls: 'roo-image-responsive-group',
1433             cn: []
1434         };
1435         var _this = this;
1436         
1437         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1438             
1439             if(!_this[size + 'Url']){
1440                 return;
1441             }
1442             
1443             var img = {
1444                 tag: 'img',
1445                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1446                 html: _this.html || cfg.html,
1447                 src: _this[size + 'Url']
1448             };
1449             
1450             img.cls += ' roo-image-responsive-' + size;
1451             
1452             var s = ['xs', 'sm', 'md', 'lg'];
1453             
1454             s.splice(s.indexOf(size), 1);
1455             
1456             Roo.each(s, function(ss){
1457                 img.cls += ' hidden-' + ss;
1458             });
1459             
1460             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1461                 cfg.cls += ' img-' + _this.border;
1462             }
1463             
1464             if(_this.alt){
1465                 cfg.alt = _this.alt;
1466             }
1467             
1468             if(_this.href){
1469                 var a = {
1470                     tag: 'a',
1471                     href: _this.href,
1472                     cn: [
1473                         img
1474                     ]
1475                 };
1476
1477                 if(this.target){
1478                     a.target = _this.target;
1479                 }
1480             }
1481             
1482             cfg.cn.push((_this.href) ? a : img);
1483             
1484         });
1485         
1486         return cfg;
1487     },
1488     
1489     createSingleImg : function()
1490     {
1491         var cfg = {
1492             tag: 'img',
1493             cls: (this.imgResponsive) ? 'img-responsive' : '',
1494             html : null,
1495             src : 'about:blank'  // just incase src get's set to undefined?!?
1496         };
1497         
1498         cfg.html = this.html || cfg.html;
1499         
1500         cfg.src = this.src || cfg.src;
1501         
1502         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1503             cfg.cls += ' img-' + this.border;
1504         }
1505         
1506         if(this.alt){
1507             cfg.alt = this.alt;
1508         }
1509         
1510         if(this.href){
1511             var a = {
1512                 tag: 'a',
1513                 href: this.href,
1514                 cn: [
1515                     cfg
1516                 ]
1517             };
1518             
1519             if(this.target){
1520                 a.target = this.target;
1521             }
1522             
1523         }
1524         
1525         return (this.href) ? a : cfg;
1526     },
1527     
1528     initEvents: function() 
1529     {
1530         if(!this.href){
1531             this.el.on('click', this.onClick, this);
1532         }
1533         
1534     },
1535     
1536     onClick : function(e)
1537     {
1538         Roo.log('img onclick');
1539         this.fireEvent('click', this, e);
1540     },
1541     /**
1542      * Sets the url of the image - used to update it
1543      * @param {String} url the url of the image
1544      */
1545     
1546     setSrc : function(url)
1547     {
1548         this.src =  url;
1549         
1550         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1551             this.el.dom.src =  url;
1552             return;
1553         }
1554         
1555         this.el.select('img', true).first().dom.src =  url;
1556     }
1557     
1558     
1559    
1560 });
1561
1562  /*
1563  * - LGPL
1564  *
1565  * image
1566  * 
1567  */
1568
1569
1570 /**
1571  * @class Roo.bootstrap.Link
1572  * @extends Roo.bootstrap.Component
1573  * Bootstrap Link Class
1574  * @cfg {String} alt image alternative text
1575  * @cfg {String} href a tag href
1576  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1577  * @cfg {String} html the content of the link.
1578  * @cfg {String} anchor name for the anchor link
1579  * @cfg {String} fa - favicon
1580
1581  * @cfg {Boolean} preventDefault (true | false) default false
1582
1583  * 
1584  * @constructor
1585  * Create a new Input
1586  * @param {Object} config The config object
1587  */
1588
1589 Roo.bootstrap.Link = function(config){
1590     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1591     
1592     this.addEvents({
1593         // img events
1594         /**
1595          * @event click
1596          * The img click event for the img.
1597          * @param {Roo.EventObject} e
1598          */
1599         "click" : true
1600     });
1601 };
1602
1603 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1604     
1605     href: false,
1606     target: false,
1607     preventDefault: false,
1608     anchor : false,
1609     alt : false,
1610     fa: false,
1611
1612
1613     getAutoCreate : function()
1614     {
1615         var html = this.html || '';
1616         
1617         if (this.fa !== false) {
1618             html = '<i class="fa fa-' + this.fa + '"></i>';
1619         }
1620         var cfg = {
1621             tag: 'a'
1622         };
1623         // anchor's do not require html/href...
1624         if (this.anchor === false) {
1625             cfg.html = html;
1626             cfg.href = this.href || '#';
1627         } else {
1628             cfg.name = this.anchor;
1629             if (this.html !== false || this.fa !== false) {
1630                 cfg.html = html;
1631             }
1632             if (this.href !== false) {
1633                 cfg.href = this.href;
1634             }
1635         }
1636         
1637         if(this.alt !== false){
1638             cfg.alt = this.alt;
1639         }
1640         
1641         
1642         if(this.target !== false) {
1643             cfg.target = this.target;
1644         }
1645         
1646         return cfg;
1647     },
1648     
1649     initEvents: function() {
1650         
1651         if(!this.href || this.preventDefault){
1652             this.el.on('click', this.onClick, this);
1653         }
1654     },
1655     
1656     onClick : function(e)
1657     {
1658         if(this.preventDefault){
1659             e.preventDefault();
1660         }
1661         //Roo.log('img onclick');
1662         this.fireEvent('click', this, e);
1663     }
1664    
1665 });
1666
1667  /*
1668  * - LGPL
1669  *
1670  * header
1671  * 
1672  */
1673
1674 /**
1675  * @class Roo.bootstrap.Header
1676  * @extends Roo.bootstrap.Component
1677  * Bootstrap Header class
1678  * @cfg {String} html content of header
1679  * @cfg {Number} level (1|2|3|4|5|6) default 1
1680  * 
1681  * @constructor
1682  * Create a new Header
1683  * @param {Object} config The config object
1684  */
1685
1686
1687 Roo.bootstrap.Header  = function(config){
1688     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1689 };
1690
1691 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1692     
1693     //href : false,
1694     html : false,
1695     level : 1,
1696     
1697     
1698     
1699     getAutoCreate : function(){
1700         
1701         
1702         
1703         var cfg = {
1704             tag: 'h' + (1 *this.level),
1705             html: this.html || ''
1706         } ;
1707         
1708         return cfg;
1709     }
1710    
1711 });
1712
1713  
1714
1715  /*
1716  * Based on:
1717  * Ext JS Library 1.1.1
1718  * Copyright(c) 2006-2007, Ext JS, LLC.
1719  *
1720  * Originally Released Under LGPL - original licence link has changed is not relivant.
1721  *
1722  * Fork - LGPL
1723  * <script type="text/javascript">
1724  */
1725  
1726 /**
1727  * @class Roo.bootstrap.MenuMgr
1728  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1729  * @singleton
1730  */
1731 Roo.bootstrap.MenuMgr = function(){
1732    var menus, active, groups = {}, attached = false, lastShow = new Date();
1733
1734    // private - called when first menu is created
1735    function init(){
1736        menus = {};
1737        active = new Roo.util.MixedCollection();
1738        Roo.get(document).addKeyListener(27, function(){
1739            if(active.length > 0){
1740                hideAll();
1741            }
1742        });
1743    }
1744
1745    // private
1746    function hideAll(){
1747        if(active && active.length > 0){
1748            var c = active.clone();
1749            c.each(function(m){
1750                m.hide();
1751            });
1752        }
1753    }
1754
1755    // private
1756    function onHide(m){
1757        active.remove(m);
1758        if(active.length < 1){
1759            Roo.get(document).un("mouseup", onMouseDown);
1760             
1761            attached = false;
1762        }
1763    }
1764
1765    // private
1766    function onShow(m){
1767        var last = active.last();
1768        lastShow = new Date();
1769        active.add(m);
1770        if(!attached){
1771           Roo.get(document).on("mouseup", onMouseDown);
1772            
1773            attached = true;
1774        }
1775        if(m.parentMenu){
1776           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1777           m.parentMenu.activeChild = m;
1778        }else if(last && last.isVisible()){
1779           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1780        }
1781    }
1782
1783    // private
1784    function onBeforeHide(m){
1785        if(m.activeChild){
1786            m.activeChild.hide();
1787        }
1788        if(m.autoHideTimer){
1789            clearTimeout(m.autoHideTimer);
1790            delete m.autoHideTimer;
1791        }
1792    }
1793
1794    // private
1795    function onBeforeShow(m){
1796        var pm = m.parentMenu;
1797        if(!pm && !m.allowOtherMenus){
1798            hideAll();
1799        }else if(pm && pm.activeChild && active != m){
1800            pm.activeChild.hide();
1801        }
1802    }
1803
1804    // private this should really trigger on mouseup..
1805    function onMouseDown(e){
1806         Roo.log("on Mouse Up");
1807         
1808         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1809             Roo.log("MenuManager hideAll");
1810             hideAll();
1811             e.stopEvent();
1812         }
1813         
1814         
1815    }
1816
1817    // private
1818    function onBeforeCheck(mi, state){
1819        if(state){
1820            var g = groups[mi.group];
1821            for(var i = 0, l = g.length; i < l; i++){
1822                if(g[i] != mi){
1823                    g[i].setChecked(false);
1824                }
1825            }
1826        }
1827    }
1828
1829    return {
1830
1831        /**
1832         * Hides all menus that are currently visible
1833         */
1834        hideAll : function(){
1835             hideAll();  
1836        },
1837
1838        // private
1839        register : function(menu){
1840            if(!menus){
1841                init();
1842            }
1843            menus[menu.id] = menu;
1844            menu.on("beforehide", onBeforeHide);
1845            menu.on("hide", onHide);
1846            menu.on("beforeshow", onBeforeShow);
1847            menu.on("show", onShow);
1848            var g = menu.group;
1849            if(g && menu.events["checkchange"]){
1850                if(!groups[g]){
1851                    groups[g] = [];
1852                }
1853                groups[g].push(menu);
1854                menu.on("checkchange", onCheck);
1855            }
1856        },
1857
1858         /**
1859          * Returns a {@link Roo.menu.Menu} object
1860          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1861          * be used to generate and return a new Menu instance.
1862          */
1863        get : function(menu){
1864            if(typeof menu == "string"){ // menu id
1865                return menus[menu];
1866            }else if(menu.events){  // menu instance
1867                return menu;
1868            }
1869            /*else if(typeof menu.length == 'number'){ // array of menu items?
1870                return new Roo.bootstrap.Menu({items:menu});
1871            }else{ // otherwise, must be a config
1872                return new Roo.bootstrap.Menu(menu);
1873            }
1874            */
1875            return false;
1876        },
1877
1878        // private
1879        unregister : function(menu){
1880            delete menus[menu.id];
1881            menu.un("beforehide", onBeforeHide);
1882            menu.un("hide", onHide);
1883            menu.un("beforeshow", onBeforeShow);
1884            menu.un("show", onShow);
1885            var g = menu.group;
1886            if(g && menu.events["checkchange"]){
1887                groups[g].remove(menu);
1888                menu.un("checkchange", onCheck);
1889            }
1890        },
1891
1892        // private
1893        registerCheckable : function(menuItem){
1894            var g = menuItem.group;
1895            if(g){
1896                if(!groups[g]){
1897                    groups[g] = [];
1898                }
1899                groups[g].push(menuItem);
1900                menuItem.on("beforecheckchange", onBeforeCheck);
1901            }
1902        },
1903
1904        // private
1905        unregisterCheckable : function(menuItem){
1906            var g = menuItem.group;
1907            if(g){
1908                groups[g].remove(menuItem);
1909                menuItem.un("beforecheckchange", onBeforeCheck);
1910            }
1911        }
1912    };
1913 }();/*
1914  * - LGPL
1915  *
1916  * menu
1917  * 
1918  */
1919
1920 /**
1921  * @class Roo.bootstrap.Menu
1922  * @extends Roo.bootstrap.Component
1923  * Bootstrap Menu class - container for MenuItems
1924  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1925  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1926  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
1927  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
1928  * 
1929  * @constructor
1930  * Create a new Menu
1931  * @param {Object} config The config object
1932  */
1933
1934
1935 Roo.bootstrap.Menu = function(config){
1936     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1937     if (this.registerMenu && this.type != 'treeview')  {
1938         Roo.bootstrap.MenuMgr.register(this);
1939     }
1940     this.addEvents({
1941         /**
1942          * @event beforeshow
1943          * Fires before this menu is displayed
1944          * @param {Roo.menu.Menu} this
1945          */
1946         beforeshow : true,
1947         /**
1948          * @event beforehide
1949          * Fires before this menu is hidden
1950          * @param {Roo.menu.Menu} this
1951          */
1952         beforehide : true,
1953         /**
1954          * @event show
1955          * Fires after this menu is displayed
1956          * @param {Roo.menu.Menu} this
1957          */
1958         show : true,
1959         /**
1960          * @event hide
1961          * Fires after this menu is hidden
1962          * @param {Roo.menu.Menu} this
1963          */
1964         hide : true,
1965         /**
1966          * @event click
1967          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1968          * @param {Roo.menu.Menu} this
1969          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1970          * @param {Roo.EventObject} e
1971          */
1972         click : true,
1973         /**
1974          * @event mouseover
1975          * Fires when the mouse is hovering over this menu
1976          * @param {Roo.menu.Menu} this
1977          * @param {Roo.EventObject} e
1978          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1979          */
1980         mouseover : true,
1981         /**
1982          * @event mouseout
1983          * Fires when the mouse exits this menu
1984          * @param {Roo.menu.Menu} this
1985          * @param {Roo.EventObject} e
1986          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1987          */
1988         mouseout : true,
1989         /**
1990          * @event itemclick
1991          * Fires when a menu item contained in this menu is clicked
1992          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
1993          * @param {Roo.EventObject} e
1994          */
1995         itemclick: true
1996     });
1997     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
1998 };
1999
2000 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2001     
2002    /// html : false,
2003     //align : '',
2004     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2005     type: false,
2006     /**
2007      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2008      */
2009     registerMenu : true,
2010     
2011     menuItems :false, // stores the menu items..
2012     
2013     hidden:true,
2014         
2015     parentMenu : false,
2016     
2017     stopEvent : true,
2018     
2019     isLink : false,
2020     
2021     getChildContainer : function() {
2022         return this.el;  
2023     },
2024     
2025     getAutoCreate : function(){
2026          
2027         //if (['right'].indexOf(this.align)!==-1) {
2028         //    cfg.cn[1].cls += ' pull-right'
2029         //}
2030         
2031         
2032         var cfg = {
2033             tag : 'ul',
2034             cls : 'dropdown-menu' ,
2035             style : 'z-index:1000'
2036             
2037         };
2038         
2039         if (this.type === 'submenu') {
2040             cfg.cls = 'submenu active';
2041         }
2042         if (this.type === 'treeview') {
2043             cfg.cls = 'treeview-menu';
2044         }
2045         
2046         return cfg;
2047     },
2048     initEvents : function() {
2049         
2050        // Roo.log("ADD event");
2051        // Roo.log(this.triggerEl.dom);
2052         
2053         this.triggerEl.on('click', this.onTriggerClick, this);
2054         
2055         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2056         
2057         this.triggerEl.addClass('dropdown-toggle');
2058         
2059         if (Roo.isTouch) {
2060             this.el.on('touchstart'  , this.onTouch, this);
2061         }
2062         this.el.on('click' , this.onClick, this);
2063
2064         this.el.on("mouseover", this.onMouseOver, this);
2065         this.el.on("mouseout", this.onMouseOut, this);
2066         
2067     },
2068     
2069     findTargetItem : function(e)
2070     {
2071         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2072         if(!t){
2073             return false;
2074         }
2075         //Roo.log(t);         Roo.log(t.id);
2076         if(t && t.id){
2077             //Roo.log(this.menuitems);
2078             return this.menuitems.get(t.id);
2079             
2080             //return this.items.get(t.menuItemId);
2081         }
2082         
2083         return false;
2084     },
2085     
2086     onTouch : function(e) 
2087     {
2088         Roo.log("menu.onTouch");
2089         //e.stopEvent(); this make the user popdown broken
2090         this.onClick(e);
2091     },
2092     
2093     onClick : function(e)
2094     {
2095         Roo.log("menu.onClick");
2096         
2097         var t = this.findTargetItem(e);
2098         if(!t || t.isContainer){
2099             return;
2100         }
2101         Roo.log(e);
2102         /*
2103         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2104             if(t == this.activeItem && t.shouldDeactivate(e)){
2105                 this.activeItem.deactivate();
2106                 delete this.activeItem;
2107                 return;
2108             }
2109             if(t.canActivate){
2110                 this.setActiveItem(t, true);
2111             }
2112             return;
2113             
2114             
2115         }
2116         */
2117        
2118         Roo.log('pass click event');
2119         
2120         t.onClick(e);
2121         
2122         this.fireEvent("click", this, t, e);
2123         
2124         var _this = this;
2125         
2126         if(!t.href.length || t.href == '#'){
2127             (function() { _this.hide(); }).defer(100);
2128         }
2129         
2130     },
2131     
2132     onMouseOver : function(e){
2133         var t  = this.findTargetItem(e);
2134         //Roo.log(t);
2135         //if(t){
2136         //    if(t.canActivate && !t.disabled){
2137         //        this.setActiveItem(t, true);
2138         //    }
2139         //}
2140         
2141         this.fireEvent("mouseover", this, e, t);
2142     },
2143     isVisible : function(){
2144         return !this.hidden;
2145     },
2146      onMouseOut : function(e){
2147         var t  = this.findTargetItem(e);
2148         
2149         //if(t ){
2150         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2151         //        this.activeItem.deactivate();
2152         //        delete this.activeItem;
2153         //    }
2154         //}
2155         this.fireEvent("mouseout", this, e, t);
2156     },
2157     
2158     
2159     /**
2160      * Displays this menu relative to another element
2161      * @param {String/HTMLElement/Roo.Element} element The element to align to
2162      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2163      * the element (defaults to this.defaultAlign)
2164      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2165      */
2166     show : function(el, pos, parentMenu){
2167         this.parentMenu = parentMenu;
2168         if(!this.el){
2169             this.render();
2170         }
2171         this.fireEvent("beforeshow", this);
2172         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2173     },
2174      /**
2175      * Displays this menu at a specific xy position
2176      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2177      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2178      */
2179     showAt : function(xy, parentMenu, /* private: */_e){
2180         this.parentMenu = parentMenu;
2181         if(!this.el){
2182             this.render();
2183         }
2184         if(_e !== false){
2185             this.fireEvent("beforeshow", this);
2186             //xy = this.el.adjustForConstraints(xy);
2187         }
2188         
2189         //this.el.show();
2190         this.hideMenuItems();
2191         this.hidden = false;
2192         this.triggerEl.addClass('open');
2193         
2194         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2195             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2196         }
2197         
2198         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2199             this.el.setXY(xy);
2200         }
2201         
2202         this.focus();
2203         this.fireEvent("show", this);
2204     },
2205     
2206     focus : function(){
2207         return;
2208         if(!this.hidden){
2209             this.doFocus.defer(50, this);
2210         }
2211     },
2212
2213     doFocus : function(){
2214         if(!this.hidden){
2215             this.focusEl.focus();
2216         }
2217     },
2218
2219     /**
2220      * Hides this menu and optionally all parent menus
2221      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2222      */
2223     hide : function(deep)
2224     {
2225         
2226         this.hideMenuItems();
2227         if(this.el && this.isVisible()){
2228             this.fireEvent("beforehide", this);
2229             if(this.activeItem){
2230                 this.activeItem.deactivate();
2231                 this.activeItem = null;
2232             }
2233             this.triggerEl.removeClass('open');;
2234             this.hidden = true;
2235             this.fireEvent("hide", this);
2236         }
2237         if(deep === true && this.parentMenu){
2238             this.parentMenu.hide(true);
2239         }
2240     },
2241     
2242     onTriggerClick : function(e)
2243     {
2244         Roo.log('trigger click');
2245         
2246         var target = e.getTarget();
2247         
2248         Roo.log(target.nodeName.toLowerCase());
2249         
2250         if(target.nodeName.toLowerCase() === 'i'){
2251             e.preventDefault();
2252         }
2253         
2254     },
2255     
2256     onTriggerPress  : function(e)
2257     {
2258         Roo.log('trigger press');
2259         //Roo.log(e.getTarget());
2260        // Roo.log(this.triggerEl.dom);
2261        
2262         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2263         var pel = Roo.get(e.getTarget());
2264         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2265             Roo.log('is treeview or dropdown?');
2266             return;
2267         }
2268         
2269         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2270             return;
2271         }
2272         
2273         if (this.isVisible()) {
2274             Roo.log('hide');
2275             this.hide();
2276         } else {
2277             Roo.log('show');
2278             this.show(this.triggerEl, false, false);
2279         }
2280         
2281         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2282             e.stopEvent();
2283         }
2284         
2285     },
2286        
2287     
2288     hideMenuItems : function()
2289     {
2290         Roo.log("hide Menu Items");
2291         if (!this.el) { 
2292             return;
2293         }
2294         //$(backdrop).remove()
2295         this.el.select('.open',true).each(function(aa) {
2296             
2297             aa.removeClass('open');
2298           //var parent = getParent($(this))
2299           //var relatedTarget = { relatedTarget: this }
2300           
2301            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2302           //if (e.isDefaultPrevented()) return
2303            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2304         });
2305     },
2306     addxtypeChild : function (tree, cntr) {
2307         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2308           
2309         this.menuitems.add(comp);
2310         return comp;
2311
2312     },
2313     getEl : function()
2314     {
2315         Roo.log(this.el);
2316         return this.el;
2317     }
2318 });
2319
2320  
2321  /*
2322  * - LGPL
2323  *
2324  * menu item
2325  * 
2326  */
2327
2328
2329 /**
2330  * @class Roo.bootstrap.MenuItem
2331  * @extends Roo.bootstrap.Component
2332  * Bootstrap MenuItem class
2333  * @cfg {String} html the menu label
2334  * @cfg {String} href the link
2335  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2336  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2337  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2338  * @cfg {String} fa favicon to show on left of menu item.
2339  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2340  * 
2341  * 
2342  * @constructor
2343  * Create a new MenuItem
2344  * @param {Object} config The config object
2345  */
2346
2347
2348 Roo.bootstrap.MenuItem = function(config){
2349     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2350     this.addEvents({
2351         // raw events
2352         /**
2353          * @event click
2354          * The raw click event for the entire grid.
2355          * @param {Roo.bootstrap.MenuItem} this
2356          * @param {Roo.EventObject} e
2357          */
2358         "click" : true
2359     });
2360 };
2361
2362 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2363     
2364     href : false,
2365     html : false,
2366     preventDefault: false,
2367     isContainer : false,
2368     active : false,
2369     fa: false,
2370     
2371     getAutoCreate : function(){
2372         
2373         if(this.isContainer){
2374             return {
2375                 tag: 'li',
2376                 cls: 'dropdown-menu-item'
2377             };
2378         }
2379         var ctag = {
2380             tag: 'span',
2381             html: 'Link'
2382         };
2383         
2384         var anc = {
2385             tag : 'a',
2386             href : '#',
2387             cn : [  ]
2388         };
2389         
2390         if (this.fa !== false) {
2391             anc.cn.push({
2392                 tag : 'i',
2393                 cls : 'fa fa-' + this.fa
2394             });
2395         }
2396         
2397         anc.cn.push(ctag);
2398         
2399         
2400         var cfg= {
2401             tag: 'li',
2402             cls: 'dropdown-menu-item',
2403             cn: [ anc ]
2404         };
2405         if (this.parent().type == 'treeview') {
2406             cfg.cls = 'treeview-menu';
2407         }
2408         if (this.active) {
2409             cfg.cls += ' active';
2410         }
2411         
2412         
2413         
2414         anc.href = this.href || cfg.cn[0].href ;
2415         ctag.html = this.html || cfg.cn[0].html ;
2416         return cfg;
2417     },
2418     
2419     initEvents: function()
2420     {
2421         if (this.parent().type == 'treeview') {
2422             this.el.select('a').on('click', this.onClick, this);
2423         }
2424         
2425         if (this.menu) {
2426             this.menu.parentType = this.xtype;
2427             this.menu.triggerEl = this.el;
2428             this.menu = this.addxtype(Roo.apply({}, this.menu));
2429         }
2430         
2431     },
2432     onClick : function(e)
2433     {
2434         Roo.log('item on click ');
2435         
2436         if(this.preventDefault){
2437             e.preventDefault();
2438         }
2439         //this.parent().hideMenuItems();
2440         
2441         this.fireEvent('click', this, e);
2442     },
2443     getEl : function()
2444     {
2445         return this.el;
2446     } 
2447 });
2448
2449  
2450
2451  /*
2452  * - LGPL
2453  *
2454  * menu separator
2455  * 
2456  */
2457
2458
2459 /**
2460  * @class Roo.bootstrap.MenuSeparator
2461  * @extends Roo.bootstrap.Component
2462  * Bootstrap MenuSeparator class
2463  * 
2464  * @constructor
2465  * Create a new MenuItem
2466  * @param {Object} config The config object
2467  */
2468
2469
2470 Roo.bootstrap.MenuSeparator = function(config){
2471     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2472 };
2473
2474 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2475     
2476     getAutoCreate : function(){
2477         var cfg = {
2478             cls: 'divider',
2479             tag : 'li'
2480         };
2481         
2482         return cfg;
2483     }
2484    
2485 });
2486
2487  
2488
2489  
2490 /*
2491 * Licence: LGPL
2492 */
2493
2494 /**
2495  * @class Roo.bootstrap.Modal
2496  * @extends Roo.bootstrap.Component
2497  * Bootstrap Modal class
2498  * @cfg {String} title Title of dialog
2499  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2500  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2501  * @cfg {Boolean} specificTitle default false
2502  * @cfg {Array} buttons Array of buttons or standard button set..
2503  * @cfg {String} buttonPosition (left|right|center) default right
2504  * @cfg {Boolean} animate default true
2505  * @cfg {Boolean} allow_close default true
2506  * @cfg {Boolean} fitwindow default false
2507  * @cfg {String} size (sm|lg) default empty
2508  *
2509  *
2510  * @constructor
2511  * Create a new Modal Dialog
2512  * @param {Object} config The config object
2513  */
2514
2515 Roo.bootstrap.Modal = function(config){
2516     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2517     this.addEvents({
2518         // raw events
2519         /**
2520          * @event btnclick
2521          * The raw btnclick event for the button
2522          * @param {Roo.EventObject} e
2523          */
2524         "btnclick" : true,
2525         /**
2526          * @event resize
2527          * Fire when dialog resize
2528          * @param {Roo.bootstrap.Modal} this
2529          * @param {Roo.EventObject} e
2530          */
2531         "resize" : true
2532     });
2533     this.buttons = this.buttons || [];
2534
2535     if (this.tmpl) {
2536         this.tmpl = Roo.factory(this.tmpl);
2537     }
2538
2539 };
2540
2541 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2542
2543     title : 'test dialog',
2544
2545     buttons : false,
2546
2547     // set on load...
2548
2549     html: false,
2550
2551     tmp: false,
2552
2553     specificTitle: false,
2554
2555     buttonPosition: 'right',
2556
2557     allow_close : true,
2558
2559     animate : true,
2560
2561     fitwindow: false,
2562
2563
2564      // private
2565     dialogEl: false,
2566     bodyEl:  false,
2567     footerEl:  false,
2568     titleEl:  false,
2569     closeEl:  false,
2570
2571     size: '',
2572
2573
2574     onRender : function(ct, position)
2575     {
2576         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2577
2578         if(!this.el){
2579             var cfg = Roo.apply({},  this.getAutoCreate());
2580             cfg.id = Roo.id();
2581             //if(!cfg.name){
2582             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2583             //}
2584             //if (!cfg.name.length) {
2585             //    delete cfg.name;
2586            // }
2587             if (this.cls) {
2588                 cfg.cls += ' ' + this.cls;
2589             }
2590             if (this.style) {
2591                 cfg.style = this.style;
2592             }
2593             this.el = Roo.get(document.body).createChild(cfg, position);
2594         }
2595         //var type = this.el.dom.type;
2596
2597
2598         if(this.tabIndex !== undefined){
2599             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2600         }
2601
2602         this.dialogEl = this.el.select('.modal-dialog',true).first();
2603         this.bodyEl = this.el.select('.modal-body',true).first();
2604         this.closeEl = this.el.select('.modal-header .close', true).first();
2605         this.headerEl = this.el.select('.modal-header',true).first();
2606         this.titleEl = this.el.select('.modal-title',true).first();
2607         this.footerEl = this.el.select('.modal-footer',true).first();
2608
2609         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2610         this.maskEl.enableDisplayMode("block");
2611         this.maskEl.hide();
2612         //this.el.addClass("x-dlg-modal");
2613
2614         if (this.buttons.length) {
2615             Roo.each(this.buttons, function(bb) {
2616                 var b = Roo.apply({}, bb);
2617                 b.xns = b.xns || Roo.bootstrap;
2618                 b.xtype = b.xtype || 'Button';
2619                 if (typeof(b.listeners) == 'undefined') {
2620                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2621                 }
2622
2623                 var btn = Roo.factory(b);
2624
2625                 btn.render(this.el.select('.modal-footer div').first());
2626
2627             },this);
2628         }
2629         // render the children.
2630         var nitems = [];
2631
2632         if(typeof(this.items) != 'undefined'){
2633             var items = this.items;
2634             delete this.items;
2635
2636             for(var i =0;i < items.length;i++) {
2637                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2638             }
2639         }
2640
2641         this.items = nitems;
2642
2643         // where are these used - they used to be body/close/footer
2644
2645
2646         this.initEvents();
2647         //this.el.addClass([this.fieldClass, this.cls]);
2648
2649     },
2650
2651     getAutoCreate : function(){
2652
2653
2654         var bdy = {
2655                 cls : 'modal-body',
2656                 html : this.html || ''
2657         };
2658
2659         var title = {
2660             tag: 'h4',
2661             cls : 'modal-title',
2662             html : this.title
2663         };
2664
2665         if(this.specificTitle){
2666             title = this.title;
2667
2668         };
2669
2670         var header = [];
2671         if (this.allow_close) {
2672             header.push({
2673                 tag: 'button',
2674                 cls : 'close',
2675                 html : '&times'
2676             });
2677         }
2678
2679         header.push(title);
2680
2681         var size = '';
2682
2683         if(this.size.length){
2684             size = 'modal-' + this.size;
2685         }
2686
2687         var modal = {
2688             cls: "modal",
2689             style : 'display: none',
2690             cn : [
2691                 {
2692                     cls: "modal-dialog " + size,
2693                     cn : [
2694                         {
2695                             cls : "modal-content",
2696                             cn : [
2697                                 {
2698                                     cls : 'modal-header',
2699                                     cn : header
2700                                 },
2701                                 bdy,
2702                                 {
2703                                     cls : 'modal-footer',
2704                                     cn : [
2705                                         {
2706                                             tag: 'div',
2707                                             cls: 'btn-' + this.buttonPosition
2708                                         }
2709                                     ]
2710
2711                                 }
2712
2713
2714                             ]
2715
2716                         }
2717                     ]
2718
2719                 }
2720             ]
2721         };
2722
2723         if(this.animate){
2724             modal.cls += ' fade';
2725         }
2726
2727         return modal;
2728
2729     },
2730     getChildContainer : function() {
2731
2732          return this.bodyEl;
2733
2734     },
2735     getButtonContainer : function() {
2736          return this.el.select('.modal-footer div',true).first();
2737
2738     },
2739     initEvents : function()
2740     {
2741         if (this.allow_close) {
2742             this.closeEl.on('click', this.hide, this);
2743         }
2744         Roo.EventManager.onWindowResize(this.resize, this, true);
2745
2746
2747     },
2748
2749     resize : function()
2750     {
2751         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),  Roo.lib.Dom.getViewHeight(true));
2752         if (this.fitwindow) {
2753             var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2754             var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2755             this.setSize(w,h);
2756         }
2757     },
2758
2759     setSize : function(w,h)
2760     {
2761         if (!w && !h) {
2762             return;
2763         }
2764         this.resizeTo(w,h);
2765     },
2766
2767     show : function() {
2768
2769         if (!this.rendered) {
2770             this.render();
2771         }
2772
2773         this.el.setStyle('display', 'block');
2774
2775         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2776             var _this = this;
2777             (function(){
2778                 this.el.addClass('in');
2779             }).defer(50, this);
2780         }else{
2781             this.el.addClass('in');
2782
2783         }
2784
2785         // not sure how we can show data in here..
2786         //if (this.tmpl) {
2787         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2788         //}
2789
2790         Roo.get(document.body).addClass("x-body-masked");
2791         
2792         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2793         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2794         this.maskEl.show();
2795         
2796         this.resize();
2797         
2798         this.fireEvent('show', this);
2799
2800         // set zindex here - otherwise it appears to be ignored...
2801         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2802
2803         (function () {
2804             this.items.forEach( function(e) {
2805                 e.layout ? e.layout() : false;
2806
2807             });
2808         }).defer(100,this);
2809
2810     },
2811     hide : function()
2812     {
2813         if(this.fireEvent("beforehide", this) !== false){
2814             this.maskEl.hide();
2815             Roo.get(document.body).removeClass("x-body-masked");
2816             this.el.removeClass('in');
2817             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2818
2819             if(this.animate){ // why
2820                 var _this = this;
2821                 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2822             }else{
2823                 this.el.setStyle('display', 'none');
2824             }
2825             this.fireEvent('hide', this);
2826         }
2827     },
2828
2829     addButton : function(str, cb)
2830     {
2831
2832
2833         var b = Roo.apply({}, { html : str } );
2834         b.xns = b.xns || Roo.bootstrap;
2835         b.xtype = b.xtype || 'Button';
2836         if (typeof(b.listeners) == 'undefined') {
2837             b.listeners = { click : cb.createDelegate(this)  };
2838         }
2839
2840         var btn = Roo.factory(b);
2841
2842         btn.render(this.el.select('.modal-footer div').first());
2843
2844         return btn;
2845
2846     },
2847
2848     setDefaultButton : function(btn)
2849     {
2850         //this.el.select('.modal-footer').()
2851     },
2852     diff : false,
2853
2854     resizeTo: function(w,h)
2855     {
2856         // skip.. ?? why??
2857
2858         this.dialogEl.setWidth(w);
2859         if (this.diff === false) {
2860             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2861         }
2862
2863         this.bodyEl.setHeight(h-this.diff);
2864
2865         this.fireEvent('resize', this);
2866
2867     },
2868     setContentSize  : function(w, h)
2869     {
2870
2871     },
2872     onButtonClick: function(btn,e)
2873     {
2874         //Roo.log([a,b,c]);
2875         this.fireEvent('btnclick', btn.name, e);
2876     },
2877      /**
2878      * Set the title of the Dialog
2879      * @param {String} str new Title
2880      */
2881     setTitle: function(str) {
2882         this.titleEl.dom.innerHTML = str;
2883     },
2884     /**
2885      * Set the body of the Dialog
2886      * @param {String} str new Title
2887      */
2888     setBody: function(str) {
2889         this.bodyEl.dom.innerHTML = str;
2890     },
2891     /**
2892      * Set the body of the Dialog using the template
2893      * @param {Obj} data - apply this data to the template and replace the body contents.
2894      */
2895     applyBody: function(obj)
2896     {
2897         if (!this.tmpl) {
2898             Roo.log("Error - using apply Body without a template");
2899             //code
2900         }
2901         this.tmpl.overwrite(this.bodyEl, obj);
2902     }
2903
2904 });
2905
2906
2907 Roo.apply(Roo.bootstrap.Modal,  {
2908     /**
2909          * Button config that displays a single OK button
2910          * @type Object
2911          */
2912         OK :  [{
2913             name : 'ok',
2914             weight : 'primary',
2915             html : 'OK'
2916         }],
2917         /**
2918          * Button config that displays Yes and No buttons
2919          * @type Object
2920          */
2921         YESNO : [
2922             {
2923                 name  : 'no',
2924                 html : 'No'
2925             },
2926             {
2927                 name  :'yes',
2928                 weight : 'primary',
2929                 html : 'Yes'
2930             }
2931         ],
2932
2933         /**
2934          * Button config that displays OK and Cancel buttons
2935          * @type Object
2936          */
2937         OKCANCEL : [
2938             {
2939                name : 'cancel',
2940                 html : 'Cancel'
2941             },
2942             {
2943                 name : 'ok',
2944                 weight : 'primary',
2945                 html : 'OK'
2946             }
2947         ],
2948         /**
2949          * Button config that displays Yes, No and Cancel buttons
2950          * @type Object
2951          */
2952         YESNOCANCEL : [
2953             {
2954                 name : 'yes',
2955                 weight : 'primary',
2956                 html : 'Yes'
2957             },
2958             {
2959                 name : 'no',
2960                 html : 'No'
2961             },
2962             {
2963                 name : 'cancel',
2964                 html : 'Cancel'
2965             }
2966         ],
2967         
2968         zIndex : 10001
2969 });
2970 /*
2971  * - LGPL
2972  *
2973  * messagebox - can be used as a replace
2974  * 
2975  */
2976 /**
2977  * @class Roo.MessageBox
2978  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2979  * Example usage:
2980  *<pre><code>
2981 // Basic alert:
2982 Roo.Msg.alert('Status', 'Changes saved successfully.');
2983
2984 // Prompt for user data:
2985 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2986     if (btn == 'ok'){
2987         // process text value...
2988     }
2989 });
2990
2991 // Show a dialog using config options:
2992 Roo.Msg.show({
2993    title:'Save Changes?',
2994    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
2995    buttons: Roo.Msg.YESNOCANCEL,
2996    fn: processResult,
2997    animEl: 'elId'
2998 });
2999 </code></pre>
3000  * @singleton
3001  */
3002 Roo.bootstrap.MessageBox = function(){
3003     var dlg, opt, mask, waitTimer;
3004     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3005     var buttons, activeTextEl, bwidth;
3006
3007     
3008     // private
3009     var handleButton = function(button){
3010         dlg.hide();
3011         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3012     };
3013
3014     // private
3015     var handleHide = function(){
3016         if(opt && opt.cls){
3017             dlg.el.removeClass(opt.cls);
3018         }
3019         //if(waitTimer){
3020         //    Roo.TaskMgr.stop(waitTimer);
3021         //    waitTimer = null;
3022         //}
3023     };
3024
3025     // private
3026     var updateButtons = function(b){
3027         var width = 0;
3028         if(!b){
3029             buttons["ok"].hide();
3030             buttons["cancel"].hide();
3031             buttons["yes"].hide();
3032             buttons["no"].hide();
3033             //dlg.footer.dom.style.display = 'none';
3034             return width;
3035         }
3036         dlg.footerEl.dom.style.display = '';
3037         for(var k in buttons){
3038             if(typeof buttons[k] != "function"){
3039                 if(b[k]){
3040                     buttons[k].show();
3041                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3042                     width += buttons[k].el.getWidth()+15;
3043                 }else{
3044                     buttons[k].hide();
3045                 }
3046             }
3047         }
3048         return width;
3049     };
3050
3051     // private
3052     var handleEsc = function(d, k, e){
3053         if(opt && opt.closable !== false){
3054             dlg.hide();
3055         }
3056         if(e){
3057             e.stopEvent();
3058         }
3059     };
3060
3061     return {
3062         /**
3063          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3064          * @return {Roo.BasicDialog} The BasicDialog element
3065          */
3066         getDialog : function(){
3067            if(!dlg){
3068                 dlg = new Roo.bootstrap.Modal( {
3069                     //draggable: true,
3070                     //resizable:false,
3071                     //constraintoviewport:false,
3072                     //fixedcenter:true,
3073                     //collapsible : false,
3074                     //shim:true,
3075                     //modal: true,
3076                 //    width: 'auto',
3077                   //  height:100,
3078                     //buttonAlign:"center",
3079                     closeClick : function(){
3080                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3081                             handleButton("no");
3082                         }else{
3083                             handleButton("cancel");
3084                         }
3085                     }
3086                 });
3087                 dlg.render();
3088                 dlg.on("hide", handleHide);
3089                 mask = dlg.mask;
3090                 //dlg.addKeyListener(27, handleEsc);
3091                 buttons = {};
3092                 this.buttons = buttons;
3093                 var bt = this.buttonText;
3094                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3095                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3096                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3097                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3098                 //Roo.log(buttons);
3099                 bodyEl = dlg.bodyEl.createChild({
3100
3101                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3102                         '<textarea class="roo-mb-textarea"></textarea>' +
3103                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3104                 });
3105                 msgEl = bodyEl.dom.firstChild;
3106                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3107                 textboxEl.enableDisplayMode();
3108                 textboxEl.addKeyListener([10,13], function(){
3109                     if(dlg.isVisible() && opt && opt.buttons){
3110                         if(opt.buttons.ok){
3111                             handleButton("ok");
3112                         }else if(opt.buttons.yes){
3113                             handleButton("yes");
3114                         }
3115                     }
3116                 });
3117                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3118                 textareaEl.enableDisplayMode();
3119                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3120                 progressEl.enableDisplayMode();
3121                 
3122                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3123                 //var pf = progressEl.dom.firstChild;
3124                 //if (pf) {
3125                     //pp = Roo.get(pf.firstChild);
3126                     //pp.setHeight(pf.offsetHeight);
3127                 //}
3128                 
3129             }
3130             return dlg;
3131         },
3132
3133         /**
3134          * Updates the message box body text
3135          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3136          * the XHTML-compliant non-breaking space character '&amp;#160;')
3137          * @return {Roo.MessageBox} This message box
3138          */
3139         updateText : function(text)
3140         {
3141             if(!dlg.isVisible() && !opt.width){
3142                 dlg.dialogEl.setWidth(this.maxWidth);
3143                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3144             }
3145             msgEl.innerHTML = text || '&#160;';
3146       
3147             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3148             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3149             var w = Math.max(
3150                     Math.min(opt.width || cw , this.maxWidth), 
3151                     Math.max(opt.minWidth || this.minWidth, bwidth)
3152             );
3153             if(opt.prompt){
3154                 activeTextEl.setWidth(w);
3155             }
3156             if(dlg.isVisible()){
3157                 dlg.fixedcenter = false;
3158             }
3159             // to big, make it scroll. = But as usual stupid IE does not support
3160             // !important..
3161             
3162             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3163                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3164                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3165             } else {
3166                 bodyEl.dom.style.height = '';
3167                 bodyEl.dom.style.overflowY = '';
3168             }
3169             if (cw > w) {
3170                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3171             } else {
3172                 bodyEl.dom.style.overflowX = '';
3173             }
3174             
3175             dlg.setContentSize(w, bodyEl.getHeight());
3176             if(dlg.isVisible()){
3177                 dlg.fixedcenter = true;
3178             }
3179             return this;
3180         },
3181
3182         /**
3183          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3184          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3185          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3186          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3187          * @return {Roo.MessageBox} This message box
3188          */
3189         updateProgress : function(value, text){
3190             if(text){
3191                 this.updateText(text);
3192             }
3193             if (pp) { // weird bug on my firefox - for some reason this is not defined
3194                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3195             }
3196             return this;
3197         },        
3198
3199         /**
3200          * Returns true if the message box is currently displayed
3201          * @return {Boolean} True if the message box is visible, else false
3202          */
3203         isVisible : function(){
3204             return dlg && dlg.isVisible();  
3205         },
3206
3207         /**
3208          * Hides the message box if it is displayed
3209          */
3210         hide : function(){
3211             if(this.isVisible()){
3212                 dlg.hide();
3213             }  
3214         },
3215
3216         /**
3217          * Displays a new message box, or reinitializes an existing message box, based on the config options
3218          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3219          * The following config object properties are supported:
3220          * <pre>
3221 Property    Type             Description
3222 ----------  ---------------  ------------------------------------------------------------------------------------
3223 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3224                                    closes (defaults to undefined)
3225 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3226                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3227 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3228                                    progress and wait dialogs will ignore this property and always hide the
3229                                    close button as they can only be closed programmatically.
3230 cls               String           A custom CSS class to apply to the message box element
3231 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3232                                    displayed (defaults to 75)
3233 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3234                                    function will be btn (the name of the button that was clicked, if applicable,
3235                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3236                                    Progress and wait dialogs will ignore this option since they do not respond to
3237                                    user actions and can only be closed programmatically, so any required function
3238                                    should be called by the same code after it closes the dialog.
3239 icon              String           A CSS class that provides a background image to be used as an icon for
3240                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3241 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3242 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3243 modal             Boolean          False to allow user interaction with the page while the message box is
3244                                    displayed (defaults to true)
3245 msg               String           A string that will replace the existing message box body text (defaults
3246                                    to the XHTML-compliant non-breaking space character '&#160;')
3247 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3248 progress          Boolean          True to display a progress bar (defaults to false)
3249 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3250 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3251 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3252 title             String           The title text
3253 value             String           The string value to set into the active textbox element if displayed
3254 wait              Boolean          True to display a progress bar (defaults to false)
3255 width             Number           The width of the dialog in pixels
3256 </pre>
3257          *
3258          * Example usage:
3259          * <pre><code>
3260 Roo.Msg.show({
3261    title: 'Address',
3262    msg: 'Please enter your address:',
3263    width: 300,
3264    buttons: Roo.MessageBox.OKCANCEL,
3265    multiline: true,
3266    fn: saveAddress,
3267    animEl: 'addAddressBtn'
3268 });
3269 </code></pre>
3270          * @param {Object} config Configuration options
3271          * @return {Roo.MessageBox} This message box
3272          */
3273         show : function(options)
3274         {
3275             
3276             // this causes nightmares if you show one dialog after another
3277             // especially on callbacks..
3278              
3279             if(this.isVisible()){
3280                 
3281                 this.hide();
3282                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3283                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3284                 Roo.log("New Dialog Message:" +  options.msg )
3285                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3286                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3287                 
3288             }
3289             var d = this.getDialog();
3290             opt = options;
3291             d.setTitle(opt.title || "&#160;");
3292             d.closeEl.setDisplayed(opt.closable !== false);
3293             activeTextEl = textboxEl;
3294             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3295             if(opt.prompt){
3296                 if(opt.multiline){
3297                     textboxEl.hide();
3298                     textareaEl.show();
3299                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3300                         opt.multiline : this.defaultTextHeight);
3301                     activeTextEl = textareaEl;
3302                 }else{
3303                     textboxEl.show();
3304                     textareaEl.hide();
3305                 }
3306             }else{
3307                 textboxEl.hide();
3308                 textareaEl.hide();
3309             }
3310             progressEl.setDisplayed(opt.progress === true);
3311             this.updateProgress(0);
3312             activeTextEl.dom.value = opt.value || "";
3313             if(opt.prompt){
3314                 dlg.setDefaultButton(activeTextEl);
3315             }else{
3316                 var bs = opt.buttons;
3317                 var db = null;
3318                 if(bs && bs.ok){
3319                     db = buttons["ok"];
3320                 }else if(bs && bs.yes){
3321                     db = buttons["yes"];
3322                 }
3323                 dlg.setDefaultButton(db);
3324             }
3325             bwidth = updateButtons(opt.buttons);
3326             this.updateText(opt.msg);
3327             if(opt.cls){
3328                 d.el.addClass(opt.cls);
3329             }
3330             d.proxyDrag = opt.proxyDrag === true;
3331             d.modal = opt.modal !== false;
3332             d.mask = opt.modal !== false ? mask : false;
3333             if(!d.isVisible()){
3334                 // force it to the end of the z-index stack so it gets a cursor in FF
3335                 document.body.appendChild(dlg.el.dom);
3336                 d.animateTarget = null;
3337                 d.show(options.animEl);
3338             }
3339             return this;
3340         },
3341
3342         /**
3343          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3344          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3345          * and closing the message box when the process is complete.
3346          * @param {String} title The title bar text
3347          * @param {String} msg The message box body text
3348          * @return {Roo.MessageBox} This message box
3349          */
3350         progress : function(title, msg){
3351             this.show({
3352                 title : title,
3353                 msg : msg,
3354                 buttons: false,
3355                 progress:true,
3356                 closable:false,
3357                 minWidth: this.minProgressWidth,
3358                 modal : true
3359             });
3360             return this;
3361         },
3362
3363         /**
3364          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3365          * If a callback function is passed it will be called after the user clicks the button, and the
3366          * id of the button that was clicked will be passed as the only parameter to the callback
3367          * (could also be the top-right close button).
3368          * @param {String} title The title bar text
3369          * @param {String} msg The message box body text
3370          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3371          * @param {Object} scope (optional) The scope of the callback function
3372          * @return {Roo.MessageBox} This message box
3373          */
3374         alert : function(title, msg, fn, scope)
3375         {
3376             this.show({
3377                 title : title,
3378                 msg : msg,
3379                 buttons: this.OK,
3380                 fn: fn,
3381                 closable : false,
3382                 scope : scope,
3383                 modal : true
3384             });
3385             return this;
3386         },
3387
3388         /**
3389          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3390          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3391          * You are responsible for closing the message box when the process is complete.
3392          * @param {String} msg The message box body text
3393          * @param {String} title (optional) The title bar text
3394          * @return {Roo.MessageBox} This message box
3395          */
3396         wait : function(msg, title){
3397             this.show({
3398                 title : title,
3399                 msg : msg,
3400                 buttons: false,
3401                 closable:false,
3402                 progress:true,
3403                 modal:true,
3404                 width:300,
3405                 wait:true
3406             });
3407             waitTimer = Roo.TaskMgr.start({
3408                 run: function(i){
3409                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3410                 },
3411                 interval: 1000
3412             });
3413             return this;
3414         },
3415
3416         /**
3417          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3418          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3419          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3420          * @param {String} title The title bar text
3421          * @param {String} msg The message box body text
3422          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3423          * @param {Object} scope (optional) The scope of the callback function
3424          * @return {Roo.MessageBox} This message box
3425          */
3426         confirm : function(title, msg, fn, scope){
3427             this.show({
3428                 title : title,
3429                 msg : msg,
3430                 buttons: this.YESNO,
3431                 fn: fn,
3432                 scope : scope,
3433                 modal : true
3434             });
3435             return this;
3436         },
3437
3438         /**
3439          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3440          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3441          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3442          * (could also be the top-right close button) and the text that was entered will be passed as the two
3443          * parameters to the callback.
3444          * @param {String} title The title bar text
3445          * @param {String} msg The message box body text
3446          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3447          * @param {Object} scope (optional) The scope of the callback function
3448          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3449          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3450          * @return {Roo.MessageBox} This message box
3451          */
3452         prompt : function(title, msg, fn, scope, multiline){
3453             this.show({
3454                 title : title,
3455                 msg : msg,
3456                 buttons: this.OKCANCEL,
3457                 fn: fn,
3458                 minWidth:250,
3459                 scope : scope,
3460                 prompt:true,
3461                 multiline: multiline,
3462                 modal : true
3463             });
3464             return this;
3465         },
3466
3467         /**
3468          * Button config that displays a single OK button
3469          * @type Object
3470          */
3471         OK : {ok:true},
3472         /**
3473          * Button config that displays Yes and No buttons
3474          * @type Object
3475          */
3476         YESNO : {yes:true, no:true},
3477         /**
3478          * Button config that displays OK and Cancel buttons
3479          * @type Object
3480          */
3481         OKCANCEL : {ok:true, cancel:true},
3482         /**
3483          * Button config that displays Yes, No and Cancel buttons
3484          * @type Object
3485          */
3486         YESNOCANCEL : {yes:true, no:true, cancel:true},
3487
3488         /**
3489          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3490          * @type Number
3491          */
3492         defaultTextHeight : 75,
3493         /**
3494          * The maximum width in pixels of the message box (defaults to 600)
3495          * @type Number
3496          */
3497         maxWidth : 600,
3498         /**
3499          * The minimum width in pixels of the message box (defaults to 100)
3500          * @type Number
3501          */
3502         minWidth : 100,
3503         /**
3504          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3505          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3506          * @type Number
3507          */
3508         minProgressWidth : 250,
3509         /**
3510          * An object containing the default button text strings that can be overriden for localized language support.
3511          * Supported properties are: ok, cancel, yes and no.
3512          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3513          * @type Object
3514          */
3515         buttonText : {
3516             ok : "OK",
3517             cancel : "Cancel",
3518             yes : "Yes",
3519             no : "No"
3520         }
3521     };
3522 }();
3523
3524 /**
3525  * Shorthand for {@link Roo.MessageBox}
3526  */
3527 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3528 Roo.Msg = Roo.Msg || Roo.MessageBox;
3529 /*
3530  * - LGPL
3531  *
3532  * navbar
3533  * 
3534  */
3535
3536 /**
3537  * @class Roo.bootstrap.Navbar
3538  * @extends Roo.bootstrap.Component
3539  * Bootstrap Navbar class
3540
3541  * @constructor
3542  * Create a new Navbar
3543  * @param {Object} config The config object
3544  */
3545
3546
3547 Roo.bootstrap.Navbar = function(config){
3548     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3549     this.addEvents({
3550         // raw events
3551         /**
3552          * @event beforetoggle
3553          * Fire before toggle the menu
3554          * @param {Roo.EventObject} e
3555          */
3556         "beforetoggle" : true
3557     });
3558 };
3559
3560 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3561     
3562     
3563    
3564     // private
3565     navItems : false,
3566     loadMask : false,
3567     
3568     
3569     getAutoCreate : function(){
3570         
3571         
3572         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3573         
3574     },
3575     
3576     initEvents :function ()
3577     {
3578         //Roo.log(this.el.select('.navbar-toggle',true));
3579         this.el.select('.navbar-toggle',true).on('click', function() {
3580             if(this.fireEvent('beforetoggle', this) !== false){
3581                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3582             }
3583             
3584         }, this);
3585         
3586         var mark = {
3587             tag: "div",
3588             cls:"x-dlg-mask"
3589         };
3590         
3591         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3592         
3593         var size = this.el.getSize();
3594         this.maskEl.setSize(size.width, size.height);
3595         this.maskEl.enableDisplayMode("block");
3596         this.maskEl.hide();
3597         
3598         if(this.loadMask){
3599             this.maskEl.show();
3600         }
3601     },
3602     
3603     
3604     getChildContainer : function()
3605     {
3606         if (this.el.select('.collapse').getCount()) {
3607             return this.el.select('.collapse',true).first();
3608         }
3609         
3610         return this.el;
3611     },
3612     
3613     mask : function()
3614     {
3615         this.maskEl.show();
3616     },
3617     
3618     unmask : function()
3619     {
3620         this.maskEl.hide();
3621     } 
3622     
3623     
3624     
3625     
3626 });
3627
3628
3629
3630  
3631
3632  /*
3633  * - LGPL
3634  *
3635  * navbar
3636  * 
3637  */
3638
3639 /**
3640  * @class Roo.bootstrap.NavSimplebar
3641  * @extends Roo.bootstrap.Navbar
3642  * Bootstrap Sidebar class
3643  *
3644  * @cfg {Boolean} inverse is inverted color
3645  * 
3646  * @cfg {String} type (nav | pills | tabs)
3647  * @cfg {Boolean} arrangement stacked | justified
3648  * @cfg {String} align (left | right) alignment
3649  * 
3650  * @cfg {Boolean} main (true|false) main nav bar? default false
3651  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3652  * 
3653  * @cfg {String} tag (header|footer|nav|div) default is nav 
3654
3655  * 
3656  * 
3657  * 
3658  * @constructor
3659  * Create a new Sidebar
3660  * @param {Object} config The config object
3661  */
3662
3663
3664 Roo.bootstrap.NavSimplebar = function(config){
3665     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3666 };
3667
3668 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3669     
3670     inverse: false,
3671     
3672     type: false,
3673     arrangement: '',
3674     align : false,
3675     
3676     
3677     
3678     main : false,
3679     
3680     
3681     tag : false,
3682     
3683     
3684     getAutoCreate : function(){
3685         
3686         
3687         var cfg = {
3688             tag : this.tag || 'div',
3689             cls : 'navbar'
3690         };
3691           
3692         
3693         cfg.cn = [
3694             {
3695                 cls: 'nav',
3696                 tag : 'ul'
3697             }
3698         ];
3699         
3700          
3701         this.type = this.type || 'nav';
3702         if (['tabs','pills'].indexOf(this.type)!==-1) {
3703             cfg.cn[0].cls += ' nav-' + this.type
3704         
3705         
3706         } else {
3707             if (this.type!=='nav') {
3708                 Roo.log('nav type must be nav/tabs/pills')
3709             }
3710             cfg.cn[0].cls += ' navbar-nav'
3711         }
3712         
3713         
3714         
3715         
3716         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3717             cfg.cn[0].cls += ' nav-' + this.arrangement;
3718         }
3719         
3720         
3721         if (this.align === 'right') {
3722             cfg.cn[0].cls += ' navbar-right';
3723         }
3724         
3725         if (this.inverse) {
3726             cfg.cls += ' navbar-inverse';
3727             
3728         }
3729         
3730         
3731         return cfg;
3732     
3733         
3734     }
3735     
3736     
3737     
3738 });
3739
3740
3741
3742  
3743
3744  
3745        /*
3746  * - LGPL
3747  *
3748  * navbar
3749  * 
3750  */
3751
3752 /**
3753  * @class Roo.bootstrap.NavHeaderbar
3754  * @extends Roo.bootstrap.NavSimplebar
3755  * Bootstrap Sidebar class
3756  *
3757  * @cfg {String} brand what is brand
3758  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3759  * @cfg {String} brand_href href of the brand
3760  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3761  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3762  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3763  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3764  * 
3765  * @constructor
3766  * Create a new Sidebar
3767  * @param {Object} config The config object
3768  */
3769
3770
3771 Roo.bootstrap.NavHeaderbar = function(config){
3772     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3773       
3774 };
3775
3776 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3777     
3778     position: '',
3779     brand: '',
3780     brand_href: false,
3781     srButton : true,
3782     autohide : false,
3783     desktopCenter : false,
3784    
3785     
3786     getAutoCreate : function(){
3787         
3788         var   cfg = {
3789             tag: this.nav || 'nav',
3790             cls: 'navbar',
3791             role: 'navigation',
3792             cn: []
3793         };
3794         
3795         var cn = cfg.cn;
3796         if (this.desktopCenter) {
3797             cn.push({cls : 'container', cn : []});
3798             cn = cn[0].cn;
3799         }
3800         
3801         if(this.srButton){
3802             cn.push({
3803                 tag: 'div',
3804                 cls: 'navbar-header',
3805                 cn: [
3806                     {
3807                         tag: 'button',
3808                         type: 'button',
3809                         cls: 'navbar-toggle',
3810                         'data-toggle': 'collapse',
3811                         cn: [
3812                             {
3813                                 tag: 'span',
3814                                 cls: 'sr-only',
3815                                 html: 'Toggle navigation'
3816                             },
3817                             {
3818                                 tag: 'span',
3819                                 cls: 'icon-bar'
3820                             },
3821                             {
3822                                 tag: 'span',
3823                                 cls: 'icon-bar'
3824                             },
3825                             {
3826                                 tag: 'span',
3827                                 cls: 'icon-bar'
3828                             }
3829                         ]
3830                     }
3831                 ]
3832             });
3833         }
3834         
3835         cn.push({
3836             tag: 'div',
3837             cls: 'collapse navbar-collapse',
3838             cn : []
3839         });
3840         
3841         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3842         
3843         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3844             cfg.cls += ' navbar-' + this.position;
3845             
3846             // tag can override this..
3847             
3848             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3849         }
3850         
3851         if (this.brand !== '') {
3852             cn[0].cn.push({
3853                 tag: 'a',
3854                 href: this.brand_href ? this.brand_href : '#',
3855                 cls: 'navbar-brand',
3856                 cn: [
3857                 this.brand
3858                 ]
3859             });
3860         }
3861         
3862         if(this.main){
3863             cfg.cls += ' main-nav';
3864         }
3865         
3866         
3867         return cfg;
3868
3869         
3870     },
3871     getHeaderChildContainer : function()
3872     {
3873         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3874             return this.el.select('.navbar-header',true).first();
3875         }
3876         
3877         return this.getChildContainer();
3878     },
3879     
3880     
3881     initEvents : function()
3882     {
3883         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3884         
3885         if (this.autohide) {
3886             
3887             var prevScroll = 0;
3888             var ft = this.el;
3889             
3890             Roo.get(document).on('scroll',function(e) {
3891                 var ns = Roo.get(document).getScroll().top;
3892                 var os = prevScroll;
3893                 prevScroll = ns;
3894                 
3895                 if(ns > os){
3896                     ft.removeClass('slideDown');
3897                     ft.addClass('slideUp');
3898                     return;
3899                 }
3900                 ft.removeClass('slideUp');
3901                 ft.addClass('slideDown');
3902                  
3903               
3904           },this);
3905         }
3906     }    
3907     
3908 });
3909
3910
3911
3912  
3913
3914  /*
3915  * - LGPL
3916  *
3917  * navbar
3918  * 
3919  */
3920
3921 /**
3922  * @class Roo.bootstrap.NavSidebar
3923  * @extends Roo.bootstrap.Navbar
3924  * Bootstrap Sidebar class
3925  * 
3926  * @constructor
3927  * Create a new Sidebar
3928  * @param {Object} config The config object
3929  */
3930
3931
3932 Roo.bootstrap.NavSidebar = function(config){
3933     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3934 };
3935
3936 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3937     
3938     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3939     
3940     getAutoCreate : function(){
3941         
3942         
3943         return  {
3944             tag: 'div',
3945             cls: 'sidebar sidebar-nav'
3946         };
3947     
3948         
3949     }
3950     
3951     
3952     
3953 });
3954
3955
3956
3957  
3958
3959  /*
3960  * - LGPL
3961  *
3962  * nav group
3963  * 
3964  */
3965
3966 /**
3967  * @class Roo.bootstrap.NavGroup
3968  * @extends Roo.bootstrap.Component
3969  * Bootstrap NavGroup class
3970  * @cfg {String} align (left|right)
3971  * @cfg {Boolean} inverse
3972  * @cfg {String} type (nav|pills|tab) default nav
3973  * @cfg {String} navId - reference Id for navbar.
3974
3975  * 
3976  * @constructor
3977  * Create a new nav group
3978  * @param {Object} config The config object
3979  */
3980
3981 Roo.bootstrap.NavGroup = function(config){
3982     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3983     this.navItems = [];
3984    
3985     Roo.bootstrap.NavGroup.register(this);
3986      this.addEvents({
3987         /**
3988              * @event changed
3989              * Fires when the active item changes
3990              * @param {Roo.bootstrap.NavGroup} this
3991              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
3992              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
3993          */
3994         'changed': true
3995      });
3996     
3997 };
3998
3999 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4000     
4001     align: '',
4002     inverse: false,
4003     form: false,
4004     type: 'nav',
4005     navId : '',
4006     // private
4007     
4008     navItems : false, 
4009     
4010     getAutoCreate : function()
4011     {
4012         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4013         
4014         cfg = {
4015             tag : 'ul',
4016             cls: 'nav' 
4017         };
4018         
4019         if (['tabs','pills'].indexOf(this.type)!==-1) {
4020             cfg.cls += ' nav-' + this.type
4021         } else {
4022             if (this.type!=='nav') {
4023                 Roo.log('nav type must be nav/tabs/pills')
4024             }
4025             cfg.cls += ' navbar-nav'
4026         }
4027         
4028         if (this.parent().sidebar) {
4029             cfg = {
4030                 tag: 'ul',
4031                 cls: 'dashboard-menu sidebar-menu'
4032             };
4033             
4034             return cfg;
4035         }
4036         
4037         if (this.form === true) {
4038             cfg = {
4039                 tag: 'form',
4040                 cls: 'navbar-form'
4041             };
4042             
4043             if (this.align === 'right') {
4044                 cfg.cls += ' navbar-right';
4045             } else {
4046                 cfg.cls += ' navbar-left';
4047             }
4048         }
4049         
4050         if (this.align === 'right') {
4051             cfg.cls += ' navbar-right';
4052         }
4053         
4054         if (this.inverse) {
4055             cfg.cls += ' navbar-inverse';
4056             
4057         }
4058         
4059         
4060         return cfg;
4061     },
4062     /**
4063     * sets the active Navigation item
4064     * @param {Roo.bootstrap.NavItem} the new current navitem
4065     */
4066     setActiveItem : function(item)
4067     {
4068         var prev = false;
4069         Roo.each(this.navItems, function(v){
4070             if (v == item) {
4071                 return ;
4072             }
4073             if (v.isActive()) {
4074                 v.setActive(false, true);
4075                 prev = v;
4076                 
4077             }
4078             
4079         });
4080
4081         item.setActive(true, true);
4082         this.fireEvent('changed', this, item, prev);
4083         
4084         
4085     },
4086     /**
4087     * gets the active Navigation item
4088     * @return {Roo.bootstrap.NavItem} the current navitem
4089     */
4090     getActive : function()
4091     {
4092         
4093         var prev = false;
4094         Roo.each(this.navItems, function(v){
4095             
4096             if (v.isActive()) {
4097                 prev = v;
4098                 
4099             }
4100             
4101         });
4102         return prev;
4103     },
4104     
4105     indexOfNav : function()
4106     {
4107         
4108         var prev = false;
4109         Roo.each(this.navItems, function(v,i){
4110             
4111             if (v.isActive()) {
4112                 prev = i;
4113                 
4114             }
4115             
4116         });
4117         return prev;
4118     },
4119     /**
4120     * adds a Navigation item
4121     * @param {Roo.bootstrap.NavItem} the navitem to add
4122     */
4123     addItem : function(cfg)
4124     {
4125         var cn = new Roo.bootstrap.NavItem(cfg);
4126         this.register(cn);
4127         cn.parentId = this.id;
4128         cn.onRender(this.el, null);
4129         return cn;
4130     },
4131     /**
4132     * register a Navigation item
4133     * @param {Roo.bootstrap.NavItem} the navitem to add
4134     */
4135     register : function(item)
4136     {
4137         this.navItems.push( item);
4138         item.navId = this.navId;
4139     
4140     },
4141     
4142     /**
4143     * clear all the Navigation item
4144     */
4145    
4146     clearAll : function()
4147     {
4148         this.navItems = [];
4149         this.el.dom.innerHTML = '';
4150     },
4151     
4152     getNavItem: function(tabId)
4153     {
4154         var ret = false;
4155         Roo.each(this.navItems, function(e) {
4156             if (e.tabId == tabId) {
4157                ret =  e;
4158                return false;
4159             }
4160             return true;
4161             
4162         });
4163         return ret;
4164     },
4165     
4166     setActiveNext : function()
4167     {
4168         var i = this.indexOfNav(this.getActive());
4169         if (i > this.navItems.length) {
4170             return;
4171         }
4172         this.setActiveItem(this.navItems[i+1]);
4173     },
4174     setActivePrev : function()
4175     {
4176         var i = this.indexOfNav(this.getActive());
4177         if (i  < 1) {
4178             return;
4179         }
4180         this.setActiveItem(this.navItems[i-1]);
4181     },
4182     clearWasActive : function(except) {
4183         Roo.each(this.navItems, function(e) {
4184             if (e.tabId != except.tabId && e.was_active) {
4185                e.was_active = false;
4186                return false;
4187             }
4188             return true;
4189             
4190         });
4191     },
4192     getWasActive : function ()
4193     {
4194         var r = false;
4195         Roo.each(this.navItems, function(e) {
4196             if (e.was_active) {
4197                r = e;
4198                return false;
4199             }
4200             return true;
4201             
4202         });
4203         return r;
4204     }
4205     
4206     
4207 });
4208
4209  
4210 Roo.apply(Roo.bootstrap.NavGroup, {
4211     
4212     groups: {},
4213      /**
4214     * register a Navigation Group
4215     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4216     */
4217     register : function(navgrp)
4218     {
4219         this.groups[navgrp.navId] = navgrp;
4220         
4221     },
4222     /**
4223     * fetch a Navigation Group based on the navigation ID
4224     * @param {string} the navgroup to add
4225     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4226     */
4227     get: function(navId) {
4228         if (typeof(this.groups[navId]) == 'undefined') {
4229             return false;
4230             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4231         }
4232         return this.groups[navId] ;
4233     }
4234     
4235     
4236     
4237 });
4238
4239  /*
4240  * - LGPL
4241  *
4242  * row
4243  * 
4244  */
4245
4246 /**
4247  * @class Roo.bootstrap.NavItem
4248  * @extends Roo.bootstrap.Component
4249  * Bootstrap Navbar.NavItem class
4250  * @cfg {String} href  link to
4251  * @cfg {String} html content of button
4252  * @cfg {String} badge text inside badge
4253  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4254  * @cfg {String} glyphicon name of glyphicon
4255  * @cfg {String} icon name of font awesome icon
4256  * @cfg {Boolean} active Is item active
4257  * @cfg {Boolean} disabled Is item disabled
4258  
4259  * @cfg {Boolean} preventDefault (true | false) default false
4260  * @cfg {String} tabId the tab that this item activates.
4261  * @cfg {String} tagtype (a|span) render as a href or span?
4262  * @cfg {Boolean} animateRef (true|false) link to element default false  
4263   
4264  * @constructor
4265  * Create a new Navbar Item
4266  * @param {Object} config The config object
4267  */
4268 Roo.bootstrap.NavItem = function(config){
4269     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4270     this.addEvents({
4271         // raw events
4272         /**
4273          * @event click
4274          * The raw click event for the entire grid.
4275          * @param {Roo.EventObject} e
4276          */
4277         "click" : true,
4278          /**
4279             * @event changed
4280             * Fires when the active item active state changes
4281             * @param {Roo.bootstrap.NavItem} this
4282             * @param {boolean} state the new state
4283              
4284          */
4285         'changed': true,
4286         /**
4287             * @event scrollto
4288             * Fires when scroll to element
4289             * @param {Roo.bootstrap.NavItem} this
4290             * @param {Object} options
4291             * @param {Roo.EventObject} e
4292              
4293          */
4294         'scrollto': true
4295     });
4296    
4297 };
4298
4299 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4300     
4301     href: false,
4302     html: '',
4303     badge: '',
4304     icon: false,
4305     glyphicon: false,
4306     active: false,
4307     preventDefault : false,
4308     tabId : false,
4309     tagtype : 'a',
4310     disabled : false,
4311     animateRef : false,
4312     was_active : false,
4313     
4314     getAutoCreate : function(){
4315          
4316         var cfg = {
4317             tag: 'li',
4318             cls: 'nav-item'
4319             
4320         };
4321         
4322         if (this.active) {
4323             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4324         }
4325         if (this.disabled) {
4326             cfg.cls += ' disabled';
4327         }
4328         
4329         if (this.href || this.html || this.glyphicon || this.icon) {
4330             cfg.cn = [
4331                 {
4332                     tag: this.tagtype,
4333                     href : this.href || "#",
4334                     html: this.html || ''
4335                 }
4336             ];
4337             
4338             if (this.icon) {
4339                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4340             }
4341
4342             if(this.glyphicon) {
4343                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4344             }
4345             
4346             if (this.menu) {
4347                 
4348                 cfg.cn[0].html += " <span class='caret'></span>";
4349              
4350             }
4351             
4352             if (this.badge !== '') {
4353                  
4354                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4355             }
4356         }
4357         
4358         
4359         
4360         return cfg;
4361     },
4362     initEvents: function() 
4363     {
4364         if (typeof (this.menu) != 'undefined') {
4365             this.menu.parentType = this.xtype;
4366             this.menu.triggerEl = this.el;
4367             this.menu = this.addxtype(Roo.apply({}, this.menu));
4368         }
4369         
4370         this.el.select('a',true).on('click', this.onClick, this);
4371         
4372         if(this.tagtype == 'span'){
4373             this.el.select('span',true).on('click', this.onClick, this);
4374         }
4375        
4376         // at this point parent should be available..
4377         this.parent().register(this);
4378     },
4379     
4380     onClick : function(e)
4381     {
4382         if (e.getTarget('.dropdown-menu-item')) {
4383             // did you click on a menu itemm.... - then don't trigger onclick..
4384             return;
4385         }
4386         
4387         if(
4388                 this.preventDefault || 
4389                 this.href == '#' 
4390         ){
4391             Roo.log("NavItem - prevent Default?");
4392             e.preventDefault();
4393         }
4394         
4395         if (this.disabled) {
4396             return;
4397         }
4398         
4399         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4400         if (tg && tg.transition) {
4401             Roo.log("waiting for the transitionend");
4402             return;
4403         }
4404         
4405         
4406         
4407         //Roo.log("fire event clicked");
4408         if(this.fireEvent('click', this, e) === false){
4409             return;
4410         };
4411         
4412         if(this.tagtype == 'span'){
4413             return;
4414         }
4415         
4416         //Roo.log(this.href);
4417         var ael = this.el.select('a',true).first();
4418         //Roo.log(ael);
4419         
4420         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4421             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4422             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4423                 return; // ignore... - it's a 'hash' to another page.
4424             }
4425             Roo.log("NavItem - prevent Default?");
4426             e.preventDefault();
4427             this.scrollToElement(e);
4428         }
4429         
4430         
4431         var p =  this.parent();
4432    
4433         if (['tabs','pills'].indexOf(p.type)!==-1) {
4434             if (typeof(p.setActiveItem) !== 'undefined') {
4435                 p.setActiveItem(this);
4436             }
4437         }
4438         
4439         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4440         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4441             // remove the collapsed menu expand...
4442             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4443         }
4444     },
4445     
4446     isActive: function () {
4447         return this.active
4448     },
4449     setActive : function(state, fire, is_was_active)
4450     {
4451         if (this.active && !state && this.navId) {
4452             this.was_active = true;
4453             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4454             if (nv) {
4455                 nv.clearWasActive(this);
4456             }
4457             
4458         }
4459         this.active = state;
4460         
4461         if (!state ) {
4462             this.el.removeClass('active');
4463         } else if (!this.el.hasClass('active')) {
4464             this.el.addClass('active');
4465         }
4466         if (fire) {
4467             this.fireEvent('changed', this, state);
4468         }
4469         
4470         // show a panel if it's registered and related..
4471         
4472         if (!this.navId || !this.tabId || !state || is_was_active) {
4473             return;
4474         }
4475         
4476         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4477         if (!tg) {
4478             return;
4479         }
4480         var pan = tg.getPanelByName(this.tabId);
4481         if (!pan) {
4482             return;
4483         }
4484         // if we can not flip to new panel - go back to old nav highlight..
4485         if (false == tg.showPanel(pan)) {
4486             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4487             if (nv) {
4488                 var onav = nv.getWasActive();
4489                 if (onav) {
4490                     onav.setActive(true, false, true);
4491                 }
4492             }
4493             
4494         }
4495         
4496         
4497         
4498     },
4499      // this should not be here...
4500     setDisabled : function(state)
4501     {
4502         this.disabled = state;
4503         if (!state ) {
4504             this.el.removeClass('disabled');
4505         } else if (!this.el.hasClass('disabled')) {
4506             this.el.addClass('disabled');
4507         }
4508         
4509     },
4510     
4511     /**
4512      * Fetch the element to display the tooltip on.
4513      * @return {Roo.Element} defaults to this.el
4514      */
4515     tooltipEl : function()
4516     {
4517         return this.el.select('' + this.tagtype + '', true).first();
4518     },
4519     
4520     scrollToElement : function(e)
4521     {
4522         var c = document.body;
4523         
4524         /*
4525          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4526          */
4527         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4528             c = document.documentElement;
4529         }
4530         
4531         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4532         
4533         if(!target){
4534             return;
4535         }
4536
4537         var o = target.calcOffsetsTo(c);
4538         
4539         var options = {
4540             target : target,
4541             value : o[1]
4542         };
4543         
4544         this.fireEvent('scrollto', this, options, e);
4545         
4546         Roo.get(c).scrollTo('top', options.value, true);
4547         
4548         return;
4549     }
4550 });
4551  
4552
4553  /*
4554  * - LGPL
4555  *
4556  * sidebar item
4557  *
4558  *  li
4559  *    <span> icon </span>
4560  *    <span> text </span>
4561  *    <span>badge </span>
4562  */
4563
4564 /**
4565  * @class Roo.bootstrap.NavSidebarItem
4566  * @extends Roo.bootstrap.NavItem
4567  * Bootstrap Navbar.NavSidebarItem class
4568  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4569  * {bool} open is the menu open
4570  * @constructor
4571  * Create a new Navbar Button
4572  * @param {Object} config The config object
4573  */
4574 Roo.bootstrap.NavSidebarItem = function(config){
4575     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4576     this.addEvents({
4577         // raw events
4578         /**
4579          * @event click
4580          * The raw click event for the entire grid.
4581          * @param {Roo.EventObject} e
4582          */
4583         "click" : true,
4584          /**
4585             * @event changed
4586             * Fires when the active item active state changes
4587             * @param {Roo.bootstrap.NavSidebarItem} this
4588             * @param {boolean} state the new state
4589              
4590          */
4591         'changed': true
4592     });
4593    
4594 };
4595
4596 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4597     
4598     badgeWeight : 'default',
4599     
4600     open: false,
4601     
4602     getAutoCreate : function(){
4603         
4604         
4605         var a = {
4606                 tag: 'a',
4607                 href : this.href || '#',
4608                 cls: '',
4609                 html : '',
4610                 cn : []
4611         };
4612         var cfg = {
4613             tag: 'li',
4614             cls: '',
4615             cn: [ a ]
4616         };
4617         var span = {
4618             tag: 'span',
4619             html : this.html || ''
4620         };
4621         
4622         
4623         if (this.active) {
4624             cfg.cls += ' active';
4625         }
4626         
4627         if (this.disabled) {
4628             cfg.cls += ' disabled';
4629         }
4630         if (this.open) {
4631             cfg.cls += ' open x-open';
4632         }
4633         // left icon..
4634         if (this.glyphicon || this.icon) {
4635             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4636             a.cn.push({ tag : 'i', cls : c }) ;
4637         }
4638         // html..
4639         a.cn.push(span);
4640         // then badge..
4641         if (this.badge !== '') {
4642             
4643             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4644         }
4645         // fi
4646         if (this.menu) {
4647             a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4648             a.cls += 'dropdown-toggle treeview' ;
4649         }
4650         
4651         return cfg;
4652          
4653            
4654     },
4655     
4656     initEvents : function()
4657     { 
4658         if (typeof (this.menu) != 'undefined') {
4659             this.menu.parentType = this.xtype;
4660             this.menu.triggerEl = this.el;
4661             this.menu = this.addxtype(Roo.apply({}, this.menu));
4662         }
4663         
4664         this.el.on('click', this.onClick, this);
4665        
4666     
4667         if(this.badge !== ''){
4668  
4669             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4670         }
4671         
4672     },
4673     
4674     onClick : function(e)
4675     {
4676         if(this.disabled){
4677             e.preventDefault();
4678             return;
4679         }
4680         
4681         if(this.preventDefault){
4682             e.preventDefault();
4683         }
4684         
4685         this.fireEvent('click', this);
4686     },
4687     
4688     disable : function()
4689     {
4690         this.setDisabled(true);
4691     },
4692     
4693     enable : function()
4694     {
4695         this.setDisabled(false);
4696     },
4697     
4698     setDisabled : function(state)
4699     {
4700         if(this.disabled == state){
4701             return;
4702         }
4703         
4704         this.disabled = state;
4705         
4706         if (state) {
4707             this.el.addClass('disabled');
4708             return;
4709         }
4710         
4711         this.el.removeClass('disabled');
4712         
4713         return;
4714     },
4715     
4716     setActive : function(state)
4717     {
4718         if(this.active == state){
4719             return;
4720         }
4721         
4722         this.active = state;
4723         
4724         if (state) {
4725             this.el.addClass('active');
4726             return;
4727         }
4728         
4729         this.el.removeClass('active');
4730         
4731         return;
4732     },
4733     
4734     isActive: function () 
4735     {
4736         return this.active;
4737     },
4738     
4739     setBadge : function(str)
4740     {
4741         if(!this.badgeEl){
4742             return;
4743         }
4744         
4745         this.badgeEl.dom.innerHTML = str;
4746     }
4747     
4748    
4749      
4750  
4751 });
4752  
4753
4754  /*
4755  * - LGPL
4756  *
4757  * row
4758  * 
4759  */
4760
4761 /**
4762  * @class Roo.bootstrap.Row
4763  * @extends Roo.bootstrap.Component
4764  * Bootstrap Row class (contains columns...)
4765  * 
4766  * @constructor
4767  * Create a new Row
4768  * @param {Object} config The config object
4769  */
4770
4771 Roo.bootstrap.Row = function(config){
4772     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4773 };
4774
4775 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4776     
4777     getAutoCreate : function(){
4778        return {
4779             cls: 'row clearfix'
4780        };
4781     }
4782     
4783     
4784 });
4785
4786  
4787
4788  /*
4789  * - LGPL
4790  *
4791  * element
4792  * 
4793  */
4794
4795 /**
4796  * @class Roo.bootstrap.Element
4797  * @extends Roo.bootstrap.Component
4798  * Bootstrap Element class
4799  * @cfg {String} html contents of the element
4800  * @cfg {String} tag tag of the element
4801  * @cfg {String} cls class of the element
4802  * @cfg {Boolean} preventDefault (true|false) default false
4803  * @cfg {Boolean} clickable (true|false) default false
4804  * 
4805  * @constructor
4806  * Create a new Element
4807  * @param {Object} config The config object
4808  */
4809
4810 Roo.bootstrap.Element = function(config){
4811     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4812     
4813     this.addEvents({
4814         // raw events
4815         /**
4816          * @event click
4817          * When a element is chick
4818          * @param {Roo.bootstrap.Element} this
4819          * @param {Roo.EventObject} e
4820          */
4821         "click" : true
4822     });
4823 };
4824
4825 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4826     
4827     tag: 'div',
4828     cls: '',
4829     html: '',
4830     preventDefault: false, 
4831     clickable: false,
4832     
4833     getAutoCreate : function(){
4834         
4835         var cfg = {
4836             tag: this.tag,
4837             cls: this.cls,
4838             html: this.html
4839         };
4840         
4841         return cfg;
4842     },
4843     
4844     initEvents: function() 
4845     {
4846         Roo.bootstrap.Element.superclass.initEvents.call(this);
4847         
4848         if(this.clickable){
4849             this.el.on('click', this.onClick, this);
4850         }
4851         
4852     },
4853     
4854     onClick : function(e)
4855     {
4856         if(this.preventDefault){
4857             e.preventDefault();
4858         }
4859         
4860         this.fireEvent('click', this, e);
4861     },
4862     
4863     getValue : function()
4864     {
4865         return this.el.dom.innerHTML;
4866     },
4867     
4868     setValue : function(value)
4869     {
4870         this.el.dom.innerHTML = value;
4871     }
4872    
4873 });
4874
4875  
4876
4877  /*
4878  * - LGPL
4879  *
4880  * pagination
4881  * 
4882  */
4883
4884 /**
4885  * @class Roo.bootstrap.Pagination
4886  * @extends Roo.bootstrap.Component
4887  * Bootstrap Pagination class
4888  * @cfg {String} size xs | sm | md | lg
4889  * @cfg {Boolean} inverse false | true
4890  * 
4891  * @constructor
4892  * Create a new Pagination
4893  * @param {Object} config The config object
4894  */
4895
4896 Roo.bootstrap.Pagination = function(config){
4897     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4898 };
4899
4900 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4901     
4902     cls: false,
4903     size: false,
4904     inverse: false,
4905     
4906     getAutoCreate : function(){
4907         var cfg = {
4908             tag: 'ul',
4909                 cls: 'pagination'
4910         };
4911         if (this.inverse) {
4912             cfg.cls += ' inverse';
4913         }
4914         if (this.html) {
4915             cfg.html=this.html;
4916         }
4917         if (this.cls) {
4918             cfg.cls += " " + this.cls;
4919         }
4920         return cfg;
4921     }
4922    
4923 });
4924
4925  
4926
4927  /*
4928  * - LGPL
4929  *
4930  * Pagination item
4931  * 
4932  */
4933
4934
4935 /**
4936  * @class Roo.bootstrap.PaginationItem
4937  * @extends Roo.bootstrap.Component
4938  * Bootstrap PaginationItem class
4939  * @cfg {String} html text
4940  * @cfg {String} href the link
4941  * @cfg {Boolean} preventDefault (true | false) default true
4942  * @cfg {Boolean} active (true | false) default false
4943  * @cfg {Boolean} disabled default false
4944  * 
4945  * 
4946  * @constructor
4947  * Create a new PaginationItem
4948  * @param {Object} config The config object
4949  */
4950
4951
4952 Roo.bootstrap.PaginationItem = function(config){
4953     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4954     this.addEvents({
4955         // raw events
4956         /**
4957          * @event click
4958          * The raw click event for the entire grid.
4959          * @param {Roo.EventObject} e
4960          */
4961         "click" : true
4962     });
4963 };
4964
4965 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
4966     
4967     href : false,
4968     html : false,
4969     preventDefault: true,
4970     active : false,
4971     cls : false,
4972     disabled: false,
4973     
4974     getAutoCreate : function(){
4975         var cfg= {
4976             tag: 'li',
4977             cn: [
4978                 {
4979                     tag : 'a',
4980                     href : this.href ? this.href : '#',
4981                     html : this.html ? this.html : ''
4982                 }
4983             ]
4984         };
4985         
4986         if(this.cls){
4987             cfg.cls = this.cls;
4988         }
4989         
4990         if(this.disabled){
4991             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
4992         }
4993         
4994         if(this.active){
4995             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
4996         }
4997         
4998         return cfg;
4999     },
5000     
5001     initEvents: function() {
5002         
5003         this.el.on('click', this.onClick, this);
5004         
5005     },
5006     onClick : function(e)
5007     {
5008         Roo.log('PaginationItem on click ');
5009         if(this.preventDefault){
5010             e.preventDefault();
5011         }
5012         
5013         if(this.disabled){
5014             return;
5015         }
5016         
5017         this.fireEvent('click', this, e);
5018     }
5019    
5020 });
5021
5022  
5023
5024  /*
5025  * - LGPL
5026  *
5027  * slider
5028  * 
5029  */
5030
5031
5032 /**
5033  * @class Roo.bootstrap.Slider
5034  * @extends Roo.bootstrap.Component
5035  * Bootstrap Slider class
5036  *    
5037  * @constructor
5038  * Create a new Slider
5039  * @param {Object} config The config object
5040  */
5041
5042 Roo.bootstrap.Slider = function(config){
5043     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5044 };
5045
5046 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5047     
5048     getAutoCreate : function(){
5049         
5050         var cfg = {
5051             tag: 'div',
5052             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5053             cn: [
5054                 {
5055                     tag: 'a',
5056                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5057                 }
5058             ]
5059         };
5060         
5061         return cfg;
5062     }
5063    
5064 });
5065
5066  /*
5067  * Based on:
5068  * Ext JS Library 1.1.1
5069  * Copyright(c) 2006-2007, Ext JS, LLC.
5070  *
5071  * Originally Released Under LGPL - original licence link has changed is not relivant.
5072  *
5073  * Fork - LGPL
5074  * <script type="text/javascript">
5075  */
5076  
5077
5078 /**
5079  * @class Roo.grid.ColumnModel
5080  * @extends Roo.util.Observable
5081  * This is the default implementation of a ColumnModel used by the Grid. It defines
5082  * the columns in the grid.
5083  * <br>Usage:<br>
5084  <pre><code>
5085  var colModel = new Roo.grid.ColumnModel([
5086         {header: "Ticker", width: 60, sortable: true, locked: true},
5087         {header: "Company Name", width: 150, sortable: true},
5088         {header: "Market Cap.", width: 100, sortable: true},
5089         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5090         {header: "Employees", width: 100, sortable: true, resizable: false}
5091  ]);
5092  </code></pre>
5093  * <p>
5094  
5095  * The config options listed for this class are options which may appear in each
5096  * individual column definition.
5097  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5098  * @constructor
5099  * @param {Object} config An Array of column config objects. See this class's
5100  * config objects for details.
5101 */
5102 Roo.grid.ColumnModel = function(config){
5103         /**
5104      * The config passed into the constructor
5105      */
5106     this.config = config;
5107     this.lookup = {};
5108
5109     // if no id, create one
5110     // if the column does not have a dataIndex mapping,
5111     // map it to the order it is in the config
5112     for(var i = 0, len = config.length; i < len; i++){
5113         var c = config[i];
5114         if(typeof c.dataIndex == "undefined"){
5115             c.dataIndex = i;
5116         }
5117         if(typeof c.renderer == "string"){
5118             c.renderer = Roo.util.Format[c.renderer];
5119         }
5120         if(typeof c.id == "undefined"){
5121             c.id = Roo.id();
5122         }
5123         if(c.editor && c.editor.xtype){
5124             c.editor  = Roo.factory(c.editor, Roo.grid);
5125         }
5126         if(c.editor && c.editor.isFormField){
5127             c.editor = new Roo.grid.GridEditor(c.editor);
5128         }
5129         this.lookup[c.id] = c;
5130     }
5131
5132     /**
5133      * The width of columns which have no width specified (defaults to 100)
5134      * @type Number
5135      */
5136     this.defaultWidth = 100;
5137
5138     /**
5139      * Default sortable of columns which have no sortable specified (defaults to false)
5140      * @type Boolean
5141      */
5142     this.defaultSortable = false;
5143
5144     this.addEvents({
5145         /**
5146              * @event widthchange
5147              * Fires when the width of a column changes.
5148              * @param {ColumnModel} this
5149              * @param {Number} columnIndex The column index
5150              * @param {Number} newWidth The new width
5151              */
5152             "widthchange": true,
5153         /**
5154              * @event headerchange
5155              * Fires when the text of a header changes.
5156              * @param {ColumnModel} this
5157              * @param {Number} columnIndex The column index
5158              * @param {Number} newText The new header text
5159              */
5160             "headerchange": true,
5161         /**
5162              * @event hiddenchange
5163              * Fires when a column is hidden or "unhidden".
5164              * @param {ColumnModel} this
5165              * @param {Number} columnIndex The column index
5166              * @param {Boolean} hidden true if hidden, false otherwise
5167              */
5168             "hiddenchange": true,
5169             /**
5170          * @event columnmoved
5171          * Fires when a column is moved.
5172          * @param {ColumnModel} this
5173          * @param {Number} oldIndex
5174          * @param {Number} newIndex
5175          */
5176         "columnmoved" : true,
5177         /**
5178          * @event columlockchange
5179          * Fires when a column's locked state is changed
5180          * @param {ColumnModel} this
5181          * @param {Number} colIndex
5182          * @param {Boolean} locked true if locked
5183          */
5184         "columnlockchange" : true
5185     });
5186     Roo.grid.ColumnModel.superclass.constructor.call(this);
5187 };
5188 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5189     /**
5190      * @cfg {String} header The header text to display in the Grid view.
5191      */
5192     /**
5193      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5194      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5195      * specified, the column's index is used as an index into the Record's data Array.
5196      */
5197     /**
5198      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5199      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5200      */
5201     /**
5202      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5203      * Defaults to the value of the {@link #defaultSortable} property.
5204      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5205      */
5206     /**
5207      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5208      */
5209     /**
5210      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5211      */
5212     /**
5213      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5214      */
5215     /**
5216      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5217      */
5218     /**
5219      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5220      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5221      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5222      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5223      */
5224        /**
5225      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5226      */
5227     /**
5228      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5229      */
5230     /**
5231      * @cfg {String} cursor (Optional)
5232      */
5233     /**
5234      * @cfg {String} tooltip (Optional)
5235      */
5236     /**
5237      * @cfg {Number} xs (Optional)
5238      */
5239     /**
5240      * @cfg {Number} sm (Optional)
5241      */
5242     /**
5243      * @cfg {Number} md (Optional)
5244      */
5245     /**
5246      * @cfg {Number} lg (Optional)
5247      */
5248     /**
5249      * Returns the id of the column at the specified index.
5250      * @param {Number} index The column index
5251      * @return {String} the id
5252      */
5253     getColumnId : function(index){
5254         return this.config[index].id;
5255     },
5256
5257     /**
5258      * Returns the column for a specified id.
5259      * @param {String} id The column id
5260      * @return {Object} the column
5261      */
5262     getColumnById : function(id){
5263         return this.lookup[id];
5264     },
5265
5266     
5267     /**
5268      * Returns the column for a specified dataIndex.
5269      * @param {String} dataIndex The column dataIndex
5270      * @return {Object|Boolean} the column or false if not found
5271      */
5272     getColumnByDataIndex: function(dataIndex){
5273         var index = this.findColumnIndex(dataIndex);
5274         return index > -1 ? this.config[index] : false;
5275     },
5276     
5277     /**
5278      * Returns the index for a specified column id.
5279      * @param {String} id The column id
5280      * @return {Number} the index, or -1 if not found
5281      */
5282     getIndexById : function(id){
5283         for(var i = 0, len = this.config.length; i < len; i++){
5284             if(this.config[i].id == id){
5285                 return i;
5286             }
5287         }
5288         return -1;
5289     },
5290     
5291     /**
5292      * Returns the index for a specified column dataIndex.
5293      * @param {String} dataIndex The column dataIndex
5294      * @return {Number} the index, or -1 if not found
5295      */
5296     
5297     findColumnIndex : function(dataIndex){
5298         for(var i = 0, len = this.config.length; i < len; i++){
5299             if(this.config[i].dataIndex == dataIndex){
5300                 return i;
5301             }
5302         }
5303         return -1;
5304     },
5305     
5306     
5307     moveColumn : function(oldIndex, newIndex){
5308         var c = this.config[oldIndex];
5309         this.config.splice(oldIndex, 1);
5310         this.config.splice(newIndex, 0, c);
5311         this.dataMap = null;
5312         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5313     },
5314
5315     isLocked : function(colIndex){
5316         return this.config[colIndex].locked === true;
5317     },
5318
5319     setLocked : function(colIndex, value, suppressEvent){
5320         if(this.isLocked(colIndex) == value){
5321             return;
5322         }
5323         this.config[colIndex].locked = value;
5324         if(!suppressEvent){
5325             this.fireEvent("columnlockchange", this, colIndex, value);
5326         }
5327     },
5328
5329     getTotalLockedWidth : function(){
5330         var totalWidth = 0;
5331         for(var i = 0; i < this.config.length; i++){
5332             if(this.isLocked(i) && !this.isHidden(i)){
5333                 this.totalWidth += this.getColumnWidth(i);
5334             }
5335         }
5336         return totalWidth;
5337     },
5338
5339     getLockedCount : function(){
5340         for(var i = 0, len = this.config.length; i < len; i++){
5341             if(!this.isLocked(i)){
5342                 return i;
5343             }
5344         }
5345         
5346         return this.config.length;
5347     },
5348
5349     /**
5350      * Returns the number of columns.
5351      * @return {Number}
5352      */
5353     getColumnCount : function(visibleOnly){
5354         if(visibleOnly === true){
5355             var c = 0;
5356             for(var i = 0, len = this.config.length; i < len; i++){
5357                 if(!this.isHidden(i)){
5358                     c++;
5359                 }
5360             }
5361             return c;
5362         }
5363         return this.config.length;
5364     },
5365
5366     /**
5367      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5368      * @param {Function} fn
5369      * @param {Object} scope (optional)
5370      * @return {Array} result
5371      */
5372     getColumnsBy : function(fn, scope){
5373         var r = [];
5374         for(var i = 0, len = this.config.length; i < len; i++){
5375             var c = this.config[i];
5376             if(fn.call(scope||this, c, i) === true){
5377                 r[r.length] = c;
5378             }
5379         }
5380         return r;
5381     },
5382
5383     /**
5384      * Returns true if the specified column is sortable.
5385      * @param {Number} col The column index
5386      * @return {Boolean}
5387      */
5388     isSortable : function(col){
5389         if(typeof this.config[col].sortable == "undefined"){
5390             return this.defaultSortable;
5391         }
5392         return this.config[col].sortable;
5393     },
5394
5395     /**
5396      * Returns the rendering (formatting) function defined for the column.
5397      * @param {Number} col The column index.
5398      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5399      */
5400     getRenderer : function(col){
5401         if(!this.config[col].renderer){
5402             return Roo.grid.ColumnModel.defaultRenderer;
5403         }
5404         return this.config[col].renderer;
5405     },
5406
5407     /**
5408      * Sets the rendering (formatting) function for a column.
5409      * @param {Number} col The column index
5410      * @param {Function} fn The function to use to process the cell's raw data
5411      * to return HTML markup for the grid view. The render function is called with
5412      * the following parameters:<ul>
5413      * <li>Data value.</li>
5414      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5415      * <li>css A CSS style string to apply to the table cell.</li>
5416      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5417      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5418      * <li>Row index</li>
5419      * <li>Column index</li>
5420      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5421      */
5422     setRenderer : function(col, fn){
5423         this.config[col].renderer = fn;
5424     },
5425
5426     /**
5427      * Returns the width for the specified column.
5428      * @param {Number} col The column index
5429      * @return {Number}
5430      */
5431     getColumnWidth : function(col){
5432         return this.config[col].width * 1 || this.defaultWidth;
5433     },
5434
5435     /**
5436      * Sets the width for a column.
5437      * @param {Number} col The column index
5438      * @param {Number} width The new width
5439      */
5440     setColumnWidth : function(col, width, suppressEvent){
5441         this.config[col].width = width;
5442         this.totalWidth = null;
5443         if(!suppressEvent){
5444              this.fireEvent("widthchange", this, col, width);
5445         }
5446     },
5447
5448     /**
5449      * Returns the total width of all columns.
5450      * @param {Boolean} includeHidden True to include hidden column widths
5451      * @return {Number}
5452      */
5453     getTotalWidth : function(includeHidden){
5454         if(!this.totalWidth){
5455             this.totalWidth = 0;
5456             for(var i = 0, len = this.config.length; i < len; i++){
5457                 if(includeHidden || !this.isHidden(i)){
5458                     this.totalWidth += this.getColumnWidth(i);
5459                 }
5460             }
5461         }
5462         return this.totalWidth;
5463     },
5464
5465     /**
5466      * Returns the header for the specified column.
5467      * @param {Number} col The column index
5468      * @return {String}
5469      */
5470     getColumnHeader : function(col){
5471         return this.config[col].header;
5472     },
5473
5474     /**
5475      * Sets the header for a column.
5476      * @param {Number} col The column index
5477      * @param {String} header The new header
5478      */
5479     setColumnHeader : function(col, header){
5480         this.config[col].header = header;
5481         this.fireEvent("headerchange", this, col, header);
5482     },
5483
5484     /**
5485      * Returns the tooltip for the specified column.
5486      * @param {Number} col The column index
5487      * @return {String}
5488      */
5489     getColumnTooltip : function(col){
5490             return this.config[col].tooltip;
5491     },
5492     /**
5493      * Sets the tooltip for a column.
5494      * @param {Number} col The column index
5495      * @param {String} tooltip The new tooltip
5496      */
5497     setColumnTooltip : function(col, tooltip){
5498             this.config[col].tooltip = tooltip;
5499     },
5500
5501     /**
5502      * Returns the dataIndex for the specified column.
5503      * @param {Number} col The column index
5504      * @return {Number}
5505      */
5506     getDataIndex : function(col){
5507         return this.config[col].dataIndex;
5508     },
5509
5510     /**
5511      * Sets the dataIndex for a column.
5512      * @param {Number} col The column index
5513      * @param {Number} dataIndex The new dataIndex
5514      */
5515     setDataIndex : function(col, dataIndex){
5516         this.config[col].dataIndex = dataIndex;
5517     },
5518
5519     
5520     
5521     /**
5522      * Returns true if the cell is editable.
5523      * @param {Number} colIndex The column index
5524      * @param {Number} rowIndex The row index - this is nto actually used..?
5525      * @return {Boolean}
5526      */
5527     isCellEditable : function(colIndex, rowIndex){
5528         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5529     },
5530
5531     /**
5532      * Returns the editor defined for the cell/column.
5533      * return false or null to disable editing.
5534      * @param {Number} colIndex The column index
5535      * @param {Number} rowIndex The row index
5536      * @return {Object}
5537      */
5538     getCellEditor : function(colIndex, rowIndex){
5539         return this.config[colIndex].editor;
5540     },
5541
5542     /**
5543      * Sets if a column is editable.
5544      * @param {Number} col The column index
5545      * @param {Boolean} editable True if the column is editable
5546      */
5547     setEditable : function(col, editable){
5548         this.config[col].editable = editable;
5549     },
5550
5551
5552     /**
5553      * Returns true if the column is hidden.
5554      * @param {Number} colIndex The column index
5555      * @return {Boolean}
5556      */
5557     isHidden : function(colIndex){
5558         return this.config[colIndex].hidden;
5559     },
5560
5561
5562     /**
5563      * Returns true if the column width cannot be changed
5564      */
5565     isFixed : function(colIndex){
5566         return this.config[colIndex].fixed;
5567     },
5568
5569     /**
5570      * Returns true if the column can be resized
5571      * @return {Boolean}
5572      */
5573     isResizable : function(colIndex){
5574         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5575     },
5576     /**
5577      * Sets if a column is hidden.
5578      * @param {Number} colIndex The column index
5579      * @param {Boolean} hidden True if the column is hidden
5580      */
5581     setHidden : function(colIndex, hidden){
5582         this.config[colIndex].hidden = hidden;
5583         this.totalWidth = null;
5584         this.fireEvent("hiddenchange", this, colIndex, hidden);
5585     },
5586
5587     /**
5588      * Sets the editor for a column.
5589      * @param {Number} col The column index
5590      * @param {Object} editor The editor object
5591      */
5592     setEditor : function(col, editor){
5593         this.config[col].editor = editor;
5594     }
5595 });
5596
5597 Roo.grid.ColumnModel.defaultRenderer = function(value)
5598 {
5599     if(typeof value == "object") {
5600         return value;
5601     }
5602         if(typeof value == "string" && value.length < 1){
5603             return "&#160;";
5604         }
5605     
5606         return String.format("{0}", value);
5607 };
5608
5609 // Alias for backwards compatibility
5610 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5611 /*
5612  * Based on:
5613  * Ext JS Library 1.1.1
5614  * Copyright(c) 2006-2007, Ext JS, LLC.
5615  *
5616  * Originally Released Under LGPL - original licence link has changed is not relivant.
5617  *
5618  * Fork - LGPL
5619  * <script type="text/javascript">
5620  */
5621  
5622 /**
5623  * @class Roo.LoadMask
5624  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5625  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5626  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5627  * element's UpdateManager load indicator and will be destroyed after the initial load.
5628  * @constructor
5629  * Create a new LoadMask
5630  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5631  * @param {Object} config The config object
5632  */
5633 Roo.LoadMask = function(el, config){
5634     this.el = Roo.get(el);
5635     Roo.apply(this, config);
5636     if(this.store){
5637         this.store.on('beforeload', this.onBeforeLoad, this);
5638         this.store.on('load', this.onLoad, this);
5639         this.store.on('loadexception', this.onLoadException, this);
5640         this.removeMask = false;
5641     }else{
5642         var um = this.el.getUpdateManager();
5643         um.showLoadIndicator = false; // disable the default indicator
5644         um.on('beforeupdate', this.onBeforeLoad, this);
5645         um.on('update', this.onLoad, this);
5646         um.on('failure', this.onLoad, this);
5647         this.removeMask = true;
5648     }
5649 };
5650
5651 Roo.LoadMask.prototype = {
5652     /**
5653      * @cfg {Boolean} removeMask
5654      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5655      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5656      */
5657     /**
5658      * @cfg {String} msg
5659      * The text to display in a centered loading message box (defaults to 'Loading...')
5660      */
5661     msg : 'Loading...',
5662     /**
5663      * @cfg {String} msgCls
5664      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5665      */
5666     msgCls : 'x-mask-loading',
5667
5668     /**
5669      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5670      * @type Boolean
5671      */
5672     disabled: false,
5673
5674     /**
5675      * Disables the mask to prevent it from being displayed
5676      */
5677     disable : function(){
5678        this.disabled = true;
5679     },
5680
5681     /**
5682      * Enables the mask so that it can be displayed
5683      */
5684     enable : function(){
5685         this.disabled = false;
5686     },
5687     
5688     onLoadException : function()
5689     {
5690         Roo.log(arguments);
5691         
5692         if (typeof(arguments[3]) != 'undefined') {
5693             Roo.MessageBox.alert("Error loading",arguments[3]);
5694         } 
5695         /*
5696         try {
5697             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5698                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5699             }   
5700         } catch(e) {
5701             
5702         }
5703         */
5704     
5705         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5706     },
5707     // private
5708     onLoad : function()
5709     {
5710         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5711     },
5712
5713     // private
5714     onBeforeLoad : function(){
5715         if(!this.disabled){
5716             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5717         }
5718     },
5719
5720     // private
5721     destroy : function(){
5722         if(this.store){
5723             this.store.un('beforeload', this.onBeforeLoad, this);
5724             this.store.un('load', this.onLoad, this);
5725             this.store.un('loadexception', this.onLoadException, this);
5726         }else{
5727             var um = this.el.getUpdateManager();
5728             um.un('beforeupdate', this.onBeforeLoad, this);
5729             um.un('update', this.onLoad, this);
5730             um.un('failure', this.onLoad, this);
5731         }
5732     }
5733 };/*
5734  * - LGPL
5735  *
5736  * table
5737  * 
5738  */
5739
5740 /**
5741  * @class Roo.bootstrap.Table
5742  * @extends Roo.bootstrap.Component
5743  * Bootstrap Table class
5744  * @cfg {String} cls table class
5745  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5746  * @cfg {String} bgcolor Specifies the background color for a table
5747  * @cfg {Number} border Specifies whether the table cells should have borders or not
5748  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5749  * @cfg {Number} cellspacing Specifies the space between cells
5750  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5751  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5752  * @cfg {String} sortable Specifies that the table should be sortable
5753  * @cfg {String} summary Specifies a summary of the content of a table
5754  * @cfg {Number} width Specifies the width of a table
5755  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5756  * 
5757  * @cfg {boolean} striped Should the rows be alternative striped
5758  * @cfg {boolean} bordered Add borders to the table
5759  * @cfg {boolean} hover Add hover highlighting
5760  * @cfg {boolean} condensed Format condensed
5761  * @cfg {boolean} responsive Format condensed
5762  * @cfg {Boolean} loadMask (true|false) default false
5763  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5764  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5765  * @cfg {Boolean} rowSelection (true|false) default false
5766  * @cfg {Boolean} cellSelection (true|false) default false
5767  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5768  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5769  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
5770  
5771  * 
5772  * @constructor
5773  * Create a new Table
5774  * @param {Object} config The config object
5775  */
5776
5777 Roo.bootstrap.Table = function(config){
5778     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5779     
5780   
5781     
5782     // BC...
5783     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5784     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5785     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5786     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5787     
5788     this.sm = this.sm || {xtype: 'RowSelectionModel'};
5789     if (this.sm) {
5790         this.sm.grid = this;
5791         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5792         this.sm = this.selModel;
5793         this.sm.xmodule = this.xmodule || false;
5794     }
5795     
5796     if (this.cm && typeof(this.cm.config) == 'undefined') {
5797         this.colModel = new Roo.grid.ColumnModel(this.cm);
5798         this.cm = this.colModel;
5799         this.cm.xmodule = this.xmodule || false;
5800     }
5801     if (this.store) {
5802         this.store= Roo.factory(this.store, Roo.data);
5803         this.ds = this.store;
5804         this.ds.xmodule = this.xmodule || false;
5805          
5806     }
5807     if (this.footer && this.store) {
5808         this.footer.dataSource = this.ds;
5809         this.footer = Roo.factory(this.footer);
5810     }
5811     
5812     /** @private */
5813     this.addEvents({
5814         /**
5815          * @event cellclick
5816          * Fires when a cell is clicked
5817          * @param {Roo.bootstrap.Table} this
5818          * @param {Roo.Element} el
5819          * @param {Number} rowIndex
5820          * @param {Number} columnIndex
5821          * @param {Roo.EventObject} e
5822          */
5823         "cellclick" : true,
5824         /**
5825          * @event celldblclick
5826          * Fires when a cell is double clicked
5827          * @param {Roo.bootstrap.Table} this
5828          * @param {Roo.Element} el
5829          * @param {Number} rowIndex
5830          * @param {Number} columnIndex
5831          * @param {Roo.EventObject} e
5832          */
5833         "celldblclick" : true,
5834         /**
5835          * @event rowclick
5836          * Fires when a row is clicked
5837          * @param {Roo.bootstrap.Table} this
5838          * @param {Roo.Element} el
5839          * @param {Number} rowIndex
5840          * @param {Roo.EventObject} e
5841          */
5842         "rowclick" : true,
5843         /**
5844          * @event rowdblclick
5845          * Fires when a row is double clicked
5846          * @param {Roo.bootstrap.Table} this
5847          * @param {Roo.Element} el
5848          * @param {Number} rowIndex
5849          * @param {Roo.EventObject} e
5850          */
5851         "rowdblclick" : true,
5852         /**
5853          * @event mouseover
5854          * Fires when a mouseover occur
5855          * @param {Roo.bootstrap.Table} this
5856          * @param {Roo.Element} el
5857          * @param {Number} rowIndex
5858          * @param {Number} columnIndex
5859          * @param {Roo.EventObject} e
5860          */
5861         "mouseover" : true,
5862         /**
5863          * @event mouseout
5864          * Fires when a mouseout occur
5865          * @param {Roo.bootstrap.Table} this
5866          * @param {Roo.Element} el
5867          * @param {Number} rowIndex
5868          * @param {Number} columnIndex
5869          * @param {Roo.EventObject} e
5870          */
5871         "mouseout" : true,
5872         /**
5873          * @event rowclass
5874          * Fires when a row is rendered, so you can change add a style to it.
5875          * @param {Roo.bootstrap.Table} this
5876          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5877          */
5878         'rowclass' : true,
5879           /**
5880          * @event rowsrendered
5881          * Fires when all the  rows have been rendered
5882          * @param {Roo.bootstrap.Table} this
5883          */
5884         'rowsrendered' : true,
5885         /**
5886          * @event contextmenu
5887          * The raw contextmenu event for the entire grid.
5888          * @param {Roo.EventObject} e
5889          */
5890         "contextmenu" : true,
5891         /**
5892          * @event rowcontextmenu
5893          * Fires when a row is right clicked
5894          * @param {Roo.bootstrap.Table} this
5895          * @param {Number} rowIndex
5896          * @param {Roo.EventObject} e
5897          */
5898         "rowcontextmenu" : true,
5899         /**
5900          * @event cellcontextmenu
5901          * Fires when a cell is right clicked
5902          * @param {Roo.bootstrap.Table} this
5903          * @param {Number} rowIndex
5904          * @param {Number} cellIndex
5905          * @param {Roo.EventObject} e
5906          */
5907          "cellcontextmenu" : true,
5908          /**
5909          * @event headercontextmenu
5910          * Fires when a header is right clicked
5911          * @param {Roo.bootstrap.Table} this
5912          * @param {Number} columnIndex
5913          * @param {Roo.EventObject} e
5914          */
5915         "headercontextmenu" : true
5916     });
5917 };
5918
5919 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5920     
5921     cls: false,
5922     align: false,
5923     bgcolor: false,
5924     border: false,
5925     cellpadding: false,
5926     cellspacing: false,
5927     frame: false,
5928     rules: false,
5929     sortable: false,
5930     summary: false,
5931     width: false,
5932     striped : false,
5933     scrollBody : false,
5934     bordered: false,
5935     hover:  false,
5936     condensed : false,
5937     responsive : false,
5938     sm : false,
5939     cm : false,
5940     store : false,
5941     loadMask : false,
5942     footerShow : true,
5943     headerShow : true,
5944   
5945     rowSelection : false,
5946     cellSelection : false,
5947     layout : false,
5948     
5949     // Roo.Element - the tbody
5950     mainBody: false,
5951     // Roo.Element - thead element
5952     mainHead: false,
5953     
5954     container: false, // used by gridpanel...
5955     
5956     lazyLoad : false,
5957     
5958     getAutoCreate : function()
5959     {
5960         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5961         
5962         cfg = {
5963             tag: 'table',
5964             cls : 'table',
5965             cn : []
5966         };
5967         if (this.scrollBody) {
5968             cfg.cls += ' table-body-fixed';
5969         }    
5970         if (this.striped) {
5971             cfg.cls += ' table-striped';
5972         }
5973         
5974         if (this.hover) {
5975             cfg.cls += ' table-hover';
5976         }
5977         if (this.bordered) {
5978             cfg.cls += ' table-bordered';
5979         }
5980         if (this.condensed) {
5981             cfg.cls += ' table-condensed';
5982         }
5983         if (this.responsive) {
5984             cfg.cls += ' table-responsive';
5985         }
5986         
5987         if (this.cls) {
5988             cfg.cls+=  ' ' +this.cls;
5989         }
5990         
5991         // this lot should be simplifed...
5992         
5993         if (this.align) {
5994             cfg.align=this.align;
5995         }
5996         if (this.bgcolor) {
5997             cfg.bgcolor=this.bgcolor;
5998         }
5999         if (this.border) {
6000             cfg.border=this.border;
6001         }
6002         if (this.cellpadding) {
6003             cfg.cellpadding=this.cellpadding;
6004         }
6005         if (this.cellspacing) {
6006             cfg.cellspacing=this.cellspacing;
6007         }
6008         if (this.frame) {
6009             cfg.frame=this.frame;
6010         }
6011         if (this.rules) {
6012             cfg.rules=this.rules;
6013         }
6014         if (this.sortable) {
6015             cfg.sortable=this.sortable;
6016         }
6017         if (this.summary) {
6018             cfg.summary=this.summary;
6019         }
6020         if (this.width) {
6021             cfg.width=this.width;
6022         }
6023         if (this.layout) {
6024             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6025         }
6026         
6027         if(this.store || this.cm){
6028             if(this.headerShow){
6029                 cfg.cn.push(this.renderHeader());
6030             }
6031             
6032             cfg.cn.push(this.renderBody());
6033             
6034             if(this.footerShow){
6035                 cfg.cn.push(this.renderFooter());
6036             }
6037             // where does this come from?
6038             //cfg.cls+=  ' TableGrid';
6039         }
6040         
6041         return { cn : [ cfg ] };
6042     },
6043     
6044     initEvents : function()
6045     {   
6046         if(!this.store || !this.cm){
6047             return;
6048         }
6049         if (this.selModel) {
6050             this.selModel.initEvents();
6051         }
6052         
6053         
6054         //Roo.log('initEvents with ds!!!!');
6055         
6056         this.mainBody = this.el.select('tbody', true).first();
6057         this.mainHead = this.el.select('thead', true).first();
6058         
6059         
6060         
6061         
6062         var _this = this;
6063         
6064         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6065             e.on('click', _this.sort, _this);
6066         });
6067         
6068         this.mainBody.on("click", this.onClick, this);
6069         this.mainBody.on("dblclick", this.onDblClick, this);
6070         
6071         // why is this done????? = it breaks dialogs??
6072         //this.parent().el.setStyle('position', 'relative');
6073         
6074         
6075         if (this.footer) {
6076             this.footer.parentId = this.id;
6077             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6078             
6079             if(this.lazyLoad){
6080                 this.el.select('tfoot tr td').first().addClass('hide');
6081             }
6082         } 
6083         
6084         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6085         
6086         this.store.on('load', this.onLoad, this);
6087         this.store.on('beforeload', this.onBeforeLoad, this);
6088         this.store.on('update', this.onUpdate, this);
6089         this.store.on('add', this.onAdd, this);
6090         this.store.on("clear", this.clear, this);
6091         
6092         this.el.on("contextmenu", this.onContextMenu, this);
6093         
6094         this.mainBody.on('scroll', this.onBodyScroll, this);
6095         
6096         
6097     },
6098     
6099     onContextMenu : function(e, t)
6100     {
6101         this.processEvent("contextmenu", e);
6102     },
6103     
6104     processEvent : function(name, e)
6105     {
6106         if (name != 'touchstart' ) {
6107             this.fireEvent(name, e);    
6108         }
6109         
6110         var t = e.getTarget();
6111         
6112         var cell = Roo.get(t);
6113         
6114         if(!cell){
6115             return;
6116         }
6117         
6118         if(cell.findParent('tfoot', false, true)){
6119             return;
6120         }
6121         
6122         if(cell.findParent('thead', false, true)){
6123             
6124             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6125                 cell = Roo.get(t).findParent('th', false, true);
6126                 if (!cell) {
6127                     Roo.log("failed to find th in thead?");
6128                     Roo.log(e.getTarget());
6129                     return;
6130                 }
6131             }
6132             
6133             var cellIndex = cell.dom.cellIndex;
6134             
6135             var ename = name == 'touchstart' ? 'click' : name;
6136             this.fireEvent("header" + ename, this, cellIndex, e);
6137             
6138             return;
6139         }
6140         
6141         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6142             cell = Roo.get(t).findParent('td', false, true);
6143             if (!cell) {
6144                 Roo.log("failed to find th in tbody?");
6145                 Roo.log(e.getTarget());
6146                 return;
6147             }
6148         }
6149         
6150         var row = cell.findParent('tr', false, true);
6151         var cellIndex = cell.dom.cellIndex;
6152         var rowIndex = row.dom.rowIndex - 1;
6153         
6154         if(row !== false){
6155             
6156             this.fireEvent("row" + name, this, rowIndex, e);
6157             
6158             if(cell !== false){
6159             
6160                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6161             }
6162         }
6163         
6164     },
6165     
6166     onMouseover : function(e, el)
6167     {
6168         var cell = Roo.get(el);
6169         
6170         if(!cell){
6171             return;
6172         }
6173         
6174         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6175             cell = cell.findParent('td', false, true);
6176         }
6177         
6178         var row = cell.findParent('tr', false, true);
6179         var cellIndex = cell.dom.cellIndex;
6180         var rowIndex = row.dom.rowIndex - 1; // start from 0
6181         
6182         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6183         
6184     },
6185     
6186     onMouseout : function(e, el)
6187     {
6188         var cell = Roo.get(el);
6189         
6190         if(!cell){
6191             return;
6192         }
6193         
6194         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6195             cell = cell.findParent('td', false, true);
6196         }
6197         
6198         var row = cell.findParent('tr', false, true);
6199         var cellIndex = cell.dom.cellIndex;
6200         var rowIndex = row.dom.rowIndex - 1; // start from 0
6201         
6202         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6203         
6204     },
6205     
6206     onClick : function(e, el)
6207     {
6208         var cell = Roo.get(el);
6209         
6210         if(!cell || (!this.cellSelection && !this.rowSelection)){
6211             return;
6212         }
6213         
6214         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6215             cell = cell.findParent('td', false, true);
6216         }
6217         
6218         if(!cell || typeof(cell) == 'undefined'){
6219             return;
6220         }
6221         
6222         var row = cell.findParent('tr', false, true);
6223         
6224         if(!row || typeof(row) == 'undefined'){
6225             return;
6226         }
6227         
6228         var cellIndex = cell.dom.cellIndex;
6229         var rowIndex = this.getRowIndex(row);
6230         
6231         // why??? - should these not be based on SelectionModel?
6232         if(this.cellSelection){
6233             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6234         }
6235         
6236         if(this.rowSelection){
6237             this.fireEvent('rowclick', this, row, rowIndex, e);
6238         }
6239         
6240         
6241     },
6242         
6243     onDblClick : function(e,el)
6244     {
6245         var cell = Roo.get(el);
6246         
6247         if(!cell || (!this.cellSelection && !this.rowSelection)){
6248             return;
6249         }
6250         
6251         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6252             cell = cell.findParent('td', false, true);
6253         }
6254         
6255         if(!cell || typeof(cell) == 'undefined'){
6256             return;
6257         }
6258         
6259         var row = cell.findParent('tr', false, true);
6260         
6261         if(!row || typeof(row) == 'undefined'){
6262             return;
6263         }
6264         
6265         var cellIndex = cell.dom.cellIndex;
6266         var rowIndex = this.getRowIndex(row);
6267         
6268         if(this.cellSelection){
6269             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6270         }
6271         
6272         if(this.rowSelection){
6273             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6274         }
6275     },
6276     
6277     sort : function(e,el)
6278     {
6279         var col = Roo.get(el);
6280         
6281         if(!col.hasClass('sortable')){
6282             return;
6283         }
6284         
6285         var sort = col.attr('sort');
6286         var dir = 'ASC';
6287         
6288         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6289             dir = 'DESC';
6290         }
6291         
6292         this.store.sortInfo = {field : sort, direction : dir};
6293         
6294         if (this.footer) {
6295             Roo.log("calling footer first");
6296             this.footer.onClick('first');
6297         } else {
6298         
6299             this.store.load({ params : { start : 0 } });
6300         }
6301     },
6302     
6303     renderHeader : function()
6304     {
6305         var header = {
6306             tag: 'thead',
6307             cn : []
6308         };
6309         
6310         var cm = this.cm;
6311         this.totalWidth = 0;
6312         
6313         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6314             
6315             var config = cm.config[i];
6316             
6317             var c = {
6318                 tag: 'th',
6319                 style : '',
6320                 html: cm.getColumnHeader(i)
6321             };
6322             
6323             var hh = '';
6324             
6325             if(typeof(config.sortable) != 'undefined' && config.sortable){
6326                 c.cls = 'sortable';
6327                 c.html = '<i class="glyphicon"></i>' + c.html;
6328             }
6329             
6330             if(typeof(config.lgHeader) != 'undefined'){
6331                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6332             }
6333             
6334             if(typeof(config.mdHeader) != 'undefined'){
6335                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6336             }
6337             
6338             if(typeof(config.smHeader) != 'undefined'){
6339                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6340             }
6341             
6342             if(typeof(config.xsHeader) != 'undefined'){
6343                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6344             }
6345             
6346             if(hh.length){
6347                 c.html = hh;
6348             }
6349             
6350             if(typeof(config.tooltip) != 'undefined'){
6351                 c.tooltip = config.tooltip;
6352             }
6353             
6354             if(typeof(config.colspan) != 'undefined'){
6355                 c.colspan = config.colspan;
6356             }
6357             
6358             if(typeof(config.hidden) != 'undefined' && config.hidden){
6359                 c.style += ' display:none;';
6360             }
6361             
6362             if(typeof(config.dataIndex) != 'undefined'){
6363                 c.sort = config.dataIndex;
6364             }
6365             
6366            
6367             
6368             if(typeof(config.align) != 'undefined' && config.align.length){
6369                 c.style += ' text-align:' + config.align + ';';
6370             }
6371             
6372             if(typeof(config.width) != 'undefined'){
6373                 c.style += ' width:' + config.width + 'px;';
6374                 this.totalWidth += config.width;
6375             } else {
6376                 this.totalWidth += 100; // assume minimum of 100 per column?
6377             }
6378             
6379             if(typeof(config.cls) != 'undefined'){
6380                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6381             }
6382             
6383             ['xs','sm','md','lg'].map(function(size){
6384                 
6385                 if(typeof(config[size]) == 'undefined'){
6386                     return;
6387                 }
6388                 
6389                 if (!config[size]) { // 0 = hidden
6390                     c.cls += ' hidden-' + size;
6391                     return;
6392                 }
6393                 
6394                 c.cls += ' col-' + size + '-' + config[size];
6395
6396             });
6397             
6398             header.cn.push(c)
6399         }
6400         
6401         return header;
6402     },
6403     
6404     renderBody : function()
6405     {
6406         var body = {
6407             tag: 'tbody',
6408             cn : [
6409                 {
6410                     tag: 'tr',
6411                     cn : [
6412                         {
6413                             tag : 'td',
6414                             colspan :  this.cm.getColumnCount()
6415                         }
6416                     ]
6417                 }
6418             ]
6419         };
6420         
6421         return body;
6422     },
6423     
6424     renderFooter : function()
6425     {
6426         var footer = {
6427             tag: 'tfoot',
6428             cn : [
6429                 {
6430                     tag: 'tr',
6431                     cn : [
6432                         {
6433                             tag : 'td',
6434                             colspan :  this.cm.getColumnCount()
6435                         }
6436                     ]
6437                 }
6438             ]
6439         };
6440         
6441         return footer;
6442     },
6443     
6444     
6445     
6446     onLoad : function()
6447     {
6448 //        Roo.log('ds onload');
6449         this.clear();
6450         
6451         var _this = this;
6452         var cm = this.cm;
6453         var ds = this.store;
6454         
6455         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6456             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6457             if (_this.store.sortInfo) {
6458                     
6459                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6460                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6461                 }
6462                 
6463                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6464                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6465                 }
6466             }
6467         });
6468         
6469         var tbody =  this.mainBody;
6470               
6471         if(ds.getCount() > 0){
6472             ds.data.each(function(d,rowIndex){
6473                 var row =  this.renderRow(cm, ds, rowIndex);
6474                 
6475                 tbody.createChild(row);
6476                 
6477                 var _this = this;
6478                 
6479                 if(row.cellObjects.length){
6480                     Roo.each(row.cellObjects, function(r){
6481                         _this.renderCellObject(r);
6482                     })
6483                 }
6484                 
6485             }, this);
6486         }
6487         
6488         Roo.each(this.el.select('tbody td', true).elements, function(e){
6489             e.on('mouseover', _this.onMouseover, _this);
6490         });
6491         
6492         Roo.each(this.el.select('tbody td', true).elements, function(e){
6493             e.on('mouseout', _this.onMouseout, _this);
6494         });
6495         this.fireEvent('rowsrendered', this);
6496         //if(this.loadMask){
6497         //    this.maskEl.hide();
6498         //}
6499         
6500         this.autoSize();
6501     },
6502     
6503     
6504     onUpdate : function(ds,record)
6505     {
6506         this.refreshRow(record);
6507         this.autoSize();
6508     },
6509     
6510     onRemove : function(ds, record, index, isUpdate){
6511         if(isUpdate !== true){
6512             this.fireEvent("beforerowremoved", this, index, record);
6513         }
6514         var bt = this.mainBody.dom;
6515         
6516         var rows = this.el.select('tbody > tr', true).elements;
6517         
6518         if(typeof(rows[index]) != 'undefined'){
6519             bt.removeChild(rows[index].dom);
6520         }
6521         
6522 //        if(bt.rows[index]){
6523 //            bt.removeChild(bt.rows[index]);
6524 //        }
6525         
6526         if(isUpdate !== true){
6527             //this.stripeRows(index);
6528             //this.syncRowHeights(index, index);
6529             //this.layout();
6530             this.fireEvent("rowremoved", this, index, record);
6531         }
6532     },
6533     
6534     onAdd : function(ds, records, rowIndex)
6535     {
6536         //Roo.log('on Add called');
6537         // - note this does not handle multiple adding very well..
6538         var bt = this.mainBody.dom;
6539         for (var i =0 ; i < records.length;i++) {
6540             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6541             //Roo.log(records[i]);
6542             //Roo.log(this.store.getAt(rowIndex+i));
6543             this.insertRow(this.store, rowIndex + i, false);
6544             return;
6545         }
6546         
6547     },
6548     
6549     
6550     refreshRow : function(record){
6551         var ds = this.store, index;
6552         if(typeof record == 'number'){
6553             index = record;
6554             record = ds.getAt(index);
6555         }else{
6556             index = ds.indexOf(record);
6557         }
6558         this.insertRow(ds, index, true);
6559         this.autoSize();
6560         this.onRemove(ds, record, index+1, true);
6561         this.autoSize();
6562         //this.syncRowHeights(index, index);
6563         //this.layout();
6564         this.fireEvent("rowupdated", this, index, record);
6565     },
6566     
6567     insertRow : function(dm, rowIndex, isUpdate){
6568         
6569         if(!isUpdate){
6570             this.fireEvent("beforerowsinserted", this, rowIndex);
6571         }
6572             //var s = this.getScrollState();
6573         var row = this.renderRow(this.cm, this.store, rowIndex);
6574         // insert before rowIndex..
6575         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6576         
6577         var _this = this;
6578                 
6579         if(row.cellObjects.length){
6580             Roo.each(row.cellObjects, function(r){
6581                 _this.renderCellObject(r);
6582             })
6583         }
6584             
6585         if(!isUpdate){
6586             this.fireEvent("rowsinserted", this, rowIndex);
6587             //this.syncRowHeights(firstRow, lastRow);
6588             //this.stripeRows(firstRow);
6589             //this.layout();
6590         }
6591         
6592     },
6593     
6594     
6595     getRowDom : function(rowIndex)
6596     {
6597         var rows = this.el.select('tbody > tr', true).elements;
6598         
6599         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6600         
6601     },
6602     // returns the object tree for a tr..
6603   
6604     
6605     renderRow : function(cm, ds, rowIndex) 
6606     {
6607         
6608         var d = ds.getAt(rowIndex);
6609         
6610         var row = {
6611             tag : 'tr',
6612             cn : []
6613         };
6614             
6615         var cellObjects = [];
6616         
6617         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6618             var config = cm.config[i];
6619             
6620             var renderer = cm.getRenderer(i);
6621             var value = '';
6622             var id = false;
6623             
6624             if(typeof(renderer) !== 'undefined'){
6625                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6626             }
6627             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6628             // and are rendered into the cells after the row is rendered - using the id for the element.
6629             
6630             if(typeof(value) === 'object'){
6631                 id = Roo.id();
6632                 cellObjects.push({
6633                     container : id,
6634                     cfg : value 
6635                 })
6636             }
6637             
6638             var rowcfg = {
6639                 record: d,
6640                 rowIndex : rowIndex,
6641                 colIndex : i,
6642                 rowClass : ''
6643             };
6644
6645             this.fireEvent('rowclass', this, rowcfg);
6646             
6647             var td = {
6648                 tag: 'td',
6649                 cls : rowcfg.rowClass,
6650                 style: '',
6651                 html: (typeof(value) === 'object') ? '' : value
6652             };
6653             
6654             if (id) {
6655                 td.id = id;
6656             }
6657             
6658             if(typeof(config.colspan) != 'undefined'){
6659                 td.colspan = config.colspan;
6660             }
6661             
6662             if(typeof(config.hidden) != 'undefined' && config.hidden){
6663                 td.style += ' display:none;';
6664             }
6665             
6666             if(typeof(config.align) != 'undefined' && config.align.length){
6667                 td.style += ' text-align:' + config.align + ';';
6668             }
6669             
6670             if(typeof(config.width) != 'undefined'){
6671                 td.style += ' width:' +  config.width + 'px;';
6672             }
6673             
6674             if(typeof(config.cursor) != 'undefined'){
6675                 td.style += ' cursor:' +  config.cursor + ';';
6676             }
6677             
6678             if(typeof(config.cls) != 'undefined'){
6679                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6680             }
6681             
6682             ['xs','sm','md','lg'].map(function(size){
6683                 
6684                 if(typeof(config[size]) == 'undefined'){
6685                     return;
6686                 }
6687                 
6688                 if (!config[size]) { // 0 = hidden
6689                     td.cls += ' hidden-' + size;
6690                     return;
6691                 }
6692                 
6693                 td.cls += ' col-' + size + '-' + config[size];
6694
6695             });
6696              
6697             row.cn.push(td);
6698            
6699         }
6700         
6701         row.cellObjects = cellObjects;
6702         
6703         return row;
6704           
6705     },
6706     
6707     
6708     
6709     onBeforeLoad : function()
6710     {
6711         //Roo.log('ds onBeforeLoad');
6712         
6713         //this.clear();
6714         
6715         //if(this.loadMask){
6716         //    this.maskEl.show();
6717         //}
6718     },
6719      /**
6720      * Remove all rows
6721      */
6722     clear : function()
6723     {
6724         this.el.select('tbody', true).first().dom.innerHTML = '';
6725     },
6726     /**
6727      * Show or hide a row.
6728      * @param {Number} rowIndex to show or hide
6729      * @param {Boolean} state hide
6730      */
6731     setRowVisibility : function(rowIndex, state)
6732     {
6733         var bt = this.mainBody.dom;
6734         
6735         var rows = this.el.select('tbody > tr', true).elements;
6736         
6737         if(typeof(rows[rowIndex]) == 'undefined'){
6738             return;
6739         }
6740         rows[rowIndex].dom.style.display = state ? '' : 'none';
6741     },
6742     
6743     
6744     getSelectionModel : function(){
6745         if(!this.selModel){
6746             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6747         }
6748         return this.selModel;
6749     },
6750     /*
6751      * Render the Roo.bootstrap object from renderder
6752      */
6753     renderCellObject : function(r)
6754     {
6755         var _this = this;
6756         
6757         var t = r.cfg.render(r.container);
6758         
6759         if(r.cfg.cn){
6760             Roo.each(r.cfg.cn, function(c){
6761                 var child = {
6762                     container: t.getChildContainer(),
6763                     cfg: c
6764                 };
6765                 _this.renderCellObject(child);
6766             })
6767         }
6768     },
6769     
6770     getRowIndex : function(row)
6771     {
6772         var rowIndex = -1;
6773         
6774         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6775             if(el != row){
6776                 return;
6777             }
6778             
6779             rowIndex = index;
6780         });
6781         
6782         return rowIndex;
6783     },
6784      /**
6785      * Returns the grid's underlying element = used by panel.Grid
6786      * @return {Element} The element
6787      */
6788     getGridEl : function(){
6789         return this.el;
6790     },
6791      /**
6792      * Forces a resize - used by panel.Grid
6793      * @return {Element} The element
6794      */
6795     autoSize : function()
6796     {
6797         //var ctr = Roo.get(this.container.dom.parentElement);
6798         var ctr = Roo.get(this.el.dom);
6799         
6800         var thd = this.getGridEl().select('thead',true).first();
6801         var tbd = this.getGridEl().select('tbody', true).first();
6802         var tfd = this.getGridEl().select('tfoot', true).first();
6803         
6804         var cw = ctr.getWidth();
6805         
6806         if (tbd) {
6807             
6808             tbd.setSize(ctr.getWidth(),
6809                         ctr.getHeight() - (thd.getHeight() + (tfd ? tfd.getHeight() : 0))
6810             );
6811             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6812             cw -= barsize;
6813         }
6814         cw = Math.max(cw, this.totalWidth);
6815         this.getGridEl().select('tr',true).setWidth(cw);
6816         // resize 'expandable coloumn?
6817         
6818         return; // we doe not have a view in this design..
6819         
6820     },
6821     onBodyScroll: function()
6822     {
6823         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6824         this.mainHead.setStyle({
6825             'position' : 'relative',
6826             'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6827         });
6828         
6829         if(this.lazyLoad){
6830             
6831             var scrollHeight = this.mainBody.dom.scrollHeight;
6832             
6833             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
6834             
6835             var height = this.mainBody.getHeight();
6836             
6837             if(scrollHeight - height == scrollTop) {
6838                 
6839                 var total = this.ds.getTotalCount();
6840                 
6841                 if(this.footer.cursor + this.footer.pageSize < total){
6842                     
6843                     this.footer.ds.load({
6844                         params : {
6845                             start : this.footer.cursor + this.footer.pageSize,
6846                             limit : this.footer.pageSize
6847                         },
6848                         add : true
6849                     });
6850                 }
6851             }
6852             
6853         }
6854     }
6855 });
6856
6857  
6858
6859  /*
6860  * - LGPL
6861  *
6862  * table cell
6863  * 
6864  */
6865
6866 /**
6867  * @class Roo.bootstrap.TableCell
6868  * @extends Roo.bootstrap.Component
6869  * Bootstrap TableCell class
6870  * @cfg {String} html cell contain text
6871  * @cfg {String} cls cell class
6872  * @cfg {String} tag cell tag (td|th) default td
6873  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6874  * @cfg {String} align Aligns the content in a cell
6875  * @cfg {String} axis Categorizes cells
6876  * @cfg {String} bgcolor Specifies the background color of a cell
6877  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6878  * @cfg {Number} colspan Specifies the number of columns a cell should span
6879  * @cfg {String} headers Specifies one or more header cells a cell is related to
6880  * @cfg {Number} height Sets the height of a cell
6881  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6882  * @cfg {Number} rowspan Sets the number of rows a cell should span
6883  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6884  * @cfg {String} valign Vertical aligns the content in a cell
6885  * @cfg {Number} width Specifies the width of a cell
6886  * 
6887  * @constructor
6888  * Create a new TableCell
6889  * @param {Object} config The config object
6890  */
6891
6892 Roo.bootstrap.TableCell = function(config){
6893     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6894 };
6895
6896 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6897     
6898     html: false,
6899     cls: false,
6900     tag: false,
6901     abbr: false,
6902     align: false,
6903     axis: false,
6904     bgcolor: false,
6905     charoff: false,
6906     colspan: false,
6907     headers: false,
6908     height: false,
6909     nowrap: false,
6910     rowspan: false,
6911     scope: false,
6912     valign: false,
6913     width: false,
6914     
6915     
6916     getAutoCreate : function(){
6917         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6918         
6919         cfg = {
6920             tag: 'td'
6921         };
6922         
6923         if(this.tag){
6924             cfg.tag = this.tag;
6925         }
6926         
6927         if (this.html) {
6928             cfg.html=this.html
6929         }
6930         if (this.cls) {
6931             cfg.cls=this.cls
6932         }
6933         if (this.abbr) {
6934             cfg.abbr=this.abbr
6935         }
6936         if (this.align) {
6937             cfg.align=this.align
6938         }
6939         if (this.axis) {
6940             cfg.axis=this.axis
6941         }
6942         if (this.bgcolor) {
6943             cfg.bgcolor=this.bgcolor
6944         }
6945         if (this.charoff) {
6946             cfg.charoff=this.charoff
6947         }
6948         if (this.colspan) {
6949             cfg.colspan=this.colspan
6950         }
6951         if (this.headers) {
6952             cfg.headers=this.headers
6953         }
6954         if (this.height) {
6955             cfg.height=this.height
6956         }
6957         if (this.nowrap) {
6958             cfg.nowrap=this.nowrap
6959         }
6960         if (this.rowspan) {
6961             cfg.rowspan=this.rowspan
6962         }
6963         if (this.scope) {
6964             cfg.scope=this.scope
6965         }
6966         if (this.valign) {
6967             cfg.valign=this.valign
6968         }
6969         if (this.width) {
6970             cfg.width=this.width
6971         }
6972         
6973         
6974         return cfg;
6975     }
6976    
6977 });
6978
6979  
6980
6981  /*
6982  * - LGPL
6983  *
6984  * table row
6985  * 
6986  */
6987
6988 /**
6989  * @class Roo.bootstrap.TableRow
6990  * @extends Roo.bootstrap.Component
6991  * Bootstrap TableRow class
6992  * @cfg {String} cls row class
6993  * @cfg {String} align Aligns the content in a table row
6994  * @cfg {String} bgcolor Specifies a background color for a table row
6995  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6996  * @cfg {String} valign Vertical aligns the content in a table row
6997  * 
6998  * @constructor
6999  * Create a new TableRow
7000  * @param {Object} config The config object
7001  */
7002
7003 Roo.bootstrap.TableRow = function(config){
7004     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7005 };
7006
7007 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7008     
7009     cls: false,
7010     align: false,
7011     bgcolor: false,
7012     charoff: false,
7013     valign: false,
7014     
7015     getAutoCreate : function(){
7016         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7017         
7018         cfg = {
7019             tag: 'tr'
7020         };
7021             
7022         if(this.cls){
7023             cfg.cls = this.cls;
7024         }
7025         if(this.align){
7026             cfg.align = this.align;
7027         }
7028         if(this.bgcolor){
7029             cfg.bgcolor = this.bgcolor;
7030         }
7031         if(this.charoff){
7032             cfg.charoff = this.charoff;
7033         }
7034         if(this.valign){
7035             cfg.valign = this.valign;
7036         }
7037         
7038         return cfg;
7039     }
7040    
7041 });
7042
7043  
7044
7045  /*
7046  * - LGPL
7047  *
7048  * table body
7049  * 
7050  */
7051
7052 /**
7053  * @class Roo.bootstrap.TableBody
7054  * @extends Roo.bootstrap.Component
7055  * Bootstrap TableBody class
7056  * @cfg {String} cls element class
7057  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7058  * @cfg {String} align Aligns the content inside the element
7059  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7060  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7061  * 
7062  * @constructor
7063  * Create a new TableBody
7064  * @param {Object} config The config object
7065  */
7066
7067 Roo.bootstrap.TableBody = function(config){
7068     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7069 };
7070
7071 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7072     
7073     cls: false,
7074     tag: false,
7075     align: false,
7076     charoff: false,
7077     valign: false,
7078     
7079     getAutoCreate : function(){
7080         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7081         
7082         cfg = {
7083             tag: 'tbody'
7084         };
7085             
7086         if (this.cls) {
7087             cfg.cls=this.cls
7088         }
7089         if(this.tag){
7090             cfg.tag = this.tag;
7091         }
7092         
7093         if(this.align){
7094             cfg.align = this.align;
7095         }
7096         if(this.charoff){
7097             cfg.charoff = this.charoff;
7098         }
7099         if(this.valign){
7100             cfg.valign = this.valign;
7101         }
7102         
7103         return cfg;
7104     }
7105     
7106     
7107 //    initEvents : function()
7108 //    {
7109 //        
7110 //        if(!this.store){
7111 //            return;
7112 //        }
7113 //        
7114 //        this.store = Roo.factory(this.store, Roo.data);
7115 //        this.store.on('load', this.onLoad, this);
7116 //        
7117 //        this.store.load();
7118 //        
7119 //    },
7120 //    
7121 //    onLoad: function () 
7122 //    {   
7123 //        this.fireEvent('load', this);
7124 //    }
7125 //    
7126 //   
7127 });
7128
7129  
7130
7131  /*
7132  * Based on:
7133  * Ext JS Library 1.1.1
7134  * Copyright(c) 2006-2007, Ext JS, LLC.
7135  *
7136  * Originally Released Under LGPL - original licence link has changed is not relivant.
7137  *
7138  * Fork - LGPL
7139  * <script type="text/javascript">
7140  */
7141
7142 // as we use this in bootstrap.
7143 Roo.namespace('Roo.form');
7144  /**
7145  * @class Roo.form.Action
7146  * Internal Class used to handle form actions
7147  * @constructor
7148  * @param {Roo.form.BasicForm} el The form element or its id
7149  * @param {Object} config Configuration options
7150  */
7151
7152  
7153  
7154 // define the action interface
7155 Roo.form.Action = function(form, options){
7156     this.form = form;
7157     this.options = options || {};
7158 };
7159 /**
7160  * Client Validation Failed
7161  * @const 
7162  */
7163 Roo.form.Action.CLIENT_INVALID = 'client';
7164 /**
7165  * Server Validation Failed
7166  * @const 
7167  */
7168 Roo.form.Action.SERVER_INVALID = 'server';
7169  /**
7170  * Connect to Server Failed
7171  * @const 
7172  */
7173 Roo.form.Action.CONNECT_FAILURE = 'connect';
7174 /**
7175  * Reading Data from Server Failed
7176  * @const 
7177  */
7178 Roo.form.Action.LOAD_FAILURE = 'load';
7179
7180 Roo.form.Action.prototype = {
7181     type : 'default',
7182     failureType : undefined,
7183     response : undefined,
7184     result : undefined,
7185
7186     // interface method
7187     run : function(options){
7188
7189     },
7190
7191     // interface method
7192     success : function(response){
7193
7194     },
7195
7196     // interface method
7197     handleResponse : function(response){
7198
7199     },
7200
7201     // default connection failure
7202     failure : function(response){
7203         
7204         this.response = response;
7205         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7206         this.form.afterAction(this, false);
7207     },
7208
7209     processResponse : function(response){
7210         this.response = response;
7211         if(!response.responseText){
7212             return true;
7213         }
7214         this.result = this.handleResponse(response);
7215         return this.result;
7216     },
7217
7218     // utility functions used internally
7219     getUrl : function(appendParams){
7220         var url = this.options.url || this.form.url || this.form.el.dom.action;
7221         if(appendParams){
7222             var p = this.getParams();
7223             if(p){
7224                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7225             }
7226         }
7227         return url;
7228     },
7229
7230     getMethod : function(){
7231         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7232     },
7233
7234     getParams : function(){
7235         var bp = this.form.baseParams;
7236         var p = this.options.params;
7237         if(p){
7238             if(typeof p == "object"){
7239                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7240             }else if(typeof p == 'string' && bp){
7241                 p += '&' + Roo.urlEncode(bp);
7242             }
7243         }else if(bp){
7244             p = Roo.urlEncode(bp);
7245         }
7246         return p;
7247     },
7248
7249     createCallback : function(){
7250         return {
7251             success: this.success,
7252             failure: this.failure,
7253             scope: this,
7254             timeout: (this.form.timeout*1000),
7255             upload: this.form.fileUpload ? this.success : undefined
7256         };
7257     }
7258 };
7259
7260 Roo.form.Action.Submit = function(form, options){
7261     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7262 };
7263
7264 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7265     type : 'submit',
7266
7267     haveProgress : false,
7268     uploadComplete : false,
7269     
7270     // uploadProgress indicator.
7271     uploadProgress : function()
7272     {
7273         if (!this.form.progressUrl) {
7274             return;
7275         }
7276         
7277         if (!this.haveProgress) {
7278             Roo.MessageBox.progress("Uploading", "Uploading");
7279         }
7280         if (this.uploadComplete) {
7281            Roo.MessageBox.hide();
7282            return;
7283         }
7284         
7285         this.haveProgress = true;
7286    
7287         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7288         
7289         var c = new Roo.data.Connection();
7290         c.request({
7291             url : this.form.progressUrl,
7292             params: {
7293                 id : uid
7294             },
7295             method: 'GET',
7296             success : function(req){
7297                //console.log(data);
7298                 var rdata = false;
7299                 var edata;
7300                 try  {
7301                    rdata = Roo.decode(req.responseText)
7302                 } catch (e) {
7303                     Roo.log("Invalid data from server..");
7304                     Roo.log(edata);
7305                     return;
7306                 }
7307                 if (!rdata || !rdata.success) {
7308                     Roo.log(rdata);
7309                     Roo.MessageBox.alert(Roo.encode(rdata));
7310                     return;
7311                 }
7312                 var data = rdata.data;
7313                 
7314                 if (this.uploadComplete) {
7315                    Roo.MessageBox.hide();
7316                    return;
7317                 }
7318                    
7319                 if (data){
7320                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7321                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7322                     );
7323                 }
7324                 this.uploadProgress.defer(2000,this);
7325             },
7326        
7327             failure: function(data) {
7328                 Roo.log('progress url failed ');
7329                 Roo.log(data);
7330             },
7331             scope : this
7332         });
7333            
7334     },
7335     
7336     
7337     run : function()
7338     {
7339         // run get Values on the form, so it syncs any secondary forms.
7340         this.form.getValues();
7341         
7342         var o = this.options;
7343         var method = this.getMethod();
7344         var isPost = method == 'POST';
7345         if(o.clientValidation === false || this.form.isValid()){
7346             
7347             if (this.form.progressUrl) {
7348                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7349                     (new Date() * 1) + '' + Math.random());
7350                     
7351             } 
7352             
7353             
7354             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7355                 form:this.form.el.dom,
7356                 url:this.getUrl(!isPost),
7357                 method: method,
7358                 params:isPost ? this.getParams() : null,
7359                 isUpload: this.form.fileUpload
7360             }));
7361             
7362             this.uploadProgress();
7363
7364         }else if (o.clientValidation !== false){ // client validation failed
7365             this.failureType = Roo.form.Action.CLIENT_INVALID;
7366             this.form.afterAction(this, false);
7367         }
7368     },
7369
7370     success : function(response)
7371     {
7372         this.uploadComplete= true;
7373         if (this.haveProgress) {
7374             Roo.MessageBox.hide();
7375         }
7376         
7377         
7378         var result = this.processResponse(response);
7379         if(result === true || result.success){
7380             this.form.afterAction(this, true);
7381             return;
7382         }
7383         if(result.errors){
7384             this.form.markInvalid(result.errors);
7385             this.failureType = Roo.form.Action.SERVER_INVALID;
7386         }
7387         this.form.afterAction(this, false);
7388     },
7389     failure : function(response)
7390     {
7391         this.uploadComplete= true;
7392         if (this.haveProgress) {
7393             Roo.MessageBox.hide();
7394         }
7395         
7396         this.response = response;
7397         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7398         this.form.afterAction(this, false);
7399     },
7400     
7401     handleResponse : function(response){
7402         if(this.form.errorReader){
7403             var rs = this.form.errorReader.read(response);
7404             var errors = [];
7405             if(rs.records){
7406                 for(var i = 0, len = rs.records.length; i < len; i++) {
7407                     var r = rs.records[i];
7408                     errors[i] = r.data;
7409                 }
7410             }
7411             if(errors.length < 1){
7412                 errors = null;
7413             }
7414             return {
7415                 success : rs.success,
7416                 errors : errors
7417             };
7418         }
7419         var ret = false;
7420         try {
7421             ret = Roo.decode(response.responseText);
7422         } catch (e) {
7423             ret = {
7424                 success: false,
7425                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7426                 errors : []
7427             };
7428         }
7429         return ret;
7430         
7431     }
7432 });
7433
7434
7435 Roo.form.Action.Load = function(form, options){
7436     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7437     this.reader = this.form.reader;
7438 };
7439
7440 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7441     type : 'load',
7442
7443     run : function(){
7444         
7445         Roo.Ajax.request(Roo.apply(
7446                 this.createCallback(), {
7447                     method:this.getMethod(),
7448                     url:this.getUrl(false),
7449                     params:this.getParams()
7450         }));
7451     },
7452
7453     success : function(response){
7454         
7455         var result = this.processResponse(response);
7456         if(result === true || !result.success || !result.data){
7457             this.failureType = Roo.form.Action.LOAD_FAILURE;
7458             this.form.afterAction(this, false);
7459             return;
7460         }
7461         this.form.clearInvalid();
7462         this.form.setValues(result.data);
7463         this.form.afterAction(this, true);
7464     },
7465
7466     handleResponse : function(response){
7467         if(this.form.reader){
7468             var rs = this.form.reader.read(response);
7469             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7470             return {
7471                 success : rs.success,
7472                 data : data
7473             };
7474         }
7475         return Roo.decode(response.responseText);
7476     }
7477 });
7478
7479 Roo.form.Action.ACTION_TYPES = {
7480     'load' : Roo.form.Action.Load,
7481     'submit' : Roo.form.Action.Submit
7482 };/*
7483  * - LGPL
7484  *
7485  * form
7486  *
7487  */
7488
7489 /**
7490  * @class Roo.bootstrap.Form
7491  * @extends Roo.bootstrap.Component
7492  * Bootstrap Form class
7493  * @cfg {String} method  GET | POST (default POST)
7494  * @cfg {String} labelAlign top | left (default top)
7495  * @cfg {String} align left  | right - for navbars
7496  * @cfg {Boolean} loadMask load mask when submit (default true)
7497
7498  *
7499  * @constructor
7500  * Create a new Form
7501  * @param {Object} config The config object
7502  */
7503
7504
7505 Roo.bootstrap.Form = function(config){
7506     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7507     
7508     Roo.bootstrap.Form.popover.apply();
7509     
7510     this.addEvents({
7511         /**
7512          * @event clientvalidation
7513          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7514          * @param {Form} this
7515          * @param {Boolean} valid true if the form has passed client-side validation
7516          */
7517         clientvalidation: true,
7518         /**
7519          * @event beforeaction
7520          * Fires before any action is performed. Return false to cancel the action.
7521          * @param {Form} this
7522          * @param {Action} action The action to be performed
7523          */
7524         beforeaction: true,
7525         /**
7526          * @event actionfailed
7527          * Fires when an action fails.
7528          * @param {Form} this
7529          * @param {Action} action The action that failed
7530          */
7531         actionfailed : true,
7532         /**
7533          * @event actioncomplete
7534          * Fires when an action is completed.
7535          * @param {Form} this
7536          * @param {Action} action The action that completed
7537          */
7538         actioncomplete : true
7539     });
7540
7541 };
7542
7543 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7544
7545      /**
7546      * @cfg {String} method
7547      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7548      */
7549     method : 'POST',
7550     /**
7551      * @cfg {String} url
7552      * The URL to use for form actions if one isn't supplied in the action options.
7553      */
7554     /**
7555      * @cfg {Boolean} fileUpload
7556      * Set to true if this form is a file upload.
7557      */
7558
7559     /**
7560      * @cfg {Object} baseParams
7561      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7562      */
7563
7564     /**
7565      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7566      */
7567     timeout: 30,
7568     /**
7569      * @cfg {Sting} align (left|right) for navbar forms
7570      */
7571     align : 'left',
7572
7573     // private
7574     activeAction : null,
7575
7576     /**
7577      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7578      * element by passing it or its id or mask the form itself by passing in true.
7579      * @type Mixed
7580      */
7581     waitMsgTarget : false,
7582
7583     loadMask : true,
7584     
7585     /**
7586      * @cfg {Boolean} errorMask (true|false) default false
7587      */
7588     errorMask : false,
7589     
7590     /**
7591      * @cfg {Number} maskOffset Default 100
7592      */
7593     maskOffset : 100,
7594
7595     getAutoCreate : function(){
7596
7597         var cfg = {
7598             tag: 'form',
7599             method : this.method || 'POST',
7600             id : this.id || Roo.id(),
7601             cls : ''
7602         };
7603         if (this.parent().xtype.match(/^Nav/)) {
7604             cfg.cls = 'navbar-form navbar-' + this.align;
7605
7606         }
7607
7608         if (this.labelAlign == 'left' ) {
7609             cfg.cls += ' form-horizontal';
7610         }
7611
7612
7613         return cfg;
7614     },
7615     initEvents : function()
7616     {
7617         this.el.on('submit', this.onSubmit, this);
7618         // this was added as random key presses on the form where triggering form submit.
7619         this.el.on('keypress', function(e) {
7620             if (e.getCharCode() != 13) {
7621                 return true;
7622             }
7623             // we might need to allow it for textareas.. and some other items.
7624             // check e.getTarget().
7625
7626             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7627                 return true;
7628             }
7629
7630             Roo.log("keypress blocked");
7631
7632             e.preventDefault();
7633             return false;
7634         });
7635         
7636     },
7637     // private
7638     onSubmit : function(e){
7639         e.stopEvent();
7640     },
7641
7642      /**
7643      * Returns true if client-side validation on the form is successful.
7644      * @return Boolean
7645      */
7646     isValid : function(){
7647         var items = this.getItems();
7648         var valid = true;
7649         var target = false;
7650         
7651         items.each(function(f){
7652             
7653             if(f.validate()){
7654                 return;
7655             }
7656             valid = false;
7657
7658             if(!target && f.el.isVisible(true)){
7659                 target = f;
7660             }
7661            
7662         });
7663         
7664         if(this.errorMask && !valid){
7665             Roo.bootstrap.Form.popover.mask(this, target);
7666         }
7667         
7668         return valid;
7669     },
7670     
7671     /**
7672      * Returns true if any fields in this form have changed since their original load.
7673      * @return Boolean
7674      */
7675     isDirty : function(){
7676         var dirty = false;
7677         var items = this.getItems();
7678         items.each(function(f){
7679            if(f.isDirty()){
7680                dirty = true;
7681                return false;
7682            }
7683            return true;
7684         });
7685         return dirty;
7686     },
7687      /**
7688      * Performs a predefined action (submit or load) or custom actions you define on this form.
7689      * @param {String} actionName The name of the action type
7690      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7691      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7692      * accept other config options):
7693      * <pre>
7694 Property          Type             Description
7695 ----------------  ---------------  ----------------------------------------------------------------------------------
7696 url               String           The url for the action (defaults to the form's url)
7697 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7698 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7699 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7700                                    validate the form on the client (defaults to false)
7701      * </pre>
7702      * @return {BasicForm} this
7703      */
7704     doAction : function(action, options){
7705         if(typeof action == 'string'){
7706             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7707         }
7708         if(this.fireEvent('beforeaction', this, action) !== false){
7709             this.beforeAction(action);
7710             action.run.defer(100, action);
7711         }
7712         return this;
7713     },
7714
7715     // private
7716     beforeAction : function(action){
7717         var o = action.options;
7718
7719         if(this.loadMask){
7720             this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7721         }
7722         // not really supported yet.. ??
7723
7724         //if(this.waitMsgTarget === true){
7725         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7726         //}else if(this.waitMsgTarget){
7727         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7728         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7729         //}else {
7730         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7731        // }
7732
7733     },
7734
7735     // private
7736     afterAction : function(action, success){
7737         this.activeAction = null;
7738         var o = action.options;
7739
7740         //if(this.waitMsgTarget === true){
7741             this.el.unmask();
7742         //}else if(this.waitMsgTarget){
7743         //    this.waitMsgTarget.unmask();
7744         //}else{
7745         //    Roo.MessageBox.updateProgress(1);
7746         //    Roo.MessageBox.hide();
7747        // }
7748         //
7749         if(success){
7750             if(o.reset){
7751                 this.reset();
7752             }
7753             Roo.callback(o.success, o.scope, [this, action]);
7754             this.fireEvent('actioncomplete', this, action);
7755
7756         }else{
7757
7758             // failure condition..
7759             // we have a scenario where updates need confirming.
7760             // eg. if a locking scenario exists..
7761             // we look for { errors : { needs_confirm : true }} in the response.
7762             if (
7763                 (typeof(action.result) != 'undefined')  &&
7764                 (typeof(action.result.errors) != 'undefined')  &&
7765                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7766            ){
7767                 var _t = this;
7768                 Roo.log("not supported yet");
7769                  /*
7770
7771                 Roo.MessageBox.confirm(
7772                     "Change requires confirmation",
7773                     action.result.errorMsg,
7774                     function(r) {
7775                         if (r != 'yes') {
7776                             return;
7777                         }
7778                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7779                     }
7780
7781                 );
7782                 */
7783
7784
7785                 return;
7786             }
7787
7788             Roo.callback(o.failure, o.scope, [this, action]);
7789             // show an error message if no failed handler is set..
7790             if (!this.hasListener('actionfailed')) {
7791                 Roo.log("need to add dialog support");
7792                 /*
7793                 Roo.MessageBox.alert("Error",
7794                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7795                         action.result.errorMsg :
7796                         "Saving Failed, please check your entries or try again"
7797                 );
7798                 */
7799             }
7800
7801             this.fireEvent('actionfailed', this, action);
7802         }
7803
7804     },
7805     /**
7806      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7807      * @param {String} id The value to search for
7808      * @return Field
7809      */
7810     findField : function(id){
7811         var items = this.getItems();
7812         var field = items.get(id);
7813         if(!field){
7814              items.each(function(f){
7815                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7816                     field = f;
7817                     return false;
7818                 }
7819                 return true;
7820             });
7821         }
7822         return field || null;
7823     },
7824      /**
7825      * Mark fields in this form invalid in bulk.
7826      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7827      * @return {BasicForm} this
7828      */
7829     markInvalid : function(errors){
7830         if(errors instanceof Array){
7831             for(var i = 0, len = errors.length; i < len; i++){
7832                 var fieldError = errors[i];
7833                 var f = this.findField(fieldError.id);
7834                 if(f){
7835                     f.markInvalid(fieldError.msg);
7836                 }
7837             }
7838         }else{
7839             var field, id;
7840             for(id in errors){
7841                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7842                     field.markInvalid(errors[id]);
7843                 }
7844             }
7845         }
7846         //Roo.each(this.childForms || [], function (f) {
7847         //    f.markInvalid(errors);
7848         //});
7849
7850         return this;
7851     },
7852
7853     /**
7854      * Set values for fields in this form in bulk.
7855      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7856      * @return {BasicForm} this
7857      */
7858     setValues : function(values){
7859         if(values instanceof Array){ // array of objects
7860             for(var i = 0, len = values.length; i < len; i++){
7861                 var v = values[i];
7862                 var f = this.findField(v.id);
7863                 if(f){
7864                     f.setValue(v.value);
7865                     if(this.trackResetOnLoad){
7866                         f.originalValue = f.getValue();
7867                     }
7868                 }
7869             }
7870         }else{ // object hash
7871             var field, id;
7872             for(id in values){
7873                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7874
7875                     if (field.setFromData &&
7876                         field.valueField &&
7877                         field.displayField &&
7878                         // combos' with local stores can
7879                         // be queried via setValue()
7880                         // to set their value..
7881                         (field.store && !field.store.isLocal)
7882                         ) {
7883                         // it's a combo
7884                         var sd = { };
7885                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7886                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7887                         field.setFromData(sd);
7888
7889                     } else {
7890                         field.setValue(values[id]);
7891                     }
7892
7893
7894                     if(this.trackResetOnLoad){
7895                         field.originalValue = field.getValue();
7896                     }
7897                 }
7898             }
7899         }
7900
7901         //Roo.each(this.childForms || [], function (f) {
7902         //    f.setValues(values);
7903         //});
7904
7905         return this;
7906     },
7907
7908     /**
7909      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7910      * they are returned as an array.
7911      * @param {Boolean} asString
7912      * @return {Object}
7913      */
7914     getValues : function(asString){
7915         //if (this.childForms) {
7916             // copy values from the child forms
7917         //    Roo.each(this.childForms, function (f) {
7918         //        this.setValues(f.getValues());
7919         //    }, this);
7920         //}
7921
7922
7923
7924         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7925         if(asString === true){
7926             return fs;
7927         }
7928         return Roo.urlDecode(fs);
7929     },
7930
7931     /**
7932      * Returns the fields in this form as an object with key/value pairs.
7933      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7934      * @return {Object}
7935      */
7936     getFieldValues : function(with_hidden)
7937     {
7938         var items = this.getItems();
7939         var ret = {};
7940         items.each(function(f){
7941             if (!f.getName()) {
7942                 return;
7943             }
7944             var v = f.getValue();
7945             if (f.inputType =='radio') {
7946                 if (typeof(ret[f.getName()]) == 'undefined') {
7947                     ret[f.getName()] = ''; // empty..
7948                 }
7949
7950                 if (!f.el.dom.checked) {
7951                     return;
7952
7953                 }
7954                 v = f.el.dom.value;
7955
7956             }
7957
7958             // not sure if this supported any more..
7959             if ((typeof(v) == 'object') && f.getRawValue) {
7960                 v = f.getRawValue() ; // dates..
7961             }
7962             // combo boxes where name != hiddenName...
7963             if (f.name !== false && f.name != '' && f.name != f.getName()) {
7964                 ret[f.name] = f.getRawValue();
7965             }
7966             ret[f.getName()] = v;
7967         });
7968
7969         return ret;
7970     },
7971
7972     /**
7973      * Clears all invalid messages in this form.
7974      * @return {BasicForm} this
7975      */
7976     clearInvalid : function(){
7977         var items = this.getItems();
7978
7979         items.each(function(f){
7980            f.clearInvalid();
7981         });
7982
7983
7984
7985         return this;
7986     },
7987
7988     /**
7989      * Resets this form.
7990      * @return {BasicForm} this
7991      */
7992     reset : function(){
7993         var items = this.getItems();
7994         items.each(function(f){
7995             f.reset();
7996         });
7997
7998         Roo.each(this.childForms || [], function (f) {
7999             f.reset();
8000         });
8001
8002
8003         return this;
8004     },
8005     getItems : function()
8006     {
8007         var r=new Roo.util.MixedCollection(false, function(o){
8008             return o.id || (o.id = Roo.id());
8009         });
8010         var iter = function(el) {
8011             if (el.inputEl) {
8012                 r.add(el);
8013             }
8014             if (!el.items) {
8015                 return;
8016             }
8017             Roo.each(el.items,function(e) {
8018                 iter(e);
8019             });
8020
8021
8022         };
8023
8024         iter(this);
8025         return r;
8026
8027
8028
8029
8030     }
8031
8032 });
8033
8034 Roo.apply(Roo.bootstrap.Form, {
8035     
8036     popover : {
8037         
8038         padding : 5,
8039         
8040         isApplied : false,
8041         
8042         isMasked : false,
8043         
8044         form : false,
8045         
8046         target : false,
8047         
8048         toolTip : false,
8049         
8050         intervalID : false,
8051         
8052         maskEl : false,
8053         
8054         apply : function()
8055         {
8056             if(this.isApplied){
8057                 return;
8058             }
8059             
8060             this.maskEl = {
8061                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8062                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8063                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8064                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8065             };
8066             
8067             this.maskEl.top.enableDisplayMode("block");
8068             this.maskEl.left.enableDisplayMode("block");
8069             this.maskEl.bottom.enableDisplayMode("block");
8070             this.maskEl.right.enableDisplayMode("block");
8071             
8072             this.toolTip = new Roo.bootstrap.Tooltip({
8073                 cls : 'roo-form-error-popover',
8074                 alignment : {
8075                     'left' : ['r-l', [-2,0], 'right'],
8076                     'right' : ['l-r', [2,0], 'left'],
8077                     'bottom' : ['tl-bl', [0,2], 'top'],
8078                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8079                 }
8080             });
8081             
8082             this.toolTip.render(Roo.get(document.body));
8083
8084             this.toolTip.el.enableDisplayMode("block");
8085             
8086             Roo.get(document.body).on('click', function(){
8087                 this.unmask();
8088             }, this);
8089             
8090             Roo.get(document.body).on('touchstart', function(){
8091                 this.unmask();
8092             }, this);
8093             
8094             this.isApplied = true
8095         },
8096         
8097         mask : function(form, target)
8098         {
8099             this.form = form;
8100             
8101             this.target = target;
8102             
8103             if(!this.form.errorMask || !target.el){
8104                 return;
8105             }
8106             
8107             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8108             
8109             var ot = this.target.el.calcOffsetsTo(scrollable);
8110             
8111             var scrollTo = ot[1] - this.form.maskOffset;
8112             
8113             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8114             
8115             scrollable.scrollTo('top', scrollTo);
8116             
8117             var box = this.target.el.getBox();
8118             Roo.log(box);
8119             var zIndex = Roo.bootstrap.Modal.zIndex++;
8120
8121             
8122             this.maskEl.top.setStyle('position', 'absolute');
8123             this.maskEl.top.setStyle('z-index', zIndex);
8124             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8125             this.maskEl.top.setLeft(0);
8126             this.maskEl.top.setTop(0);
8127             this.maskEl.top.show();
8128             
8129             this.maskEl.left.setStyle('position', 'absolute');
8130             this.maskEl.left.setStyle('z-index', zIndex);
8131             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8132             this.maskEl.left.setLeft(0);
8133             this.maskEl.left.setTop(box.y - this.padding);
8134             this.maskEl.left.show();
8135
8136             this.maskEl.bottom.setStyle('position', 'absolute');
8137             this.maskEl.bottom.setStyle('z-index', zIndex);
8138             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8139             this.maskEl.bottom.setLeft(0);
8140             this.maskEl.bottom.setTop(box.bottom + this.padding);
8141             this.maskEl.bottom.show();
8142
8143             this.maskEl.right.setStyle('position', 'absolute');
8144             this.maskEl.right.setStyle('z-index', zIndex);
8145             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8146             this.maskEl.right.setLeft(box.right + this.padding);
8147             this.maskEl.right.setTop(box.y - this.padding);
8148             this.maskEl.right.show();
8149
8150             this.toolTip.bindEl = this.target.el;
8151
8152             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8153
8154             var tip = this.target.blankText;
8155
8156             if(this.target.getValue() !== '' && this.target.regexText.length){
8157                 tip = this.target.regexText;
8158             }
8159
8160             this.toolTip.show(tip);
8161
8162             this.intervalID = window.setInterval(function() {
8163                 Roo.bootstrap.Form.popover.unmask();
8164             }, 10000);
8165
8166             window.onwheel = function(){ return false;};
8167             
8168             (function(){ this.isMasked = true; }).defer(500, this);
8169             
8170         },
8171         
8172         unmask : function()
8173         {
8174             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8175                 return;
8176             }
8177             
8178             this.maskEl.top.setStyle('position', 'absolute');
8179             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8180             this.maskEl.top.hide();
8181
8182             this.maskEl.left.setStyle('position', 'absolute');
8183             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8184             this.maskEl.left.hide();
8185
8186             this.maskEl.bottom.setStyle('position', 'absolute');
8187             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8188             this.maskEl.bottom.hide();
8189
8190             this.maskEl.right.setStyle('position', 'absolute');
8191             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8192             this.maskEl.right.hide();
8193             
8194             this.toolTip.hide();
8195             
8196             this.toolTip.el.hide();
8197             
8198             window.onwheel = function(){ return true;};
8199             
8200             if(this.intervalID){
8201                 window.clearInterval(this.intervalID);
8202                 this.intervalID = false;
8203             }
8204             
8205             this.isMasked = false;
8206             
8207         }
8208         
8209     }
8210     
8211 });
8212
8213 /*
8214  * Based on:
8215  * Ext JS Library 1.1.1
8216  * Copyright(c) 2006-2007, Ext JS, LLC.
8217  *
8218  * Originally Released Under LGPL - original licence link has changed is not relivant.
8219  *
8220  * Fork - LGPL
8221  * <script type="text/javascript">
8222  */
8223 /**
8224  * @class Roo.form.VTypes
8225  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8226  * @singleton
8227  */
8228 Roo.form.VTypes = function(){
8229     // closure these in so they are only created once.
8230     var alpha = /^[a-zA-Z_]+$/;
8231     var alphanum = /^[a-zA-Z0-9_]+$/;
8232     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8233     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8234
8235     // All these messages and functions are configurable
8236     return {
8237         /**
8238          * The function used to validate email addresses
8239          * @param {String} value The email address
8240          */
8241         'email' : function(v){
8242             return email.test(v);
8243         },
8244         /**
8245          * The error text to display when the email validation function returns false
8246          * @type String
8247          */
8248         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8249         /**
8250          * The keystroke filter mask to be applied on email input
8251          * @type RegExp
8252          */
8253         'emailMask' : /[a-z0-9_\.\-@]/i,
8254
8255         /**
8256          * The function used to validate URLs
8257          * @param {String} value The URL
8258          */
8259         'url' : function(v){
8260             return url.test(v);
8261         },
8262         /**
8263          * The error text to display when the url validation function returns false
8264          * @type String
8265          */
8266         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8267         
8268         /**
8269          * The function used to validate alpha values
8270          * @param {String} value The value
8271          */
8272         'alpha' : function(v){
8273             return alpha.test(v);
8274         },
8275         /**
8276          * The error text to display when the alpha validation function returns false
8277          * @type String
8278          */
8279         'alphaText' : 'This field should only contain letters and _',
8280         /**
8281          * The keystroke filter mask to be applied on alpha input
8282          * @type RegExp
8283          */
8284         'alphaMask' : /[a-z_]/i,
8285
8286         /**
8287          * The function used to validate alphanumeric values
8288          * @param {String} value The value
8289          */
8290         'alphanum' : function(v){
8291             return alphanum.test(v);
8292         },
8293         /**
8294          * The error text to display when the alphanumeric validation function returns false
8295          * @type String
8296          */
8297         'alphanumText' : 'This field should only contain letters, numbers and _',
8298         /**
8299          * The keystroke filter mask to be applied on alphanumeric input
8300          * @type RegExp
8301          */
8302         'alphanumMask' : /[a-z0-9_]/i
8303     };
8304 }();/*
8305  * - LGPL
8306  *
8307  * Input
8308  * 
8309  */
8310
8311 /**
8312  * @class Roo.bootstrap.Input
8313  * @extends Roo.bootstrap.Component
8314  * Bootstrap Input class
8315  * @cfg {Boolean} disabled is it disabled
8316  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8317  * @cfg {String} name name of the input
8318  * @cfg {string} fieldLabel - the label associated
8319  * @cfg {string} placeholder - placeholder to put in text.
8320  * @cfg {string}  before - input group add on before
8321  * @cfg {string} after - input group add on after
8322  * @cfg {string} size - (lg|sm) or leave empty..
8323  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8324  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8325  * @cfg {Number} md colspan out of 12 for computer-sized screens
8326  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8327  * @cfg {string} value default value of the input
8328  * @cfg {Number} labelWidth set the width of label 
8329  * @cfg {Number} labellg set the width of label (1-12)
8330  * @cfg {Number} labelmd set the width of label (1-12)
8331  * @cfg {Number} labelsm set the width of label (1-12)
8332  * @cfg {Number} labelxs set the width of label (1-12)
8333  * @cfg {String} labelAlign (top|left)
8334  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8335  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8336  * @cfg {String} indicatorpos (left|right) default left
8337
8338  * @cfg {String} align (left|center|right) Default left
8339  * @cfg {Boolean} forceFeedback (true|false) Default false
8340  * 
8341  * 
8342  * 
8343  * 
8344  * @constructor
8345  * Create a new Input
8346  * @param {Object} config The config object
8347  */
8348
8349 Roo.bootstrap.Input = function(config){
8350     
8351     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8352     
8353     this.addEvents({
8354         /**
8355          * @event focus
8356          * Fires when this field receives input focus.
8357          * @param {Roo.form.Field} this
8358          */
8359         focus : true,
8360         /**
8361          * @event blur
8362          * Fires when this field loses input focus.
8363          * @param {Roo.form.Field} this
8364          */
8365         blur : true,
8366         /**
8367          * @event specialkey
8368          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8369          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8370          * @param {Roo.form.Field} this
8371          * @param {Roo.EventObject} e The event object
8372          */
8373         specialkey : true,
8374         /**
8375          * @event change
8376          * Fires just before the field blurs if the field value has changed.
8377          * @param {Roo.form.Field} this
8378          * @param {Mixed} newValue The new value
8379          * @param {Mixed} oldValue The original value
8380          */
8381         change : true,
8382         /**
8383          * @event invalid
8384          * Fires after the field has been marked as invalid.
8385          * @param {Roo.form.Field} this
8386          * @param {String} msg The validation message
8387          */
8388         invalid : true,
8389         /**
8390          * @event valid
8391          * Fires after the field has been validated with no errors.
8392          * @param {Roo.form.Field} this
8393          */
8394         valid : true,
8395          /**
8396          * @event keyup
8397          * Fires after the key up
8398          * @param {Roo.form.Field} this
8399          * @param {Roo.EventObject}  e The event Object
8400          */
8401         keyup : true
8402     });
8403 };
8404
8405 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8406      /**
8407      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8408       automatic validation (defaults to "keyup").
8409      */
8410     validationEvent : "keyup",
8411      /**
8412      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8413      */
8414     validateOnBlur : true,
8415     /**
8416      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8417      */
8418     validationDelay : 250,
8419      /**
8420      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8421      */
8422     focusClass : "x-form-focus",  // not needed???
8423     
8424        
8425     /**
8426      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8427      */
8428     invalidClass : "has-warning",
8429     
8430     /**
8431      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8432      */
8433     validClass : "has-success",
8434     
8435     /**
8436      * @cfg {Boolean} hasFeedback (true|false) default true
8437      */
8438     hasFeedback : true,
8439     
8440     /**
8441      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8442      */
8443     invalidFeedbackClass : "glyphicon-warning-sign",
8444     
8445     /**
8446      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8447      */
8448     validFeedbackClass : "glyphicon-ok",
8449     
8450     /**
8451      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8452      */
8453     selectOnFocus : false,
8454     
8455      /**
8456      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8457      */
8458     maskRe : null,
8459        /**
8460      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8461      */
8462     vtype : null,
8463     
8464       /**
8465      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8466      */
8467     disableKeyFilter : false,
8468     
8469        /**
8470      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8471      */
8472     disabled : false,
8473      /**
8474      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8475      */
8476     allowBlank : true,
8477     /**
8478      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8479      */
8480     blankText : "Please complete this mandatory field",
8481     
8482      /**
8483      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8484      */
8485     minLength : 0,
8486     /**
8487      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8488      */
8489     maxLength : Number.MAX_VALUE,
8490     /**
8491      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8492      */
8493     minLengthText : "The minimum length for this field is {0}",
8494     /**
8495      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8496      */
8497     maxLengthText : "The maximum length for this field is {0}",
8498   
8499     
8500     /**
8501      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8502      * If available, this function will be called only after the basic validators all return true, and will be passed the
8503      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8504      */
8505     validator : null,
8506     /**
8507      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8508      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8509      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8510      */
8511     regex : null,
8512     /**
8513      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
8514      */
8515     regexText : "",
8516     
8517     autocomplete: false,
8518     
8519     
8520     fieldLabel : '',
8521     inputType : 'text',
8522     
8523     name : false,
8524     placeholder: false,
8525     before : false,
8526     after : false,
8527     size : false,
8528     hasFocus : false,
8529     preventMark: false,
8530     isFormField : true,
8531     value : '',
8532     labelWidth : 2,
8533     labelAlign : false,
8534     readOnly : false,
8535     align : false,
8536     formatedValue : false,
8537     forceFeedback : false,
8538     
8539     indicatorpos : 'left',
8540     
8541     labellg : 0,
8542     labelmd : 0,
8543     labelsm : 0,
8544     labelxs : 0,
8545     
8546     parentLabelAlign : function()
8547     {
8548         var parent = this;
8549         while (parent.parent()) {
8550             parent = parent.parent();
8551             if (typeof(parent.labelAlign) !='undefined') {
8552                 return parent.labelAlign;
8553             }
8554         }
8555         return 'left';
8556         
8557     },
8558     
8559     getAutoCreate : function()
8560     {
8561         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8562         
8563         var id = Roo.id();
8564         
8565         var cfg = {};
8566         
8567         if(this.inputType != 'hidden'){
8568             cfg.cls = 'form-group' //input-group
8569         }
8570         
8571         var input =  {
8572             tag: 'input',
8573             id : id,
8574             type : this.inputType,
8575             value : this.value,
8576             cls : 'form-control',
8577             placeholder : this.placeholder || '',
8578             autocomplete : this.autocomplete || 'new-password'
8579         };
8580         
8581         if(this.align){
8582             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8583         }
8584         
8585         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8586             input.maxLength = this.maxLength;
8587         }
8588         
8589         if (this.disabled) {
8590             input.disabled=true;
8591         }
8592         
8593         if (this.readOnly) {
8594             input.readonly=true;
8595         }
8596         
8597         if (this.name) {
8598             input.name = this.name;
8599         }
8600         
8601         if (this.size) {
8602             input.cls += ' input-' + this.size;
8603         }
8604         
8605         var settings=this;
8606         ['xs','sm','md','lg'].map(function(size){
8607             if (settings[size]) {
8608                 cfg.cls += ' col-' + size + '-' + settings[size];
8609             }
8610         });
8611         
8612         var inputblock = input;
8613         
8614         var feedback = {
8615             tag: 'span',
8616             cls: 'glyphicon form-control-feedback'
8617         };
8618             
8619         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8620             
8621             inputblock = {
8622                 cls : 'has-feedback',
8623                 cn :  [
8624                     input,
8625                     feedback
8626                 ] 
8627             };  
8628         }
8629         
8630         if (this.before || this.after) {
8631             
8632             inputblock = {
8633                 cls : 'input-group',
8634                 cn :  [] 
8635             };
8636             
8637             if (this.before && typeof(this.before) == 'string') {
8638                 
8639                 inputblock.cn.push({
8640                     tag :'span',
8641                     cls : 'roo-input-before input-group-addon',
8642                     html : this.before
8643                 });
8644             }
8645             if (this.before && typeof(this.before) == 'object') {
8646                 this.before = Roo.factory(this.before);
8647                 
8648                 inputblock.cn.push({
8649                     tag :'span',
8650                     cls : 'roo-input-before input-group-' +
8651                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8652                 });
8653             }
8654             
8655             inputblock.cn.push(input);
8656             
8657             if (this.after && typeof(this.after) == 'string') {
8658                 inputblock.cn.push({
8659                     tag :'span',
8660                     cls : 'roo-input-after input-group-addon',
8661                     html : this.after
8662                 });
8663             }
8664             if (this.after && typeof(this.after) == 'object') {
8665                 this.after = Roo.factory(this.after);
8666                 
8667                 inputblock.cn.push({
8668                     tag :'span',
8669                     cls : 'roo-input-after input-group-' +
8670                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8671                 });
8672             }
8673             
8674             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8675                 inputblock.cls += ' has-feedback';
8676                 inputblock.cn.push(feedback);
8677             }
8678         };
8679         
8680         if (align ==='left' && this.fieldLabel.length) {
8681             
8682             cfg.cls += ' roo-form-group-label-left';
8683             
8684             cfg.cn = [
8685                 {
8686                     tag : 'i',
8687                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8688                     tooltip : 'This field is required'
8689                 },
8690                 {
8691                     tag: 'label',
8692                     'for' :  id,
8693                     cls : 'control-label',
8694                     html : this.fieldLabel
8695
8696                 },
8697                 {
8698                     cls : "", 
8699                     cn: [
8700                         inputblock
8701                     ]
8702                 }
8703             ];
8704             
8705             var labelCfg = cfg.cn[1];
8706             var contentCfg = cfg.cn[2];
8707             
8708             if(this.indicatorpos == 'right'){
8709                 cfg.cn = [
8710                     {
8711                         tag: 'label',
8712                         'for' :  id,
8713                         cls : 'control-label',
8714                         html : this.fieldLabel
8715
8716                     },
8717                     {
8718                         tag : 'i',
8719                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8720                         tooltip : 'This field is required'
8721                     },
8722                     {
8723                         cls : "",
8724                         cn: [
8725                             inputblock
8726                         ]
8727                     }
8728
8729                 ];
8730                 
8731                 labelCfg = cfg.cn[0];
8732                 contentCfg = cfg.cn[2];
8733             
8734             }
8735             
8736             if(this.labelWidth > 12){
8737                 labelCfg.style = "width: " + this.labelWidth + 'px';
8738             }
8739             
8740             if(this.labelWidth < 13 && this.labelmd == 0){
8741                 this.labelmd = this.labelWidth;
8742             }
8743             
8744             if(this.labellg > 0){
8745                 labelCfg.cls += ' col-lg-' + this.labellg;
8746                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
8747             }
8748             
8749             if(this.labelmd > 0){
8750                 labelCfg.cls += ' col-md-' + this.labelmd;
8751                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
8752             }
8753             
8754             if(this.labelsm > 0){
8755                 labelCfg.cls += ' col-sm-' + this.labelsm;
8756                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
8757             }
8758             
8759             if(this.labelxs > 0){
8760                 labelCfg.cls += ' col-xs-' + this.labelxs;
8761                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
8762             }
8763             
8764             
8765         } else if ( this.fieldLabel.length) {
8766                 
8767             cfg.cn = [
8768                 {
8769                     tag : 'i',
8770                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8771                     tooltip : 'This field is required'
8772                 },
8773                 {
8774                     tag: 'label',
8775                    //cls : 'input-group-addon',
8776                     html : this.fieldLabel
8777
8778                 },
8779
8780                inputblock
8781
8782            ];
8783            
8784            if(this.indicatorpos == 'right'){
8785                 
8786                 cfg.cn = [
8787                     {
8788                         tag: 'label',
8789                        //cls : 'input-group-addon',
8790                         html : this.fieldLabel
8791
8792                     },
8793                     {
8794                         tag : 'i',
8795                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8796                         tooltip : 'This field is required'
8797                     },
8798
8799                    inputblock
8800
8801                ];
8802
8803             }
8804
8805         } else {
8806             
8807             cfg.cn = [
8808
8809                     inputblock
8810
8811             ];
8812                 
8813                 
8814         };
8815         
8816         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8817            cfg.cls += ' navbar-form';
8818         }
8819         
8820         if (this.parentType === 'NavGroup') {
8821            cfg.cls += ' navbar-form';
8822            cfg.tag = 'li';
8823         }
8824         
8825         return cfg;
8826         
8827     },
8828     /**
8829      * return the real input element.
8830      */
8831     inputEl: function ()
8832     {
8833         return this.el.select('input.form-control',true).first();
8834     },
8835     
8836     tooltipEl : function()
8837     {
8838         return this.inputEl();
8839     },
8840     
8841     indicatorEl : function()
8842     {
8843         var indicator = this.el.select('i.roo-required-indicator',true).first();
8844         
8845         if(!indicator){
8846             return false;
8847         }
8848         
8849         return indicator;
8850         
8851     },
8852     
8853     setDisabled : function(v)
8854     {
8855         var i  = this.inputEl().dom;
8856         if (!v) {
8857             i.removeAttribute('disabled');
8858             return;
8859             
8860         }
8861         i.setAttribute('disabled','true');
8862     },
8863     initEvents : function()
8864     {
8865           
8866         this.inputEl().on("keydown" , this.fireKey,  this);
8867         this.inputEl().on("focus", this.onFocus,  this);
8868         this.inputEl().on("blur", this.onBlur,  this);
8869         
8870         this.inputEl().relayEvent('keyup', this);
8871         
8872         this.indicator = this.indicatorEl();
8873         
8874         if(this.indicator){
8875             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
8876             this.indicator.hide();
8877         }
8878  
8879         // reference to original value for reset
8880         this.originalValue = this.getValue();
8881         //Roo.form.TextField.superclass.initEvents.call(this);
8882         if(this.validationEvent == 'keyup'){
8883             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8884             this.inputEl().on('keyup', this.filterValidation, this);
8885         }
8886         else if(this.validationEvent !== false){
8887             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8888         }
8889         
8890         if(this.selectOnFocus){
8891             this.on("focus", this.preFocus, this);
8892             
8893         }
8894         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8895             this.inputEl().on("keypress", this.filterKeys, this);
8896         } else {
8897             this.inputEl().relayEvent('keypress', this);
8898         }
8899        /* if(this.grow){
8900             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8901             this.el.on("click", this.autoSize,  this);
8902         }
8903         */
8904         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8905             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8906         }
8907         
8908         if (typeof(this.before) == 'object') {
8909             this.before.render(this.el.select('.roo-input-before',true).first());
8910         }
8911         if (typeof(this.after) == 'object') {
8912             this.after.render(this.el.select('.roo-input-after',true).first());
8913         }
8914         
8915         
8916     },
8917     filterValidation : function(e){
8918         if(!e.isNavKeyPress()){
8919             this.validationTask.delay(this.validationDelay);
8920         }
8921     },
8922      /**
8923      * Validates the field value
8924      * @return {Boolean} True if the value is valid, else false
8925      */
8926     validate : function(){
8927         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8928         if(this.disabled || this.validateValue(this.getRawValue())){
8929             this.markValid();
8930             return true;
8931         }
8932         
8933         this.markInvalid();
8934         return false;
8935     },
8936     
8937     
8938     /**
8939      * Validates a value according to the field's validation rules and marks the field as invalid
8940      * if the validation fails
8941      * @param {Mixed} value The value to validate
8942      * @return {Boolean} True if the value is valid, else false
8943      */
8944     validateValue : function(value){
8945         if(value.length < 1)  { // if it's blank
8946             if(this.allowBlank){
8947                 return true;
8948             }            
8949             return this.inputEl().hasClass('hide') ? true : false;
8950         }
8951         
8952         if(value.length < this.minLength){
8953             return false;
8954         }
8955         if(value.length > this.maxLength){
8956             return false;
8957         }
8958         if(this.vtype){
8959             var vt = Roo.form.VTypes;
8960             if(!vt[this.vtype](value, this)){
8961                 return false;
8962             }
8963         }
8964         if(typeof this.validator == "function"){
8965             var msg = this.validator(value);
8966             if(msg !== true){
8967                 return false;
8968             }
8969         }
8970         
8971         if(this.regex && !this.regex.test(value)){
8972             return false;
8973         }
8974         
8975         return true;
8976     },
8977
8978     
8979     
8980      // private
8981     fireKey : function(e){
8982         //Roo.log('field ' + e.getKey());
8983         if(e.isNavKeyPress()){
8984             this.fireEvent("specialkey", this, e);
8985         }
8986     },
8987     focus : function (selectText){
8988         if(this.rendered){
8989             this.inputEl().focus();
8990             if(selectText === true){
8991                 this.inputEl().dom.select();
8992             }
8993         }
8994         return this;
8995     } ,
8996     
8997     onFocus : function(){
8998         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8999            // this.el.addClass(this.focusClass);
9000         }
9001         if(!this.hasFocus){
9002             this.hasFocus = true;
9003             this.startValue = this.getValue();
9004             this.fireEvent("focus", this);
9005         }
9006     },
9007     
9008     beforeBlur : Roo.emptyFn,
9009
9010     
9011     // private
9012     onBlur : function(){
9013         this.beforeBlur();
9014         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9015             //this.el.removeClass(this.focusClass);
9016         }
9017         this.hasFocus = false;
9018         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9019             this.validate();
9020         }
9021         var v = this.getValue();
9022         if(String(v) !== String(this.startValue)){
9023             this.fireEvent('change', this, v, this.startValue);
9024         }
9025         this.fireEvent("blur", this);
9026     },
9027     
9028     /**
9029      * Resets the current field value to the originally loaded value and clears any validation messages
9030      */
9031     reset : function(){
9032         this.setValue(this.originalValue);
9033         this.validate();
9034     },
9035      /**
9036      * Returns the name of the field
9037      * @return {Mixed} name The name field
9038      */
9039     getName: function(){
9040         return this.name;
9041     },
9042      /**
9043      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9044      * @return {Mixed} value The field value
9045      */
9046     getValue : function(){
9047         
9048         var v = this.inputEl().getValue();
9049         
9050         return v;
9051     },
9052     /**
9053      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9054      * @return {Mixed} value The field value
9055      */
9056     getRawValue : function(){
9057         var v = this.inputEl().getValue();
9058         
9059         return v;
9060     },
9061     
9062     /**
9063      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9064      * @param {Mixed} value The value to set
9065      */
9066     setRawValue : function(v){
9067         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9068     },
9069     
9070     selectText : function(start, end){
9071         var v = this.getRawValue();
9072         if(v.length > 0){
9073             start = start === undefined ? 0 : start;
9074             end = end === undefined ? v.length : end;
9075             var d = this.inputEl().dom;
9076             if(d.setSelectionRange){
9077                 d.setSelectionRange(start, end);
9078             }else if(d.createTextRange){
9079                 var range = d.createTextRange();
9080                 range.moveStart("character", start);
9081                 range.moveEnd("character", v.length-end);
9082                 range.select();
9083             }
9084         }
9085     },
9086     
9087     /**
9088      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9089      * @param {Mixed} value The value to set
9090      */
9091     setValue : function(v){
9092         this.value = v;
9093         if(this.rendered){
9094             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9095             this.validate();
9096         }
9097     },
9098     
9099     /*
9100     processValue : function(value){
9101         if(this.stripCharsRe){
9102             var newValue = value.replace(this.stripCharsRe, '');
9103             if(newValue !== value){
9104                 this.setRawValue(newValue);
9105                 return newValue;
9106             }
9107         }
9108         return value;
9109     },
9110   */
9111     preFocus : function(){
9112         
9113         if(this.selectOnFocus){
9114             this.inputEl().dom.select();
9115         }
9116     },
9117     filterKeys : function(e){
9118         var k = e.getKey();
9119         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9120             return;
9121         }
9122         var c = e.getCharCode(), cc = String.fromCharCode(c);
9123         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9124             return;
9125         }
9126         if(!this.maskRe.test(cc)){
9127             e.stopEvent();
9128         }
9129     },
9130      /**
9131      * Clear any invalid styles/messages for this field
9132      */
9133     clearInvalid : function(){
9134         
9135         if(!this.el || this.preventMark){ // not rendered
9136             return;
9137         }
9138         
9139      
9140         this.el.removeClass(this.invalidClass);
9141         
9142         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9143             
9144             var feedback = this.el.select('.form-control-feedback', true).first();
9145             
9146             if(feedback){
9147                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9148             }
9149             
9150         }
9151         
9152         this.fireEvent('valid', this);
9153     },
9154     
9155      /**
9156      * Mark this field as valid
9157      */
9158     markValid : function()
9159     {
9160         if(!this.el  || this.preventMark){ // not rendered...
9161             return;
9162         }
9163         
9164         this.el.removeClass([this.invalidClass, this.validClass]);
9165         
9166         var feedback = this.el.select('.form-control-feedback', true).first();
9167             
9168         if(feedback){
9169             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9170         }
9171
9172         if(this.disabled){
9173             return;
9174         }
9175         
9176         if(this.allowBlank && !this.getRawValue().length){
9177             return;
9178         }
9179         
9180         if(this.indicator){
9181             this.indicator.hide();
9182         }
9183         
9184         this.el.addClass(this.validClass);
9185         
9186         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9187             
9188             var feedback = this.el.select('.form-control-feedback', true).first();
9189             
9190             if(feedback){
9191                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9192                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9193             }
9194             
9195         }
9196         
9197         this.fireEvent('valid', this);
9198     },
9199     
9200      /**
9201      * Mark this field as invalid
9202      * @param {String} msg The validation message
9203      */
9204     markInvalid : function(msg)
9205     {
9206         if(!this.el  || this.preventMark){ // not rendered
9207             return;
9208         }
9209         
9210         this.el.removeClass([this.invalidClass, this.validClass]);
9211         
9212         var feedback = this.el.select('.form-control-feedback', true).first();
9213             
9214         if(feedback){
9215             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9216         }
9217
9218         if(this.disabled){
9219             return;
9220         }
9221         
9222         if(this.allowBlank && !this.getRawValue().length){
9223             return;
9224         }
9225         
9226         if(this.indicator){
9227             this.indicator.show();
9228         }
9229         
9230         this.el.addClass(this.invalidClass);
9231         
9232         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9233             
9234             var feedback = this.el.select('.form-control-feedback', true).first();
9235             
9236             if(feedback){
9237                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9238                 
9239                 if(this.getValue().length || this.forceFeedback){
9240                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9241                 }
9242                 
9243             }
9244             
9245         }
9246         
9247         this.fireEvent('invalid', this, msg);
9248     },
9249     // private
9250     SafariOnKeyDown : function(event)
9251     {
9252         // this is a workaround for a password hang bug on chrome/ webkit.
9253         if (this.inputEl().dom.type != 'password') {
9254             return;
9255         }
9256         
9257         var isSelectAll = false;
9258         
9259         if(this.inputEl().dom.selectionEnd > 0){
9260             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9261         }
9262         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9263             event.preventDefault();
9264             this.setValue('');
9265             return;
9266         }
9267         
9268         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9269             
9270             event.preventDefault();
9271             // this is very hacky as keydown always get's upper case.
9272             //
9273             var cc = String.fromCharCode(event.getCharCode());
9274             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9275             
9276         }
9277     },
9278     adjustWidth : function(tag, w){
9279         tag = tag.toLowerCase();
9280         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9281             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9282                 if(tag == 'input'){
9283                     return w + 2;
9284                 }
9285                 if(tag == 'textarea'){
9286                     return w-2;
9287                 }
9288             }else if(Roo.isOpera){
9289                 if(tag == 'input'){
9290                     return w + 2;
9291                 }
9292                 if(tag == 'textarea'){
9293                     return w-2;
9294                 }
9295             }
9296         }
9297         return w;
9298     }
9299     
9300 });
9301
9302  
9303 /*
9304  * - LGPL
9305  *
9306  * Input
9307  * 
9308  */
9309
9310 /**
9311  * @class Roo.bootstrap.TextArea
9312  * @extends Roo.bootstrap.Input
9313  * Bootstrap TextArea class
9314  * @cfg {Number} cols Specifies the visible width of a text area
9315  * @cfg {Number} rows Specifies the visible number of lines in a text area
9316  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9317  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9318  * @cfg {string} html text
9319  * 
9320  * @constructor
9321  * Create a new TextArea
9322  * @param {Object} config The config object
9323  */
9324
9325 Roo.bootstrap.TextArea = function(config){
9326     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9327    
9328 };
9329
9330 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9331      
9332     cols : false,
9333     rows : 5,
9334     readOnly : false,
9335     warp : 'soft',
9336     resize : false,
9337     value: false,
9338     html: false,
9339     
9340     getAutoCreate : function(){
9341         
9342         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9343         
9344         var id = Roo.id();
9345         
9346         var cfg = {};
9347         
9348         var input =  {
9349             tag: 'textarea',
9350             id : id,
9351             warp : this.warp,
9352             rows : this.rows,
9353             value : this.value || '',
9354             html: this.html || '',
9355             cls : 'form-control',
9356             placeholder : this.placeholder || '' 
9357             
9358         };
9359         
9360         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9361             input.maxLength = this.maxLength;
9362         }
9363         
9364         if(this.resize){
9365             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9366         }
9367         
9368         if(this.cols){
9369             input.cols = this.cols;
9370         }
9371         
9372         if (this.readOnly) {
9373             input.readonly = true;
9374         }
9375         
9376         if (this.name) {
9377             input.name = this.name;
9378         }
9379         
9380         if (this.size) {
9381             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9382         }
9383         
9384         var settings=this;
9385         ['xs','sm','md','lg'].map(function(size){
9386             if (settings[size]) {
9387                 cfg.cls += ' col-' + size + '-' + settings[size];
9388             }
9389         });
9390         
9391         var inputblock = input;
9392         
9393         if(this.hasFeedback && !this.allowBlank){
9394             
9395             var feedback = {
9396                 tag: 'span',
9397                 cls: 'glyphicon form-control-feedback'
9398             };
9399
9400             inputblock = {
9401                 cls : 'has-feedback',
9402                 cn :  [
9403                     input,
9404                     feedback
9405                 ] 
9406             };  
9407         }
9408         
9409         
9410         if (this.before || this.after) {
9411             
9412             inputblock = {
9413                 cls : 'input-group',
9414                 cn :  [] 
9415             };
9416             if (this.before) {
9417                 inputblock.cn.push({
9418                     tag :'span',
9419                     cls : 'input-group-addon',
9420                     html : this.before
9421                 });
9422             }
9423             
9424             inputblock.cn.push(input);
9425             
9426             if(this.hasFeedback && !this.allowBlank){
9427                 inputblock.cls += ' has-feedback';
9428                 inputblock.cn.push(feedback);
9429             }
9430             
9431             if (this.after) {
9432                 inputblock.cn.push({
9433                     tag :'span',
9434                     cls : 'input-group-addon',
9435                     html : this.after
9436                 });
9437             }
9438             
9439         }
9440         
9441         if (align ==='left' && this.fieldLabel.length) {
9442             cfg.cn = [
9443                 {
9444                     tag: 'label',
9445                     'for' :  id,
9446                     cls : 'control-label',
9447                     html : this.fieldLabel
9448                 },
9449                 {
9450                     cls : "",
9451                     cn: [
9452                         inputblock
9453                     ]
9454                 }
9455
9456             ];
9457             
9458             if(this.labelWidth > 12){
9459                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9460             }
9461
9462             if(this.labelWidth < 13 && this.labelmd == 0){
9463                 this.labelmd = this.labelWidth;
9464             }
9465
9466             if(this.labellg > 0){
9467                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9468                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9469             }
9470
9471             if(this.labelmd > 0){
9472                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9473                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9474             }
9475
9476             if(this.labelsm > 0){
9477                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9478                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9479             }
9480
9481             if(this.labelxs > 0){
9482                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9483                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9484             }
9485             
9486         } else if ( this.fieldLabel.length) {
9487             cfg.cn = [
9488
9489                {
9490                    tag: 'label',
9491                    //cls : 'input-group-addon',
9492                    html : this.fieldLabel
9493
9494                },
9495
9496                inputblock
9497
9498            ];
9499
9500         } else {
9501
9502             cfg.cn = [
9503
9504                 inputblock
9505
9506             ];
9507                 
9508         }
9509         
9510         if (this.disabled) {
9511             input.disabled=true;
9512         }
9513         
9514         return cfg;
9515         
9516     },
9517     /**
9518      * return the real textarea element.
9519      */
9520     inputEl: function ()
9521     {
9522         return this.el.select('textarea.form-control',true).first();
9523     },
9524     
9525     /**
9526      * Clear any invalid styles/messages for this field
9527      */
9528     clearInvalid : function()
9529     {
9530         
9531         if(!this.el || this.preventMark){ // not rendered
9532             return;
9533         }
9534         
9535         var label = this.el.select('label', true).first();
9536         var icon = this.el.select('i.fa-star', true).first();
9537         
9538         if(label && icon){
9539             icon.remove();
9540         }
9541         
9542         this.el.removeClass(this.invalidClass);
9543         
9544         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9545             
9546             var feedback = this.el.select('.form-control-feedback', true).first();
9547             
9548             if(feedback){
9549                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9550             }
9551             
9552         }
9553         
9554         this.fireEvent('valid', this);
9555     },
9556     
9557      /**
9558      * Mark this field as valid
9559      */
9560     markValid : function()
9561     {
9562         if(!this.el  || this.preventMark){ // not rendered
9563             return;
9564         }
9565         
9566         this.el.removeClass([this.invalidClass, this.validClass]);
9567         
9568         var feedback = this.el.select('.form-control-feedback', true).first();
9569             
9570         if(feedback){
9571             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9572         }
9573
9574         if(this.disabled || this.allowBlank){
9575             return;
9576         }
9577         
9578         var label = this.el.select('label', true).first();
9579         var icon = this.el.select('i.fa-star', true).first();
9580         
9581         if(label && icon){
9582             icon.remove();
9583         }
9584         
9585         this.el.addClass(this.validClass);
9586         
9587         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9588             
9589             var feedback = this.el.select('.form-control-feedback', true).first();
9590             
9591             if(feedback){
9592                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9593                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9594             }
9595             
9596         }
9597         
9598         this.fireEvent('valid', this);
9599     },
9600     
9601      /**
9602      * Mark this field as invalid
9603      * @param {String} msg The validation message
9604      */
9605     markInvalid : function(msg)
9606     {
9607         if(!this.el  || this.preventMark){ // not rendered
9608             return;
9609         }
9610         
9611         this.el.removeClass([this.invalidClass, this.validClass]);
9612         
9613         var feedback = this.el.select('.form-control-feedback', true).first();
9614             
9615         if(feedback){
9616             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9617         }
9618
9619         if(this.disabled || this.allowBlank){
9620             return;
9621         }
9622         
9623         var label = this.el.select('label', true).first();
9624         var icon = this.el.select('i.fa-star', true).first();
9625         
9626         if(!this.getValue().length && label && !icon){
9627             this.el.createChild({
9628                 tag : 'i',
9629                 cls : 'text-danger fa fa-lg fa-star',
9630                 tooltip : 'This field is required',
9631                 style : 'margin-right:5px;'
9632             }, label, true);
9633         }
9634
9635         this.el.addClass(this.invalidClass);
9636         
9637         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
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                 if(this.getValue().length || this.forceFeedback){
9645                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9646                 }
9647                 
9648             }
9649             
9650         }
9651         
9652         this.fireEvent('invalid', this, msg);
9653     }
9654 });
9655
9656  
9657 /*
9658  * - LGPL
9659  *
9660  * trigger field - base class for combo..
9661  * 
9662  */
9663  
9664 /**
9665  * @class Roo.bootstrap.TriggerField
9666  * @extends Roo.bootstrap.Input
9667  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9668  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9669  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9670  * for which you can provide a custom implementation.  For example:
9671  * <pre><code>
9672 var trigger = new Roo.bootstrap.TriggerField();
9673 trigger.onTriggerClick = myTriggerFn;
9674 trigger.applyTo('my-field');
9675 </code></pre>
9676  *
9677  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9678  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9679  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9680  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9681  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9682
9683  * @constructor
9684  * Create a new TriggerField.
9685  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9686  * to the base TextField)
9687  */
9688 Roo.bootstrap.TriggerField = function(config){
9689     this.mimicing = false;
9690     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9691 };
9692
9693 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9694     /**
9695      * @cfg {String} triggerClass A CSS class to apply to the trigger
9696      */
9697      /**
9698      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9699      */
9700     hideTrigger:false,
9701
9702     /**
9703      * @cfg {Boolean} removable (true|false) special filter default false
9704      */
9705     removable : false,
9706     
9707     /** @cfg {Boolean} grow @hide */
9708     /** @cfg {Number} growMin @hide */
9709     /** @cfg {Number} growMax @hide */
9710
9711     /**
9712      * @hide 
9713      * @method
9714      */
9715     autoSize: Roo.emptyFn,
9716     // private
9717     monitorTab : true,
9718     // private
9719     deferHeight : true,
9720
9721     
9722     actionMode : 'wrap',
9723     
9724     caret : false,
9725     
9726     
9727     getAutoCreate : function(){
9728        
9729         var align = this.labelAlign || this.parentLabelAlign();
9730         
9731         var id = Roo.id();
9732         
9733         var cfg = {
9734             cls: 'form-group' //input-group
9735         };
9736         
9737         
9738         var input =  {
9739             tag: 'input',
9740             id : id,
9741             type : this.inputType,
9742             cls : 'form-control',
9743             autocomplete: 'new-password',
9744             placeholder : this.placeholder || '' 
9745             
9746         };
9747         if (this.name) {
9748             input.name = this.name;
9749         }
9750         if (this.size) {
9751             input.cls += ' input-' + this.size;
9752         }
9753         
9754         if (this.disabled) {
9755             input.disabled=true;
9756         }
9757         
9758         var inputblock = input;
9759         
9760         if(this.hasFeedback && !this.allowBlank){
9761             
9762             var feedback = {
9763                 tag: 'span',
9764                 cls: 'glyphicon form-control-feedback'
9765             };
9766             
9767             if(this.removable && !this.editable && !this.tickable){
9768                 inputblock = {
9769                     cls : 'has-feedback',
9770                     cn :  [
9771                         inputblock,
9772                         {
9773                             tag: 'button',
9774                             html : 'x',
9775                             cls : 'roo-combo-removable-btn close'
9776                         },
9777                         feedback
9778                     ] 
9779                 };
9780             } else {
9781                 inputblock = {
9782                     cls : 'has-feedback',
9783                     cn :  [
9784                         inputblock,
9785                         feedback
9786                     ] 
9787                 };
9788             }
9789
9790         } else {
9791             if(this.removable && !this.editable && !this.tickable){
9792                 inputblock = {
9793                     cls : 'roo-removable',
9794                     cn :  [
9795                         inputblock,
9796                         {
9797                             tag: 'button',
9798                             html : 'x',
9799                             cls : 'roo-combo-removable-btn close'
9800                         }
9801                     ] 
9802                 };
9803             }
9804         }
9805         
9806         if (this.before || this.after) {
9807             
9808             inputblock = {
9809                 cls : 'input-group',
9810                 cn :  [] 
9811             };
9812             if (this.before) {
9813                 inputblock.cn.push({
9814                     tag :'span',
9815                     cls : 'input-group-addon',
9816                     html : this.before
9817                 });
9818             }
9819             
9820             inputblock.cn.push(input);
9821             
9822             if(this.hasFeedback && !this.allowBlank){
9823                 inputblock.cls += ' has-feedback';
9824                 inputblock.cn.push(feedback);
9825             }
9826             
9827             if (this.after) {
9828                 inputblock.cn.push({
9829                     tag :'span',
9830                     cls : 'input-group-addon',
9831                     html : this.after
9832                 });
9833             }
9834             
9835         };
9836         
9837         var box = {
9838             tag: 'div',
9839             cn: [
9840                 {
9841                     tag: 'input',
9842                     type : 'hidden',
9843                     cls: 'form-hidden-field'
9844                 },
9845                 inputblock
9846             ]
9847             
9848         };
9849         
9850         if(this.multiple){
9851             box = {
9852                 tag: 'div',
9853                 cn: [
9854                     {
9855                         tag: 'input',
9856                         type : 'hidden',
9857                         cls: 'form-hidden-field'
9858                     },
9859                     {
9860                         tag: 'ul',
9861                         cls: 'roo-select2-choices',
9862                         cn:[
9863                             {
9864                                 tag: 'li',
9865                                 cls: 'roo-select2-search-field',
9866                                 cn: [
9867
9868                                     inputblock
9869                                 ]
9870                             }
9871                         ]
9872                     }
9873                 ]
9874             }
9875         };
9876         
9877         var combobox = {
9878             cls: 'roo-select2-container input-group',
9879             cn: [
9880                 box
9881 //                {
9882 //                    tag: 'ul',
9883 //                    cls: 'typeahead typeahead-long dropdown-menu',
9884 //                    style: 'display:none'
9885 //                }
9886             ]
9887         };
9888         
9889         if(!this.multiple && this.showToggleBtn){
9890             
9891             var caret = {
9892                         tag: 'span',
9893                         cls: 'caret'
9894              };
9895             if (this.caret != false) {
9896                 caret = {
9897                      tag: 'i',
9898                      cls: 'fa fa-' + this.caret
9899                 };
9900                 
9901             }
9902             
9903             combobox.cn.push({
9904                 tag :'span',
9905                 cls : 'input-group-addon btn dropdown-toggle',
9906                 cn : [
9907                     caret,
9908                     {
9909                         tag: 'span',
9910                         cls: 'combobox-clear',
9911                         cn  : [
9912                             {
9913                                 tag : 'i',
9914                                 cls: 'icon-remove'
9915                             }
9916                         ]
9917                     }
9918                 ]
9919
9920             })
9921         }
9922         
9923         if(this.multiple){
9924             combobox.cls += ' roo-select2-container-multi';
9925         }
9926         
9927         if (align ==='left' && this.fieldLabel.length) {
9928             
9929             cfg.cls += ' roo-form-group-label-left';
9930
9931             cfg.cn = [
9932                 {
9933                     tag : 'i',
9934                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9935                     tooltip : 'This field is required'
9936                 },
9937                 {
9938                     tag: 'label',
9939                     'for' :  id,
9940                     cls : 'control-label',
9941                     html : this.fieldLabel
9942
9943                 },
9944                 {
9945                     cls : "", 
9946                     cn: [
9947                         combobox
9948                     ]
9949                 }
9950
9951             ];
9952             
9953             var labelCfg = cfg.cn[1];
9954             var contentCfg = cfg.cn[2];
9955             
9956             if(this.indicatorpos == 'right'){
9957                 cfg.cn = [
9958                     {
9959                         tag: 'label',
9960                         'for' :  id,
9961                         cls : 'control-label',
9962                         cn : [
9963                             {
9964                                 tag : 'span',
9965                                 html : this.fieldLabel
9966                             },
9967                             {
9968                                 tag : 'i',
9969                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9970                                 tooltip : 'This field is required'
9971                             }
9972                         ]
9973                     },
9974                     {
9975                         cls : "", 
9976                         cn: [
9977                             combobox
9978                         ]
9979                     }
9980
9981                 ];
9982                 
9983                 labelCfg = cfg.cn[0];
9984                 contentCfg = cfg.cn[1];
9985             }
9986             
9987             if(this.labelWidth > 12){
9988                 labelCfg.style = "width: " + this.labelWidth + 'px';
9989             }
9990             
9991             if(this.labelWidth < 13 && this.labelmd == 0){
9992                 this.labelmd = this.labelWidth;
9993             }
9994             
9995             if(this.labellg > 0){
9996                 labelCfg.cls += ' col-lg-' + this.labellg;
9997                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9998             }
9999             
10000             if(this.labelmd > 0){
10001                 labelCfg.cls += ' col-md-' + this.labelmd;
10002                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10003             }
10004             
10005             if(this.labelsm > 0){
10006                 labelCfg.cls += ' col-sm-' + this.labelsm;
10007                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10008             }
10009             
10010             if(this.labelxs > 0){
10011                 labelCfg.cls += ' col-xs-' + this.labelxs;
10012                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10013             }
10014             
10015         } else if ( this.fieldLabel.length) {
10016 //                Roo.log(" label");
10017             cfg.cn = [
10018                 {
10019                    tag : 'i',
10020                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10021                    tooltip : 'This field is required'
10022                },
10023                {
10024                    tag: 'label',
10025                    //cls : 'input-group-addon',
10026                    html : this.fieldLabel
10027
10028                },
10029
10030                combobox
10031
10032             ];
10033             
10034             if(this.indicatorpos == 'right'){
10035                 
10036                 cfg.cn = [
10037                     {
10038                        tag: 'label',
10039                        cn : [
10040                            {
10041                                tag : 'span',
10042                                html : this.fieldLabel
10043                            },
10044                            {
10045                               tag : 'i',
10046                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10047                               tooltip : 'This field is required'
10048                            }
10049                        ]
10050
10051                     },
10052                     combobox
10053
10054                 ];
10055
10056             }
10057
10058         } else {
10059             
10060 //                Roo.log(" no label && no align");
10061                 cfg = combobox
10062                      
10063                 
10064         }
10065         
10066         var settings=this;
10067         ['xs','sm','md','lg'].map(function(size){
10068             if (settings[size]) {
10069                 cfg.cls += ' col-' + size + '-' + settings[size];
10070             }
10071         });
10072         
10073         return cfg;
10074         
10075     },
10076     
10077     
10078     
10079     // private
10080     onResize : function(w, h){
10081 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10082 //        if(typeof w == 'number'){
10083 //            var x = w - this.trigger.getWidth();
10084 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10085 //            this.trigger.setStyle('left', x+'px');
10086 //        }
10087     },
10088
10089     // private
10090     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10091
10092     // private
10093     getResizeEl : function(){
10094         return this.inputEl();
10095     },
10096
10097     // private
10098     getPositionEl : function(){
10099         return this.inputEl();
10100     },
10101
10102     // private
10103     alignErrorIcon : function(){
10104         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10105     },
10106
10107     // private
10108     initEvents : function(){
10109         
10110         this.createList();
10111         
10112         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10113         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10114         if(!this.multiple && this.showToggleBtn){
10115             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10116             if(this.hideTrigger){
10117                 this.trigger.setDisplayed(false);
10118             }
10119             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10120         }
10121         
10122         if(this.multiple){
10123             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10124         }
10125         
10126         if(this.removable && !this.editable && !this.tickable){
10127             var close = this.closeTriggerEl();
10128             
10129             if(close){
10130                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10131                 close.on('click', this.removeBtnClick, this, close);
10132             }
10133         }
10134         
10135         //this.trigger.addClassOnOver('x-form-trigger-over');
10136         //this.trigger.addClassOnClick('x-form-trigger-click');
10137         
10138         //if(!this.width){
10139         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10140         //}
10141     },
10142     
10143     closeTriggerEl : function()
10144     {
10145         var close = this.el.select('.roo-combo-removable-btn', true).first();
10146         return close ? close : false;
10147     },
10148     
10149     removeBtnClick : function(e, h, el)
10150     {
10151         e.preventDefault();
10152         
10153         if(this.fireEvent("remove", this) !== false){
10154             this.reset();
10155             this.fireEvent("afterremove", this)
10156         }
10157     },
10158     
10159     createList : function()
10160     {
10161         this.list = Roo.get(document.body).createChild({
10162             tag: 'ul',
10163             cls: 'typeahead typeahead-long dropdown-menu',
10164             style: 'display:none'
10165         });
10166         
10167         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10168         
10169     },
10170
10171     // private
10172     initTrigger : function(){
10173        
10174     },
10175
10176     // private
10177     onDestroy : function(){
10178         if(this.trigger){
10179             this.trigger.removeAllListeners();
10180           //  this.trigger.remove();
10181         }
10182         //if(this.wrap){
10183         //    this.wrap.remove();
10184         //}
10185         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10186     },
10187
10188     // private
10189     onFocus : function(){
10190         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10191         /*
10192         if(!this.mimicing){
10193             this.wrap.addClass('x-trigger-wrap-focus');
10194             this.mimicing = true;
10195             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10196             if(this.monitorTab){
10197                 this.el.on("keydown", this.checkTab, this);
10198             }
10199         }
10200         */
10201     },
10202
10203     // private
10204     checkTab : function(e){
10205         if(e.getKey() == e.TAB){
10206             this.triggerBlur();
10207         }
10208     },
10209
10210     // private
10211     onBlur : function(){
10212         // do nothing
10213     },
10214
10215     // private
10216     mimicBlur : function(e, t){
10217         /*
10218         if(!this.wrap.contains(t) && this.validateBlur()){
10219             this.triggerBlur();
10220         }
10221         */
10222     },
10223
10224     // private
10225     triggerBlur : function(){
10226         this.mimicing = false;
10227         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10228         if(this.monitorTab){
10229             this.el.un("keydown", this.checkTab, this);
10230         }
10231         //this.wrap.removeClass('x-trigger-wrap-focus');
10232         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10233     },
10234
10235     // private
10236     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10237     validateBlur : function(e, t){
10238         return true;
10239     },
10240
10241     // private
10242     onDisable : function(){
10243         this.inputEl().dom.disabled = true;
10244         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10245         //if(this.wrap){
10246         //    this.wrap.addClass('x-item-disabled');
10247         //}
10248     },
10249
10250     // private
10251     onEnable : function(){
10252         this.inputEl().dom.disabled = false;
10253         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10254         //if(this.wrap){
10255         //    this.el.removeClass('x-item-disabled');
10256         //}
10257     },
10258
10259     // private
10260     onShow : function(){
10261         var ae = this.getActionEl();
10262         
10263         if(ae){
10264             ae.dom.style.display = '';
10265             ae.dom.style.visibility = 'visible';
10266         }
10267     },
10268
10269     // private
10270     
10271     onHide : function(){
10272         var ae = this.getActionEl();
10273         ae.dom.style.display = 'none';
10274     },
10275
10276     /**
10277      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10278      * by an implementing function.
10279      * @method
10280      * @param {EventObject} e
10281      */
10282     onTriggerClick : Roo.emptyFn
10283 });
10284  /*
10285  * Based on:
10286  * Ext JS Library 1.1.1
10287  * Copyright(c) 2006-2007, Ext JS, LLC.
10288  *
10289  * Originally Released Under LGPL - original licence link has changed is not relivant.
10290  *
10291  * Fork - LGPL
10292  * <script type="text/javascript">
10293  */
10294
10295
10296 /**
10297  * @class Roo.data.SortTypes
10298  * @singleton
10299  * Defines the default sorting (casting?) comparison functions used when sorting data.
10300  */
10301 Roo.data.SortTypes = {
10302     /**
10303      * Default sort that does nothing
10304      * @param {Mixed} s The value being converted
10305      * @return {Mixed} The comparison value
10306      */
10307     none : function(s){
10308         return s;
10309     },
10310     
10311     /**
10312      * The regular expression used to strip tags
10313      * @type {RegExp}
10314      * @property
10315      */
10316     stripTagsRE : /<\/?[^>]+>/gi,
10317     
10318     /**
10319      * Strips all HTML tags to sort on text only
10320      * @param {Mixed} s The value being converted
10321      * @return {String} The comparison value
10322      */
10323     asText : function(s){
10324         return String(s).replace(this.stripTagsRE, "");
10325     },
10326     
10327     /**
10328      * Strips all HTML tags to sort on text only - Case insensitive
10329      * @param {Mixed} s The value being converted
10330      * @return {String} The comparison value
10331      */
10332     asUCText : function(s){
10333         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10334     },
10335     
10336     /**
10337      * Case insensitive string
10338      * @param {Mixed} s The value being converted
10339      * @return {String} The comparison value
10340      */
10341     asUCString : function(s) {
10342         return String(s).toUpperCase();
10343     },
10344     
10345     /**
10346      * Date sorting
10347      * @param {Mixed} s The value being converted
10348      * @return {Number} The comparison value
10349      */
10350     asDate : function(s) {
10351         if(!s){
10352             return 0;
10353         }
10354         if(s instanceof Date){
10355             return s.getTime();
10356         }
10357         return Date.parse(String(s));
10358     },
10359     
10360     /**
10361      * Float sorting
10362      * @param {Mixed} s The value being converted
10363      * @return {Float} The comparison value
10364      */
10365     asFloat : function(s) {
10366         var val = parseFloat(String(s).replace(/,/g, ""));
10367         if(isNaN(val)) {
10368             val = 0;
10369         }
10370         return val;
10371     },
10372     
10373     /**
10374      * Integer sorting
10375      * @param {Mixed} s The value being converted
10376      * @return {Number} The comparison value
10377      */
10378     asInt : function(s) {
10379         var val = parseInt(String(s).replace(/,/g, ""));
10380         if(isNaN(val)) {
10381             val = 0;
10382         }
10383         return val;
10384     }
10385 };/*
10386  * Based on:
10387  * Ext JS Library 1.1.1
10388  * Copyright(c) 2006-2007, Ext JS, LLC.
10389  *
10390  * Originally Released Under LGPL - original licence link has changed is not relivant.
10391  *
10392  * Fork - LGPL
10393  * <script type="text/javascript">
10394  */
10395
10396 /**
10397 * @class Roo.data.Record
10398  * Instances of this class encapsulate both record <em>definition</em> information, and record
10399  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10400  * to access Records cached in an {@link Roo.data.Store} object.<br>
10401  * <p>
10402  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10403  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10404  * objects.<br>
10405  * <p>
10406  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10407  * @constructor
10408  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10409  * {@link #create}. The parameters are the same.
10410  * @param {Array} data An associative Array of data values keyed by the field name.
10411  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10412  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10413  * not specified an integer id is generated.
10414  */
10415 Roo.data.Record = function(data, id){
10416     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10417     this.data = data;
10418 };
10419
10420 /**
10421  * Generate a constructor for a specific record layout.
10422  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10423  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10424  * Each field definition object may contain the following properties: <ul>
10425  * <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,
10426  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10427  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10428  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10429  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10430  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10431  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10432  * this may be omitted.</p></li>
10433  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10434  * <ul><li>auto (Default, implies no conversion)</li>
10435  * <li>string</li>
10436  * <li>int</li>
10437  * <li>float</li>
10438  * <li>boolean</li>
10439  * <li>date</li></ul></p></li>
10440  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10441  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10442  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10443  * by the Reader into an object that will be stored in the Record. It is passed the
10444  * following parameters:<ul>
10445  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10446  * </ul></p></li>
10447  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10448  * </ul>
10449  * <br>usage:<br><pre><code>
10450 var TopicRecord = Roo.data.Record.create(
10451     {name: 'title', mapping: 'topic_title'},
10452     {name: 'author', mapping: 'username'},
10453     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10454     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10455     {name: 'lastPoster', mapping: 'user2'},
10456     {name: 'excerpt', mapping: 'post_text'}
10457 );
10458
10459 var myNewRecord = new TopicRecord({
10460     title: 'Do my job please',
10461     author: 'noobie',
10462     totalPosts: 1,
10463     lastPost: new Date(),
10464     lastPoster: 'Animal',
10465     excerpt: 'No way dude!'
10466 });
10467 myStore.add(myNewRecord);
10468 </code></pre>
10469  * @method create
10470  * @static
10471  */
10472 Roo.data.Record.create = function(o){
10473     var f = function(){
10474         f.superclass.constructor.apply(this, arguments);
10475     };
10476     Roo.extend(f, Roo.data.Record);
10477     var p = f.prototype;
10478     p.fields = new Roo.util.MixedCollection(false, function(field){
10479         return field.name;
10480     });
10481     for(var i = 0, len = o.length; i < len; i++){
10482         p.fields.add(new Roo.data.Field(o[i]));
10483     }
10484     f.getField = function(name){
10485         return p.fields.get(name);  
10486     };
10487     return f;
10488 };
10489
10490 Roo.data.Record.AUTO_ID = 1000;
10491 Roo.data.Record.EDIT = 'edit';
10492 Roo.data.Record.REJECT = 'reject';
10493 Roo.data.Record.COMMIT = 'commit';
10494
10495 Roo.data.Record.prototype = {
10496     /**
10497      * Readonly flag - true if this record has been modified.
10498      * @type Boolean
10499      */
10500     dirty : false,
10501     editing : false,
10502     error: null,
10503     modified: null,
10504
10505     // private
10506     join : function(store){
10507         this.store = store;
10508     },
10509
10510     /**
10511      * Set the named field to the specified value.
10512      * @param {String} name The name of the field to set.
10513      * @param {Object} value The value to set the field to.
10514      */
10515     set : function(name, value){
10516         if(this.data[name] == value){
10517             return;
10518         }
10519         this.dirty = true;
10520         if(!this.modified){
10521             this.modified = {};
10522         }
10523         if(typeof this.modified[name] == 'undefined'){
10524             this.modified[name] = this.data[name];
10525         }
10526         this.data[name] = value;
10527         if(!this.editing && this.store){
10528             this.store.afterEdit(this);
10529         }       
10530     },
10531
10532     /**
10533      * Get the value of the named field.
10534      * @param {String} name The name of the field to get the value of.
10535      * @return {Object} The value of the field.
10536      */
10537     get : function(name){
10538         return this.data[name]; 
10539     },
10540
10541     // private
10542     beginEdit : function(){
10543         this.editing = true;
10544         this.modified = {}; 
10545     },
10546
10547     // private
10548     cancelEdit : function(){
10549         this.editing = false;
10550         delete this.modified;
10551     },
10552
10553     // private
10554     endEdit : function(){
10555         this.editing = false;
10556         if(this.dirty && this.store){
10557             this.store.afterEdit(this);
10558         }
10559     },
10560
10561     /**
10562      * Usually called by the {@link Roo.data.Store} which owns the Record.
10563      * Rejects all changes made to the Record since either creation, or the last commit operation.
10564      * Modified fields are reverted to their original values.
10565      * <p>
10566      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10567      * of reject operations.
10568      */
10569     reject : function(){
10570         var m = this.modified;
10571         for(var n in m){
10572             if(typeof m[n] != "function"){
10573                 this.data[n] = m[n];
10574             }
10575         }
10576         this.dirty = false;
10577         delete this.modified;
10578         this.editing = false;
10579         if(this.store){
10580             this.store.afterReject(this);
10581         }
10582     },
10583
10584     /**
10585      * Usually called by the {@link Roo.data.Store} which owns the Record.
10586      * Commits all changes made to the Record since either creation, or the last commit operation.
10587      * <p>
10588      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10589      * of commit operations.
10590      */
10591     commit : function(){
10592         this.dirty = false;
10593         delete this.modified;
10594         this.editing = false;
10595         if(this.store){
10596             this.store.afterCommit(this);
10597         }
10598     },
10599
10600     // private
10601     hasError : function(){
10602         return this.error != null;
10603     },
10604
10605     // private
10606     clearError : function(){
10607         this.error = null;
10608     },
10609
10610     /**
10611      * Creates a copy of this record.
10612      * @param {String} id (optional) A new record id if you don't want to use this record's id
10613      * @return {Record}
10614      */
10615     copy : function(newId) {
10616         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10617     }
10618 };/*
10619  * Based on:
10620  * Ext JS Library 1.1.1
10621  * Copyright(c) 2006-2007, Ext JS, LLC.
10622  *
10623  * Originally Released Under LGPL - original licence link has changed is not relivant.
10624  *
10625  * Fork - LGPL
10626  * <script type="text/javascript">
10627  */
10628
10629
10630
10631 /**
10632  * @class Roo.data.Store
10633  * @extends Roo.util.Observable
10634  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10635  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10636  * <p>
10637  * 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
10638  * has no knowledge of the format of the data returned by the Proxy.<br>
10639  * <p>
10640  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10641  * instances from the data object. These records are cached and made available through accessor functions.
10642  * @constructor
10643  * Creates a new Store.
10644  * @param {Object} config A config object containing the objects needed for the Store to access data,
10645  * and read the data into Records.
10646  */
10647 Roo.data.Store = function(config){
10648     this.data = new Roo.util.MixedCollection(false);
10649     this.data.getKey = function(o){
10650         return o.id;
10651     };
10652     this.baseParams = {};
10653     // private
10654     this.paramNames = {
10655         "start" : "start",
10656         "limit" : "limit",
10657         "sort" : "sort",
10658         "dir" : "dir",
10659         "multisort" : "_multisort"
10660     };
10661
10662     if(config && config.data){
10663         this.inlineData = config.data;
10664         delete config.data;
10665     }
10666
10667     Roo.apply(this, config);
10668     
10669     if(this.reader){ // reader passed
10670         this.reader = Roo.factory(this.reader, Roo.data);
10671         this.reader.xmodule = this.xmodule || false;
10672         if(!this.recordType){
10673             this.recordType = this.reader.recordType;
10674         }
10675         if(this.reader.onMetaChange){
10676             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10677         }
10678     }
10679
10680     if(this.recordType){
10681         this.fields = this.recordType.prototype.fields;
10682     }
10683     this.modified = [];
10684
10685     this.addEvents({
10686         /**
10687          * @event datachanged
10688          * Fires when the data cache has changed, and a widget which is using this Store
10689          * as a Record cache should refresh its view.
10690          * @param {Store} this
10691          */
10692         datachanged : true,
10693         /**
10694          * @event metachange
10695          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10696          * @param {Store} this
10697          * @param {Object} meta The JSON metadata
10698          */
10699         metachange : true,
10700         /**
10701          * @event add
10702          * Fires when Records have been added to the Store
10703          * @param {Store} this
10704          * @param {Roo.data.Record[]} records The array of Records added
10705          * @param {Number} index The index at which the record(s) were added
10706          */
10707         add : true,
10708         /**
10709          * @event remove
10710          * Fires when a Record has been removed from the Store
10711          * @param {Store} this
10712          * @param {Roo.data.Record} record The Record that was removed
10713          * @param {Number} index The index at which the record was removed
10714          */
10715         remove : true,
10716         /**
10717          * @event update
10718          * Fires when a Record has been updated
10719          * @param {Store} this
10720          * @param {Roo.data.Record} record The Record that was updated
10721          * @param {String} operation The update operation being performed.  Value may be one of:
10722          * <pre><code>
10723  Roo.data.Record.EDIT
10724  Roo.data.Record.REJECT
10725  Roo.data.Record.COMMIT
10726          * </code></pre>
10727          */
10728         update : true,
10729         /**
10730          * @event clear
10731          * Fires when the data cache has been cleared.
10732          * @param {Store} this
10733          */
10734         clear : true,
10735         /**
10736          * @event beforeload
10737          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10738          * the load action will be canceled.
10739          * @param {Store} this
10740          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10741          */
10742         beforeload : true,
10743         /**
10744          * @event beforeloadadd
10745          * Fires after a new set of Records has been loaded.
10746          * @param {Store} this
10747          * @param {Roo.data.Record[]} records The Records that were loaded
10748          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10749          */
10750         beforeloadadd : true,
10751         /**
10752          * @event load
10753          * Fires after a new set of Records has been loaded, before they are added to the store.
10754          * @param {Store} this
10755          * @param {Roo.data.Record[]} records The Records that were loaded
10756          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10757          * @params {Object} return from reader
10758          */
10759         load : true,
10760         /**
10761          * @event loadexception
10762          * Fires if an exception occurs in the Proxy during loading.
10763          * Called with the signature of the Proxy's "loadexception" event.
10764          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10765          * 
10766          * @param {Proxy} 
10767          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10768          * @param {Object} load options 
10769          * @param {Object} jsonData from your request (normally this contains the Exception)
10770          */
10771         loadexception : true
10772     });
10773     
10774     if(this.proxy){
10775         this.proxy = Roo.factory(this.proxy, Roo.data);
10776         this.proxy.xmodule = this.xmodule || false;
10777         this.relayEvents(this.proxy,  ["loadexception"]);
10778     }
10779     this.sortToggle = {};
10780     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10781
10782     Roo.data.Store.superclass.constructor.call(this);
10783
10784     if(this.inlineData){
10785         this.loadData(this.inlineData);
10786         delete this.inlineData;
10787     }
10788 };
10789
10790 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10791      /**
10792     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10793     * without a remote query - used by combo/forms at present.
10794     */
10795     
10796     /**
10797     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10798     */
10799     /**
10800     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10801     */
10802     /**
10803     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10804     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10805     */
10806     /**
10807     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10808     * on any HTTP request
10809     */
10810     /**
10811     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10812     */
10813     /**
10814     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10815     */
10816     multiSort: false,
10817     /**
10818     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10819     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10820     */
10821     remoteSort : false,
10822
10823     /**
10824     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10825      * loaded or when a record is removed. (defaults to false).
10826     */
10827     pruneModifiedRecords : false,
10828
10829     // private
10830     lastOptions : null,
10831
10832     /**
10833      * Add Records to the Store and fires the add event.
10834      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10835      */
10836     add : function(records){
10837         records = [].concat(records);
10838         for(var i = 0, len = records.length; i < len; i++){
10839             records[i].join(this);
10840         }
10841         var index = this.data.length;
10842         this.data.addAll(records);
10843         this.fireEvent("add", this, records, index);
10844     },
10845
10846     /**
10847      * Remove a Record from the Store and fires the remove event.
10848      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10849      */
10850     remove : function(record){
10851         var index = this.data.indexOf(record);
10852         this.data.removeAt(index);
10853         if(this.pruneModifiedRecords){
10854             this.modified.remove(record);
10855         }
10856         this.fireEvent("remove", this, record, index);
10857     },
10858
10859     /**
10860      * Remove all Records from the Store and fires the clear event.
10861      */
10862     removeAll : function(){
10863         this.data.clear();
10864         if(this.pruneModifiedRecords){
10865             this.modified = [];
10866         }
10867         this.fireEvent("clear", this);
10868     },
10869
10870     /**
10871      * Inserts Records to the Store at the given index and fires the add event.
10872      * @param {Number} index The start index at which to insert the passed Records.
10873      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10874      */
10875     insert : function(index, records){
10876         records = [].concat(records);
10877         for(var i = 0, len = records.length; i < len; i++){
10878             this.data.insert(index, records[i]);
10879             records[i].join(this);
10880         }
10881         this.fireEvent("add", this, records, index);
10882     },
10883
10884     /**
10885      * Get the index within the cache of the passed Record.
10886      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10887      * @return {Number} The index of the passed Record. Returns -1 if not found.
10888      */
10889     indexOf : function(record){
10890         return this.data.indexOf(record);
10891     },
10892
10893     /**
10894      * Get the index within the cache of the Record with the passed id.
10895      * @param {String} id The id of the Record to find.
10896      * @return {Number} The index of the Record. Returns -1 if not found.
10897      */
10898     indexOfId : function(id){
10899         return this.data.indexOfKey(id);
10900     },
10901
10902     /**
10903      * Get the Record with the specified id.
10904      * @param {String} id The id of the Record to find.
10905      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10906      */
10907     getById : function(id){
10908         return this.data.key(id);
10909     },
10910
10911     /**
10912      * Get the Record at the specified index.
10913      * @param {Number} index The index of the Record to find.
10914      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10915      */
10916     getAt : function(index){
10917         return this.data.itemAt(index);
10918     },
10919
10920     /**
10921      * Returns a range of Records between specified indices.
10922      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10923      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10924      * @return {Roo.data.Record[]} An array of Records
10925      */
10926     getRange : function(start, end){
10927         return this.data.getRange(start, end);
10928     },
10929
10930     // private
10931     storeOptions : function(o){
10932         o = Roo.apply({}, o);
10933         delete o.callback;
10934         delete o.scope;
10935         this.lastOptions = o;
10936     },
10937
10938     /**
10939      * Loads the Record cache from the configured Proxy using the configured Reader.
10940      * <p>
10941      * If using remote paging, then the first load call must specify the <em>start</em>
10942      * and <em>limit</em> properties in the options.params property to establish the initial
10943      * position within the dataset, and the number of Records to cache on each read from the Proxy.
10944      * <p>
10945      * <strong>It is important to note that for remote data sources, loading is asynchronous,
10946      * and this call will return before the new data has been loaded. Perform any post-processing
10947      * in a callback function, or in a "load" event handler.</strong>
10948      * <p>
10949      * @param {Object} options An object containing properties which control loading options:<ul>
10950      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10951      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10952      * passed the following arguments:<ul>
10953      * <li>r : Roo.data.Record[]</li>
10954      * <li>options: Options object from the load call</li>
10955      * <li>success: Boolean success indicator</li></ul></li>
10956      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10957      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10958      * </ul>
10959      */
10960     load : function(options){
10961         options = options || {};
10962         if(this.fireEvent("beforeload", this, options) !== false){
10963             this.storeOptions(options);
10964             var p = Roo.apply(options.params || {}, this.baseParams);
10965             // if meta was not loaded from remote source.. try requesting it.
10966             if (!this.reader.metaFromRemote) {
10967                 p._requestMeta = 1;
10968             }
10969             if(this.sortInfo && this.remoteSort){
10970                 var pn = this.paramNames;
10971                 p[pn["sort"]] = this.sortInfo.field;
10972                 p[pn["dir"]] = this.sortInfo.direction;
10973             }
10974             if (this.multiSort) {
10975                 var pn = this.paramNames;
10976                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10977             }
10978             
10979             this.proxy.load(p, this.reader, this.loadRecords, this, options);
10980         }
10981     },
10982
10983     /**
10984      * Reloads the Record cache from the configured Proxy using the configured Reader and
10985      * the options from the last load operation performed.
10986      * @param {Object} options (optional) An object containing properties which may override the options
10987      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10988      * the most recently used options are reused).
10989      */
10990     reload : function(options){
10991         this.load(Roo.applyIf(options||{}, this.lastOptions));
10992     },
10993
10994     // private
10995     // Called as a callback by the Reader during a load operation.
10996     loadRecords : function(o, options, success){
10997         if(!o || success === false){
10998             if(success !== false){
10999                 this.fireEvent("load", this, [], options, o);
11000             }
11001             if(options.callback){
11002                 options.callback.call(options.scope || this, [], options, false);
11003             }
11004             return;
11005         }
11006         // if data returned failure - throw an exception.
11007         if (o.success === false) {
11008             // show a message if no listener is registered.
11009             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11010                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11011             }
11012             // loadmask wil be hooked into this..
11013             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11014             return;
11015         }
11016         var r = o.records, t = o.totalRecords || r.length;
11017         
11018         this.fireEvent("beforeloadadd", this, r, options, o);
11019         
11020         if(!options || options.add !== true){
11021             if(this.pruneModifiedRecords){
11022                 this.modified = [];
11023             }
11024             for(var i = 0, len = r.length; i < len; i++){
11025                 r[i].join(this);
11026             }
11027             if(this.snapshot){
11028                 this.data = this.snapshot;
11029                 delete this.snapshot;
11030             }
11031             this.data.clear();
11032             this.data.addAll(r);
11033             this.totalLength = t;
11034             this.applySort();
11035             this.fireEvent("datachanged", this);
11036         }else{
11037             this.totalLength = Math.max(t, this.data.length+r.length);
11038             this.add(r);
11039         }
11040         
11041         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11042                 
11043             var e = new Roo.data.Record({});
11044
11045             e.set(this.parent.displayField, this.parent.emptyTitle);
11046             e.set(this.parent.valueField, '');
11047
11048             this.insert(0, e);
11049         }
11050             
11051         this.fireEvent("load", this, r, options, o);
11052         if(options.callback){
11053             options.callback.call(options.scope || this, r, options, true);
11054         }
11055     },
11056
11057
11058     /**
11059      * Loads data from a passed data block. A Reader which understands the format of the data
11060      * must have been configured in the constructor.
11061      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11062      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11063      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11064      */
11065     loadData : function(o, append){
11066         var r = this.reader.readRecords(o);
11067         this.loadRecords(r, {add: append}, true);
11068     },
11069
11070     /**
11071      * Gets the number of cached records.
11072      * <p>
11073      * <em>If using paging, this may not be the total size of the dataset. If the data object
11074      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11075      * the data set size</em>
11076      */
11077     getCount : function(){
11078         return this.data.length || 0;
11079     },
11080
11081     /**
11082      * Gets the total number of records in the dataset as returned by the server.
11083      * <p>
11084      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11085      * the dataset size</em>
11086      */
11087     getTotalCount : function(){
11088         return this.totalLength || 0;
11089     },
11090
11091     /**
11092      * Returns the sort state of the Store as an object with two properties:
11093      * <pre><code>
11094  field {String} The name of the field by which the Records are sorted
11095  direction {String} The sort order, "ASC" or "DESC"
11096      * </code></pre>
11097      */
11098     getSortState : function(){
11099         return this.sortInfo;
11100     },
11101
11102     // private
11103     applySort : function(){
11104         if(this.sortInfo && !this.remoteSort){
11105             var s = this.sortInfo, f = s.field;
11106             var st = this.fields.get(f).sortType;
11107             var fn = function(r1, r2){
11108                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11109                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11110             };
11111             this.data.sort(s.direction, fn);
11112             if(this.snapshot && this.snapshot != this.data){
11113                 this.snapshot.sort(s.direction, fn);
11114             }
11115         }
11116     },
11117
11118     /**
11119      * Sets the default sort column and order to be used by the next load operation.
11120      * @param {String} fieldName The name of the field to sort by.
11121      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11122      */
11123     setDefaultSort : function(field, dir){
11124         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11125     },
11126
11127     /**
11128      * Sort the Records.
11129      * If remote sorting is used, the sort is performed on the server, and the cache is
11130      * reloaded. If local sorting is used, the cache is sorted internally.
11131      * @param {String} fieldName The name of the field to sort by.
11132      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11133      */
11134     sort : function(fieldName, dir){
11135         var f = this.fields.get(fieldName);
11136         if(!dir){
11137             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11138             
11139             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11140                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11141             }else{
11142                 dir = f.sortDir;
11143             }
11144         }
11145         this.sortToggle[f.name] = dir;
11146         this.sortInfo = {field: f.name, direction: dir};
11147         if(!this.remoteSort){
11148             this.applySort();
11149             this.fireEvent("datachanged", this);
11150         }else{
11151             this.load(this.lastOptions);
11152         }
11153     },
11154
11155     /**
11156      * Calls the specified function for each of the Records in the cache.
11157      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11158      * Returning <em>false</em> aborts and exits the iteration.
11159      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11160      */
11161     each : function(fn, scope){
11162         this.data.each(fn, scope);
11163     },
11164
11165     /**
11166      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11167      * (e.g., during paging).
11168      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11169      */
11170     getModifiedRecords : function(){
11171         return this.modified;
11172     },
11173
11174     // private
11175     createFilterFn : function(property, value, anyMatch){
11176         if(!value.exec){ // not a regex
11177             value = String(value);
11178             if(value.length == 0){
11179                 return false;
11180             }
11181             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11182         }
11183         return function(r){
11184             return value.test(r.data[property]);
11185         };
11186     },
11187
11188     /**
11189      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11190      * @param {String} property A field on your records
11191      * @param {Number} start The record index to start at (defaults to 0)
11192      * @param {Number} end The last record index to include (defaults to length - 1)
11193      * @return {Number} The sum
11194      */
11195     sum : function(property, start, end){
11196         var rs = this.data.items, v = 0;
11197         start = start || 0;
11198         end = (end || end === 0) ? end : rs.length-1;
11199
11200         for(var i = start; i <= end; i++){
11201             v += (rs[i].data[property] || 0);
11202         }
11203         return v;
11204     },
11205
11206     /**
11207      * Filter the records by a specified property.
11208      * @param {String} field A field on your records
11209      * @param {String/RegExp} value Either a string that the field
11210      * should start with or a RegExp to test against the field
11211      * @param {Boolean} anyMatch True to match any part not just the beginning
11212      */
11213     filter : function(property, value, anyMatch){
11214         var fn = this.createFilterFn(property, value, anyMatch);
11215         return fn ? this.filterBy(fn) : this.clearFilter();
11216     },
11217
11218     /**
11219      * Filter by a function. The specified function will be called with each
11220      * record in this data source. If the function returns true the record is included,
11221      * otherwise it is filtered.
11222      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11223      * @param {Object} scope (optional) The scope of the function (defaults to this)
11224      */
11225     filterBy : function(fn, scope){
11226         this.snapshot = this.snapshot || this.data;
11227         this.data = this.queryBy(fn, scope||this);
11228         this.fireEvent("datachanged", this);
11229     },
11230
11231     /**
11232      * Query the records by a specified property.
11233      * @param {String} field A field on your records
11234      * @param {String/RegExp} value Either a string that the field
11235      * should start with or a RegExp to test against the field
11236      * @param {Boolean} anyMatch True to match any part not just the beginning
11237      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11238      */
11239     query : function(property, value, anyMatch){
11240         var fn = this.createFilterFn(property, value, anyMatch);
11241         return fn ? this.queryBy(fn) : this.data.clone();
11242     },
11243
11244     /**
11245      * Query by a function. The specified function will be called with each
11246      * record in this data source. If the function returns true the record is included
11247      * in the results.
11248      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11249      * @param {Object} scope (optional) The scope of the function (defaults to this)
11250       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11251      **/
11252     queryBy : function(fn, scope){
11253         var data = this.snapshot || this.data;
11254         return data.filterBy(fn, scope||this);
11255     },
11256
11257     /**
11258      * Collects unique values for a particular dataIndex from this store.
11259      * @param {String} dataIndex The property to collect
11260      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11261      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11262      * @return {Array} An array of the unique values
11263      **/
11264     collect : function(dataIndex, allowNull, bypassFilter){
11265         var d = (bypassFilter === true && this.snapshot) ?
11266                 this.snapshot.items : this.data.items;
11267         var v, sv, r = [], l = {};
11268         for(var i = 0, len = d.length; i < len; i++){
11269             v = d[i].data[dataIndex];
11270             sv = String(v);
11271             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11272                 l[sv] = true;
11273                 r[r.length] = v;
11274             }
11275         }
11276         return r;
11277     },
11278
11279     /**
11280      * Revert to a view of the Record cache with no filtering applied.
11281      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11282      */
11283     clearFilter : function(suppressEvent){
11284         if(this.snapshot && this.snapshot != this.data){
11285             this.data = this.snapshot;
11286             delete this.snapshot;
11287             if(suppressEvent !== true){
11288                 this.fireEvent("datachanged", this);
11289             }
11290         }
11291     },
11292
11293     // private
11294     afterEdit : function(record){
11295         if(this.modified.indexOf(record) == -1){
11296             this.modified.push(record);
11297         }
11298         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11299     },
11300     
11301     // private
11302     afterReject : function(record){
11303         this.modified.remove(record);
11304         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11305     },
11306
11307     // private
11308     afterCommit : function(record){
11309         this.modified.remove(record);
11310         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11311     },
11312
11313     /**
11314      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11315      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11316      */
11317     commitChanges : function(){
11318         var m = this.modified.slice(0);
11319         this.modified = [];
11320         for(var i = 0, len = m.length; i < len; i++){
11321             m[i].commit();
11322         }
11323     },
11324
11325     /**
11326      * Cancel outstanding changes on all changed records.
11327      */
11328     rejectChanges : function(){
11329         var m = this.modified.slice(0);
11330         this.modified = [];
11331         for(var i = 0, len = m.length; i < len; i++){
11332             m[i].reject();
11333         }
11334     },
11335
11336     onMetaChange : function(meta, rtype, o){
11337         this.recordType = rtype;
11338         this.fields = rtype.prototype.fields;
11339         delete this.snapshot;
11340         this.sortInfo = meta.sortInfo || this.sortInfo;
11341         this.modified = [];
11342         this.fireEvent('metachange', this, this.reader.meta);
11343     },
11344     
11345     moveIndex : function(data, type)
11346     {
11347         var index = this.indexOf(data);
11348         
11349         var newIndex = index + type;
11350         
11351         this.remove(data);
11352         
11353         this.insert(newIndex, data);
11354         
11355     }
11356 });/*
11357  * Based on:
11358  * Ext JS Library 1.1.1
11359  * Copyright(c) 2006-2007, Ext JS, LLC.
11360  *
11361  * Originally Released Under LGPL - original licence link has changed is not relivant.
11362  *
11363  * Fork - LGPL
11364  * <script type="text/javascript">
11365  */
11366
11367 /**
11368  * @class Roo.data.SimpleStore
11369  * @extends Roo.data.Store
11370  * Small helper class to make creating Stores from Array data easier.
11371  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11372  * @cfg {Array} fields An array of field definition objects, or field name strings.
11373  * @cfg {Array} data The multi-dimensional array of data
11374  * @constructor
11375  * @param {Object} config
11376  */
11377 Roo.data.SimpleStore = function(config){
11378     Roo.data.SimpleStore.superclass.constructor.call(this, {
11379         isLocal : true,
11380         reader: new Roo.data.ArrayReader({
11381                 id: config.id
11382             },
11383             Roo.data.Record.create(config.fields)
11384         ),
11385         proxy : new Roo.data.MemoryProxy(config.data)
11386     });
11387     this.load();
11388 };
11389 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11390  * Based on:
11391  * Ext JS Library 1.1.1
11392  * Copyright(c) 2006-2007, Ext JS, LLC.
11393  *
11394  * Originally Released Under LGPL - original licence link has changed is not relivant.
11395  *
11396  * Fork - LGPL
11397  * <script type="text/javascript">
11398  */
11399
11400 /**
11401 /**
11402  * @extends Roo.data.Store
11403  * @class Roo.data.JsonStore
11404  * Small helper class to make creating Stores for JSON data easier. <br/>
11405 <pre><code>
11406 var store = new Roo.data.JsonStore({
11407     url: 'get-images.php',
11408     root: 'images',
11409     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11410 });
11411 </code></pre>
11412  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11413  * JsonReader and HttpProxy (unless inline data is provided).</b>
11414  * @cfg {Array} fields An array of field definition objects, or field name strings.
11415  * @constructor
11416  * @param {Object} config
11417  */
11418 Roo.data.JsonStore = function(c){
11419     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11420         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11421         reader: new Roo.data.JsonReader(c, c.fields)
11422     }));
11423 };
11424 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11425  * Based on:
11426  * Ext JS Library 1.1.1
11427  * Copyright(c) 2006-2007, Ext JS, LLC.
11428  *
11429  * Originally Released Under LGPL - original licence link has changed is not relivant.
11430  *
11431  * Fork - LGPL
11432  * <script type="text/javascript">
11433  */
11434
11435  
11436 Roo.data.Field = function(config){
11437     if(typeof config == "string"){
11438         config = {name: config};
11439     }
11440     Roo.apply(this, config);
11441     
11442     if(!this.type){
11443         this.type = "auto";
11444     }
11445     
11446     var st = Roo.data.SortTypes;
11447     // named sortTypes are supported, here we look them up
11448     if(typeof this.sortType == "string"){
11449         this.sortType = st[this.sortType];
11450     }
11451     
11452     // set default sortType for strings and dates
11453     if(!this.sortType){
11454         switch(this.type){
11455             case "string":
11456                 this.sortType = st.asUCString;
11457                 break;
11458             case "date":
11459                 this.sortType = st.asDate;
11460                 break;
11461             default:
11462                 this.sortType = st.none;
11463         }
11464     }
11465
11466     // define once
11467     var stripRe = /[\$,%]/g;
11468
11469     // prebuilt conversion function for this field, instead of
11470     // switching every time we're reading a value
11471     if(!this.convert){
11472         var cv, dateFormat = this.dateFormat;
11473         switch(this.type){
11474             case "":
11475             case "auto":
11476             case undefined:
11477                 cv = function(v){ return v; };
11478                 break;
11479             case "string":
11480                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11481                 break;
11482             case "int":
11483                 cv = function(v){
11484                     return v !== undefined && v !== null && v !== '' ?
11485                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11486                     };
11487                 break;
11488             case "float":
11489                 cv = function(v){
11490                     return v !== undefined && v !== null && v !== '' ?
11491                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11492                     };
11493                 break;
11494             case "bool":
11495             case "boolean":
11496                 cv = function(v){ return v === true || v === "true" || v == 1; };
11497                 break;
11498             case "date":
11499                 cv = function(v){
11500                     if(!v){
11501                         return '';
11502                     }
11503                     if(v instanceof Date){
11504                         return v;
11505                     }
11506                     if(dateFormat){
11507                         if(dateFormat == "timestamp"){
11508                             return new Date(v*1000);
11509                         }
11510                         return Date.parseDate(v, dateFormat);
11511                     }
11512                     var parsed = Date.parse(v);
11513                     return parsed ? new Date(parsed) : null;
11514                 };
11515              break;
11516             
11517         }
11518         this.convert = cv;
11519     }
11520 };
11521
11522 Roo.data.Field.prototype = {
11523     dateFormat: null,
11524     defaultValue: "",
11525     mapping: null,
11526     sortType : null,
11527     sortDir : "ASC"
11528 };/*
11529  * Based on:
11530  * Ext JS Library 1.1.1
11531  * Copyright(c) 2006-2007, Ext JS, LLC.
11532  *
11533  * Originally Released Under LGPL - original licence link has changed is not relivant.
11534  *
11535  * Fork - LGPL
11536  * <script type="text/javascript">
11537  */
11538  
11539 // Base class for reading structured data from a data source.  This class is intended to be
11540 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11541
11542 /**
11543  * @class Roo.data.DataReader
11544  * Base class for reading structured data from a data source.  This class is intended to be
11545  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11546  */
11547
11548 Roo.data.DataReader = function(meta, recordType){
11549     
11550     this.meta = meta;
11551     
11552     this.recordType = recordType instanceof Array ? 
11553         Roo.data.Record.create(recordType) : recordType;
11554 };
11555
11556 Roo.data.DataReader.prototype = {
11557      /**
11558      * Create an empty record
11559      * @param {Object} data (optional) - overlay some values
11560      * @return {Roo.data.Record} record created.
11561      */
11562     newRow :  function(d) {
11563         var da =  {};
11564         this.recordType.prototype.fields.each(function(c) {
11565             switch( c.type) {
11566                 case 'int' : da[c.name] = 0; break;
11567                 case 'date' : da[c.name] = new Date(); break;
11568                 case 'float' : da[c.name] = 0.0; break;
11569                 case 'boolean' : da[c.name] = false; break;
11570                 default : da[c.name] = ""; break;
11571             }
11572             
11573         });
11574         return new this.recordType(Roo.apply(da, d));
11575     }
11576     
11577 };/*
11578  * Based on:
11579  * Ext JS Library 1.1.1
11580  * Copyright(c) 2006-2007, Ext JS, LLC.
11581  *
11582  * Originally Released Under LGPL - original licence link has changed is not relivant.
11583  *
11584  * Fork - LGPL
11585  * <script type="text/javascript">
11586  */
11587
11588 /**
11589  * @class Roo.data.DataProxy
11590  * @extends Roo.data.Observable
11591  * This class is an abstract base class for implementations which provide retrieval of
11592  * unformatted data objects.<br>
11593  * <p>
11594  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11595  * (of the appropriate type which knows how to parse the data object) to provide a block of
11596  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11597  * <p>
11598  * Custom implementations must implement the load method as described in
11599  * {@link Roo.data.HttpProxy#load}.
11600  */
11601 Roo.data.DataProxy = function(){
11602     this.addEvents({
11603         /**
11604          * @event beforeload
11605          * Fires before a network request is made to retrieve a data object.
11606          * @param {Object} This DataProxy object.
11607          * @param {Object} params The params parameter to the load function.
11608          */
11609         beforeload : true,
11610         /**
11611          * @event load
11612          * Fires before the load method's callback is called.
11613          * @param {Object} This DataProxy object.
11614          * @param {Object} o The data object.
11615          * @param {Object} arg The callback argument object passed to the load function.
11616          */
11617         load : true,
11618         /**
11619          * @event loadexception
11620          * Fires if an Exception occurs during data retrieval.
11621          * @param {Object} This DataProxy object.
11622          * @param {Object} o The data object.
11623          * @param {Object} arg The callback argument object passed to the load function.
11624          * @param {Object} e The Exception.
11625          */
11626         loadexception : true
11627     });
11628     Roo.data.DataProxy.superclass.constructor.call(this);
11629 };
11630
11631 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11632
11633     /**
11634      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11635      */
11636 /*
11637  * Based on:
11638  * Ext JS Library 1.1.1
11639  * Copyright(c) 2006-2007, Ext JS, LLC.
11640  *
11641  * Originally Released Under LGPL - original licence link has changed is not relivant.
11642  *
11643  * Fork - LGPL
11644  * <script type="text/javascript">
11645  */
11646 /**
11647  * @class Roo.data.MemoryProxy
11648  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11649  * to the Reader when its load method is called.
11650  * @constructor
11651  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11652  */
11653 Roo.data.MemoryProxy = function(data){
11654     if (data.data) {
11655         data = data.data;
11656     }
11657     Roo.data.MemoryProxy.superclass.constructor.call(this);
11658     this.data = data;
11659 };
11660
11661 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11662     
11663     /**
11664      * Load data from the requested source (in this case an in-memory
11665      * data object passed to the constructor), read the data object into
11666      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11667      * process that block using the passed callback.
11668      * @param {Object} params This parameter is not used by the MemoryProxy class.
11669      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11670      * object into a block of Roo.data.Records.
11671      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11672      * The function must be passed <ul>
11673      * <li>The Record block object</li>
11674      * <li>The "arg" argument from the load function</li>
11675      * <li>A boolean success indicator</li>
11676      * </ul>
11677      * @param {Object} scope The scope in which to call the callback
11678      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11679      */
11680     load : function(params, reader, callback, scope, arg){
11681         params = params || {};
11682         var result;
11683         try {
11684             result = reader.readRecords(this.data);
11685         }catch(e){
11686             this.fireEvent("loadexception", this, arg, null, e);
11687             callback.call(scope, null, arg, false);
11688             return;
11689         }
11690         callback.call(scope, result, arg, true);
11691     },
11692     
11693     // private
11694     update : function(params, records){
11695         
11696     }
11697 });/*
11698  * Based on:
11699  * Ext JS Library 1.1.1
11700  * Copyright(c) 2006-2007, Ext JS, LLC.
11701  *
11702  * Originally Released Under LGPL - original licence link has changed is not relivant.
11703  *
11704  * Fork - LGPL
11705  * <script type="text/javascript">
11706  */
11707 /**
11708  * @class Roo.data.HttpProxy
11709  * @extends Roo.data.DataProxy
11710  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11711  * configured to reference a certain URL.<br><br>
11712  * <p>
11713  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11714  * from which the running page was served.<br><br>
11715  * <p>
11716  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11717  * <p>
11718  * Be aware that to enable the browser to parse an XML document, the server must set
11719  * the Content-Type header in the HTTP response to "text/xml".
11720  * @constructor
11721  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11722  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11723  * will be used to make the request.
11724  */
11725 Roo.data.HttpProxy = function(conn){
11726     Roo.data.HttpProxy.superclass.constructor.call(this);
11727     // is conn a conn config or a real conn?
11728     this.conn = conn;
11729     this.useAjax = !conn || !conn.events;
11730   
11731 };
11732
11733 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11734     // thse are take from connection...
11735     
11736     /**
11737      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11738      */
11739     /**
11740      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11741      * extra parameters to each request made by this object. (defaults to undefined)
11742      */
11743     /**
11744      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11745      *  to each request made by this object. (defaults to undefined)
11746      */
11747     /**
11748      * @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)
11749      */
11750     /**
11751      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11752      */
11753      /**
11754      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11755      * @type Boolean
11756      */
11757   
11758
11759     /**
11760      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11761      * @type Boolean
11762      */
11763     /**
11764      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11765      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11766      * a finer-grained basis than the DataProxy events.
11767      */
11768     getConnection : function(){
11769         return this.useAjax ? Roo.Ajax : this.conn;
11770     },
11771
11772     /**
11773      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11774      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11775      * process that block using the passed callback.
11776      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11777      * for the request to the remote server.
11778      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11779      * object into a block of Roo.data.Records.
11780      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11781      * The function must be passed <ul>
11782      * <li>The Record block object</li>
11783      * <li>The "arg" argument from the load function</li>
11784      * <li>A boolean success indicator</li>
11785      * </ul>
11786      * @param {Object} scope The scope in which to call the callback
11787      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11788      */
11789     load : function(params, reader, callback, scope, arg){
11790         if(this.fireEvent("beforeload", this, params) !== false){
11791             var  o = {
11792                 params : params || {},
11793                 request: {
11794                     callback : callback,
11795                     scope : scope,
11796                     arg : arg
11797                 },
11798                 reader: reader,
11799                 callback : this.loadResponse,
11800                 scope: this
11801             };
11802             if(this.useAjax){
11803                 Roo.applyIf(o, this.conn);
11804                 if(this.activeRequest){
11805                     Roo.Ajax.abort(this.activeRequest);
11806                 }
11807                 this.activeRequest = Roo.Ajax.request(o);
11808             }else{
11809                 this.conn.request(o);
11810             }
11811         }else{
11812             callback.call(scope||this, null, arg, false);
11813         }
11814     },
11815
11816     // private
11817     loadResponse : function(o, success, response){
11818         delete this.activeRequest;
11819         if(!success){
11820             this.fireEvent("loadexception", this, o, response);
11821             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11822             return;
11823         }
11824         var result;
11825         try {
11826             result = o.reader.read(response);
11827         }catch(e){
11828             this.fireEvent("loadexception", this, o, response, e);
11829             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11830             return;
11831         }
11832         
11833         this.fireEvent("load", this, o, o.request.arg);
11834         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11835     },
11836
11837     // private
11838     update : function(dataSet){
11839
11840     },
11841
11842     // private
11843     updateResponse : function(dataSet){
11844
11845     }
11846 });/*
11847  * Based on:
11848  * Ext JS Library 1.1.1
11849  * Copyright(c) 2006-2007, Ext JS, LLC.
11850  *
11851  * Originally Released Under LGPL - original licence link has changed is not relivant.
11852  *
11853  * Fork - LGPL
11854  * <script type="text/javascript">
11855  */
11856
11857 /**
11858  * @class Roo.data.ScriptTagProxy
11859  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11860  * other than the originating domain of the running page.<br><br>
11861  * <p>
11862  * <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
11863  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11864  * <p>
11865  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11866  * source code that is used as the source inside a &lt;script> tag.<br><br>
11867  * <p>
11868  * In order for the browser to process the returned data, the server must wrap the data object
11869  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11870  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11871  * depending on whether the callback name was passed:
11872  * <p>
11873  * <pre><code>
11874 boolean scriptTag = false;
11875 String cb = request.getParameter("callback");
11876 if (cb != null) {
11877     scriptTag = true;
11878     response.setContentType("text/javascript");
11879 } else {
11880     response.setContentType("application/x-json");
11881 }
11882 Writer out = response.getWriter();
11883 if (scriptTag) {
11884     out.write(cb + "(");
11885 }
11886 out.print(dataBlock.toJsonString());
11887 if (scriptTag) {
11888     out.write(");");
11889 }
11890 </pre></code>
11891  *
11892  * @constructor
11893  * @param {Object} config A configuration object.
11894  */
11895 Roo.data.ScriptTagProxy = function(config){
11896     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11897     Roo.apply(this, config);
11898     this.head = document.getElementsByTagName("head")[0];
11899 };
11900
11901 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11902
11903 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11904     /**
11905      * @cfg {String} url The URL from which to request the data object.
11906      */
11907     /**
11908      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11909      */
11910     timeout : 30000,
11911     /**
11912      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11913      * the server the name of the callback function set up by the load call to process the returned data object.
11914      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11915      * javascript output which calls this named function passing the data object as its only parameter.
11916      */
11917     callbackParam : "callback",
11918     /**
11919      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11920      * name to the request.
11921      */
11922     nocache : true,
11923
11924     /**
11925      * Load data from the configured URL, read the data object into
11926      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11927      * process that block using the passed callback.
11928      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11929      * for the request to the remote server.
11930      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11931      * object into a block of Roo.data.Records.
11932      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11933      * The function must be passed <ul>
11934      * <li>The Record block object</li>
11935      * <li>The "arg" argument from the load function</li>
11936      * <li>A boolean success indicator</li>
11937      * </ul>
11938      * @param {Object} scope The scope in which to call the callback
11939      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11940      */
11941     load : function(params, reader, callback, scope, arg){
11942         if(this.fireEvent("beforeload", this, params) !== false){
11943
11944             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11945
11946             var url = this.url;
11947             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11948             if(this.nocache){
11949                 url += "&_dc=" + (new Date().getTime());
11950             }
11951             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11952             var trans = {
11953                 id : transId,
11954                 cb : "stcCallback"+transId,
11955                 scriptId : "stcScript"+transId,
11956                 params : params,
11957                 arg : arg,
11958                 url : url,
11959                 callback : callback,
11960                 scope : scope,
11961                 reader : reader
11962             };
11963             var conn = this;
11964
11965             window[trans.cb] = function(o){
11966                 conn.handleResponse(o, trans);
11967             };
11968
11969             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11970
11971             if(this.autoAbort !== false){
11972                 this.abort();
11973             }
11974
11975             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11976
11977             var script = document.createElement("script");
11978             script.setAttribute("src", url);
11979             script.setAttribute("type", "text/javascript");
11980             script.setAttribute("id", trans.scriptId);
11981             this.head.appendChild(script);
11982
11983             this.trans = trans;
11984         }else{
11985             callback.call(scope||this, null, arg, false);
11986         }
11987     },
11988
11989     // private
11990     isLoading : function(){
11991         return this.trans ? true : false;
11992     },
11993
11994     /**
11995      * Abort the current server request.
11996      */
11997     abort : function(){
11998         if(this.isLoading()){
11999             this.destroyTrans(this.trans);
12000         }
12001     },
12002
12003     // private
12004     destroyTrans : function(trans, isLoaded){
12005         this.head.removeChild(document.getElementById(trans.scriptId));
12006         clearTimeout(trans.timeoutId);
12007         if(isLoaded){
12008             window[trans.cb] = undefined;
12009             try{
12010                 delete window[trans.cb];
12011             }catch(e){}
12012         }else{
12013             // if hasn't been loaded, wait for load to remove it to prevent script error
12014             window[trans.cb] = function(){
12015                 window[trans.cb] = undefined;
12016                 try{
12017                     delete window[trans.cb];
12018                 }catch(e){}
12019             };
12020         }
12021     },
12022
12023     // private
12024     handleResponse : function(o, trans){
12025         this.trans = false;
12026         this.destroyTrans(trans, true);
12027         var result;
12028         try {
12029             result = trans.reader.readRecords(o);
12030         }catch(e){
12031             this.fireEvent("loadexception", this, o, trans.arg, e);
12032             trans.callback.call(trans.scope||window, null, trans.arg, false);
12033             return;
12034         }
12035         this.fireEvent("load", this, o, trans.arg);
12036         trans.callback.call(trans.scope||window, result, trans.arg, true);
12037     },
12038
12039     // private
12040     handleFailure : function(trans){
12041         this.trans = false;
12042         this.destroyTrans(trans, false);
12043         this.fireEvent("loadexception", this, null, trans.arg);
12044         trans.callback.call(trans.scope||window, null, trans.arg, false);
12045     }
12046 });/*
12047  * Based on:
12048  * Ext JS Library 1.1.1
12049  * Copyright(c) 2006-2007, Ext JS, LLC.
12050  *
12051  * Originally Released Under LGPL - original licence link has changed is not relivant.
12052  *
12053  * Fork - LGPL
12054  * <script type="text/javascript">
12055  */
12056
12057 /**
12058  * @class Roo.data.JsonReader
12059  * @extends Roo.data.DataReader
12060  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12061  * based on mappings in a provided Roo.data.Record constructor.
12062  * 
12063  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12064  * in the reply previously. 
12065  * 
12066  * <p>
12067  * Example code:
12068  * <pre><code>
12069 var RecordDef = Roo.data.Record.create([
12070     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12071     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12072 ]);
12073 var myReader = new Roo.data.JsonReader({
12074     totalProperty: "results",    // The property which contains the total dataset size (optional)
12075     root: "rows",                // The property which contains an Array of row objects
12076     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12077 }, RecordDef);
12078 </code></pre>
12079  * <p>
12080  * This would consume a JSON file like this:
12081  * <pre><code>
12082 { 'results': 2, 'rows': [
12083     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12084     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12085 }
12086 </code></pre>
12087  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12088  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12089  * paged from the remote server.
12090  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12091  * @cfg {String} root name of the property which contains the Array of row objects.
12092  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12093  * @cfg {Array} fields Array of field definition objects
12094  * @constructor
12095  * Create a new JsonReader
12096  * @param {Object} meta Metadata configuration options
12097  * @param {Object} recordType Either an Array of field definition objects,
12098  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12099  */
12100 Roo.data.JsonReader = function(meta, recordType){
12101     
12102     meta = meta || {};
12103     // set some defaults:
12104     Roo.applyIf(meta, {
12105         totalProperty: 'total',
12106         successProperty : 'success',
12107         root : 'data',
12108         id : 'id'
12109     });
12110     
12111     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12112 };
12113 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12114     
12115     /**
12116      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12117      * Used by Store query builder to append _requestMeta to params.
12118      * 
12119      */
12120     metaFromRemote : false,
12121     /**
12122      * This method is only used by a DataProxy which has retrieved data from a remote server.
12123      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12124      * @return {Object} data A data block which is used by an Roo.data.Store object as
12125      * a cache of Roo.data.Records.
12126      */
12127     read : function(response){
12128         var json = response.responseText;
12129        
12130         var o = /* eval:var:o */ eval("("+json+")");
12131         if(!o) {
12132             throw {message: "JsonReader.read: Json object not found"};
12133         }
12134         
12135         if(o.metaData){
12136             
12137             delete this.ef;
12138             this.metaFromRemote = true;
12139             this.meta = o.metaData;
12140             this.recordType = Roo.data.Record.create(o.metaData.fields);
12141             this.onMetaChange(this.meta, this.recordType, o);
12142         }
12143         return this.readRecords(o);
12144     },
12145
12146     // private function a store will implement
12147     onMetaChange : function(meta, recordType, o){
12148
12149     },
12150
12151     /**
12152          * @ignore
12153          */
12154     simpleAccess: function(obj, subsc) {
12155         return obj[subsc];
12156     },
12157
12158         /**
12159          * @ignore
12160          */
12161     getJsonAccessor: function(){
12162         var re = /[\[\.]/;
12163         return function(expr) {
12164             try {
12165                 return(re.test(expr))
12166                     ? new Function("obj", "return obj." + expr)
12167                     : function(obj){
12168                         return obj[expr];
12169                     };
12170             } catch(e){}
12171             return Roo.emptyFn;
12172         };
12173     }(),
12174
12175     /**
12176      * Create a data block containing Roo.data.Records from an XML document.
12177      * @param {Object} o An object which contains an Array of row objects in the property specified
12178      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12179      * which contains the total size of the dataset.
12180      * @return {Object} data A data block which is used by an Roo.data.Store object as
12181      * a cache of Roo.data.Records.
12182      */
12183     readRecords : function(o){
12184         /**
12185          * After any data loads, the raw JSON data is available for further custom processing.
12186          * @type Object
12187          */
12188         this.o = o;
12189         var s = this.meta, Record = this.recordType,
12190             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12191
12192 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12193         if (!this.ef) {
12194             if(s.totalProperty) {
12195                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12196                 }
12197                 if(s.successProperty) {
12198                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12199                 }
12200                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12201                 if (s.id) {
12202                         var g = this.getJsonAccessor(s.id);
12203                         this.getId = function(rec) {
12204                                 var r = g(rec);  
12205                                 return (r === undefined || r === "") ? null : r;
12206                         };
12207                 } else {
12208                         this.getId = function(){return null;};
12209                 }
12210             this.ef = [];
12211             for(var jj = 0; jj < fl; jj++){
12212                 f = fi[jj];
12213                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12214                 this.ef[jj] = this.getJsonAccessor(map);
12215             }
12216         }
12217
12218         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12219         if(s.totalProperty){
12220             var vt = parseInt(this.getTotal(o), 10);
12221             if(!isNaN(vt)){
12222                 totalRecords = vt;
12223             }
12224         }
12225         if(s.successProperty){
12226             var vs = this.getSuccess(o);
12227             if(vs === false || vs === 'false'){
12228                 success = false;
12229             }
12230         }
12231         var records = [];
12232         for(var i = 0; i < c; i++){
12233                 var n = root[i];
12234             var values = {};
12235             var id = this.getId(n);
12236             for(var j = 0; j < fl; j++){
12237                 f = fi[j];
12238             var v = this.ef[j](n);
12239             if (!f.convert) {
12240                 Roo.log('missing convert for ' + f.name);
12241                 Roo.log(f);
12242                 continue;
12243             }
12244             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12245             }
12246             var record = new Record(values, id);
12247             record.json = n;
12248             records[i] = record;
12249         }
12250         return {
12251             raw : o,
12252             success : success,
12253             records : records,
12254             totalRecords : totalRecords
12255         };
12256     }
12257 });/*
12258  * Based on:
12259  * Ext JS Library 1.1.1
12260  * Copyright(c) 2006-2007, Ext JS, LLC.
12261  *
12262  * Originally Released Under LGPL - original licence link has changed is not relivant.
12263  *
12264  * Fork - LGPL
12265  * <script type="text/javascript">
12266  */
12267
12268 /**
12269  * @class Roo.data.ArrayReader
12270  * @extends Roo.data.DataReader
12271  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12272  * Each element of that Array represents a row of data fields. The
12273  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12274  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12275  * <p>
12276  * Example code:.
12277  * <pre><code>
12278 var RecordDef = Roo.data.Record.create([
12279     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12280     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12281 ]);
12282 var myReader = new Roo.data.ArrayReader({
12283     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12284 }, RecordDef);
12285 </code></pre>
12286  * <p>
12287  * This would consume an Array like this:
12288  * <pre><code>
12289 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12290   </code></pre>
12291  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12292  * @constructor
12293  * Create a new JsonReader
12294  * @param {Object} meta Metadata configuration options.
12295  * @param {Object} recordType Either an Array of field definition objects
12296  * as specified to {@link Roo.data.Record#create},
12297  * or an {@link Roo.data.Record} object
12298  * created using {@link Roo.data.Record#create}.
12299  */
12300 Roo.data.ArrayReader = function(meta, recordType){
12301     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12302 };
12303
12304 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12305     /**
12306      * Create a data block containing Roo.data.Records from an XML document.
12307      * @param {Object} o An Array of row objects which represents the dataset.
12308      * @return {Object} data A data block which is used by an Roo.data.Store object as
12309      * a cache of Roo.data.Records.
12310      */
12311     readRecords : function(o){
12312         var sid = this.meta ? this.meta.id : null;
12313         var recordType = this.recordType, fields = recordType.prototype.fields;
12314         var records = [];
12315         var root = o;
12316             for(var i = 0; i < root.length; i++){
12317                     var n = root[i];
12318                 var values = {};
12319                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12320                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12321                 var f = fields.items[j];
12322                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12323                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12324                 v = f.convert(v);
12325                 values[f.name] = v;
12326             }
12327                 var record = new recordType(values, id);
12328                 record.json = n;
12329                 records[records.length] = record;
12330             }
12331             return {
12332                 records : records,
12333                 totalRecords : records.length
12334             };
12335     }
12336 });/*
12337  * - LGPL
12338  * * 
12339  */
12340
12341 /**
12342  * @class Roo.bootstrap.ComboBox
12343  * @extends Roo.bootstrap.TriggerField
12344  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12345  * @cfg {Boolean} append (true|false) default false
12346  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12347  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12348  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12349  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12350  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12351  * @cfg {Boolean} animate default true
12352  * @cfg {Boolean} emptyResultText only for touch device
12353  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12354  * @cfg {String} emptyTitle default ''
12355  * @constructor
12356  * Create a new ComboBox.
12357  * @param {Object} config Configuration options
12358  */
12359 Roo.bootstrap.ComboBox = function(config){
12360     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12361     this.addEvents({
12362         /**
12363          * @event expand
12364          * Fires when the dropdown list is expanded
12365              * @param {Roo.bootstrap.ComboBox} combo This combo box
12366              */
12367         'expand' : true,
12368         /**
12369          * @event collapse
12370          * Fires when the dropdown list is collapsed
12371              * @param {Roo.bootstrap.ComboBox} combo This combo box
12372              */
12373         'collapse' : true,
12374         /**
12375          * @event beforeselect
12376          * Fires before a list item is selected. Return false to cancel the selection.
12377              * @param {Roo.bootstrap.ComboBox} combo This combo box
12378              * @param {Roo.data.Record} record The data record returned from the underlying store
12379              * @param {Number} index The index of the selected item in the dropdown list
12380              */
12381         'beforeselect' : true,
12382         /**
12383          * @event select
12384          * Fires when a list item is selected
12385              * @param {Roo.bootstrap.ComboBox} combo This combo box
12386              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12387              * @param {Number} index The index of the selected item in the dropdown list
12388              */
12389         'select' : true,
12390         /**
12391          * @event beforequery
12392          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12393          * The event object passed has these properties:
12394              * @param {Roo.bootstrap.ComboBox} combo This combo box
12395              * @param {String} query The query
12396              * @param {Boolean} forceAll true to force "all" query
12397              * @param {Boolean} cancel true to cancel the query
12398              * @param {Object} e The query event object
12399              */
12400         'beforequery': true,
12401          /**
12402          * @event add
12403          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12404              * @param {Roo.bootstrap.ComboBox} combo This combo box
12405              */
12406         'add' : true,
12407         /**
12408          * @event edit
12409          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12410              * @param {Roo.bootstrap.ComboBox} combo This combo box
12411              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12412              */
12413         'edit' : true,
12414         /**
12415          * @event remove
12416          * Fires when the remove value from the combobox array
12417              * @param {Roo.bootstrap.ComboBox} combo This combo box
12418              */
12419         'remove' : true,
12420         /**
12421          * @event afterremove
12422          * Fires when the remove value from the combobox array
12423              * @param {Roo.bootstrap.ComboBox} combo This combo box
12424              */
12425         'afterremove' : true,
12426         /**
12427          * @event specialfilter
12428          * Fires when specialfilter
12429             * @param {Roo.bootstrap.ComboBox} combo This combo box
12430             */
12431         'specialfilter' : true,
12432         /**
12433          * @event tick
12434          * Fires when tick the element
12435             * @param {Roo.bootstrap.ComboBox} combo This combo box
12436             */
12437         'tick' : true,
12438         /**
12439          * @event touchviewdisplay
12440          * Fires when touch view require special display (default is using displayField)
12441             * @param {Roo.bootstrap.ComboBox} combo This combo box
12442             * @param {Object} cfg set html .
12443             */
12444         'touchviewdisplay' : true
12445         
12446     });
12447     
12448     this.item = [];
12449     this.tickItems = [];
12450     
12451     this.selectedIndex = -1;
12452     if(this.mode == 'local'){
12453         if(config.queryDelay === undefined){
12454             this.queryDelay = 10;
12455         }
12456         if(config.minChars === undefined){
12457             this.minChars = 0;
12458         }
12459     }
12460 };
12461
12462 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12463      
12464     /**
12465      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12466      * rendering into an Roo.Editor, defaults to false)
12467      */
12468     /**
12469      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12470      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12471      */
12472     /**
12473      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12474      */
12475     /**
12476      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12477      * the dropdown list (defaults to undefined, with no header element)
12478      */
12479
12480      /**
12481      * @cfg {String/Roo.Template} tpl The template to use to render the output
12482      */
12483      
12484      /**
12485      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12486      */
12487     listWidth: undefined,
12488     /**
12489      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12490      * mode = 'remote' or 'text' if mode = 'local')
12491      */
12492     displayField: undefined,
12493     
12494     /**
12495      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12496      * mode = 'remote' or 'value' if mode = 'local'). 
12497      * Note: use of a valueField requires the user make a selection
12498      * in order for a value to be mapped.
12499      */
12500     valueField: undefined,
12501     /**
12502      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12503      */
12504     modalTitle : '',
12505     
12506     /**
12507      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12508      * field's data value (defaults to the underlying DOM element's name)
12509      */
12510     hiddenName: undefined,
12511     /**
12512      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12513      */
12514     listClass: '',
12515     /**
12516      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12517      */
12518     selectedClass: 'active',
12519     
12520     /**
12521      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12522      */
12523     shadow:'sides',
12524     /**
12525      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12526      * anchor positions (defaults to 'tl-bl')
12527      */
12528     listAlign: 'tl-bl?',
12529     /**
12530      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12531      */
12532     maxHeight: 300,
12533     /**
12534      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12535      * query specified by the allQuery config option (defaults to 'query')
12536      */
12537     triggerAction: 'query',
12538     /**
12539      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12540      * (defaults to 4, does not apply if editable = false)
12541      */
12542     minChars : 4,
12543     /**
12544      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12545      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12546      */
12547     typeAhead: false,
12548     /**
12549      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12550      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12551      */
12552     queryDelay: 500,
12553     /**
12554      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12555      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12556      */
12557     pageSize: 0,
12558     /**
12559      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12560      * when editable = true (defaults to false)
12561      */
12562     selectOnFocus:false,
12563     /**
12564      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12565      */
12566     queryParam: 'query',
12567     /**
12568      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12569      * when mode = 'remote' (defaults to 'Loading...')
12570      */
12571     loadingText: 'Loading...',
12572     /**
12573      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12574      */
12575     resizable: false,
12576     /**
12577      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12578      */
12579     handleHeight : 8,
12580     /**
12581      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12582      * traditional select (defaults to true)
12583      */
12584     editable: true,
12585     /**
12586      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12587      */
12588     allQuery: '',
12589     /**
12590      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12591      */
12592     mode: 'remote',
12593     /**
12594      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12595      * listWidth has a higher value)
12596      */
12597     minListWidth : 70,
12598     /**
12599      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12600      * allow the user to set arbitrary text into the field (defaults to false)
12601      */
12602     forceSelection:false,
12603     /**
12604      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12605      * if typeAhead = true (defaults to 250)
12606      */
12607     typeAheadDelay : 250,
12608     /**
12609      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12610      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12611      */
12612     valueNotFoundText : undefined,
12613     /**
12614      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12615      */
12616     blockFocus : false,
12617     
12618     /**
12619      * @cfg {Boolean} disableClear Disable showing of clear button.
12620      */
12621     disableClear : false,
12622     /**
12623      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12624      */
12625     alwaysQuery : false,
12626     
12627     /**
12628      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12629      */
12630     multiple : false,
12631     
12632     /**
12633      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12634      */
12635     invalidClass : "has-warning",
12636     
12637     /**
12638      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12639      */
12640     validClass : "has-success",
12641     
12642     /**
12643      * @cfg {Boolean} specialFilter (true|false) special filter default false
12644      */
12645     specialFilter : false,
12646     
12647     /**
12648      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12649      */
12650     mobileTouchView : true,
12651     
12652     /**
12653      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12654      */
12655     useNativeIOS : false,
12656     
12657     ios_options : false,
12658     
12659     //private
12660     addicon : false,
12661     editicon: false,
12662     
12663     page: 0,
12664     hasQuery: false,
12665     append: false,
12666     loadNext: false,
12667     autoFocus : true,
12668     tickable : false,
12669     btnPosition : 'right',
12670     triggerList : true,
12671     showToggleBtn : true,
12672     animate : true,
12673     emptyResultText: 'Empty',
12674     triggerText : 'Select',
12675     emptyTitle : '',
12676     
12677     // element that contains real text value.. (when hidden is used..)
12678     
12679     getAutoCreate : function()
12680     {   
12681         var cfg = false;
12682         //render
12683         /*
12684          * Render classic select for iso
12685          */
12686         
12687         if(Roo.isIOS && this.useNativeIOS){
12688             cfg = this.getAutoCreateNativeIOS();
12689             return cfg;
12690         }
12691         
12692         /*
12693          * Touch Devices
12694          */
12695         
12696         if(Roo.isTouch && this.mobileTouchView){
12697             cfg = this.getAutoCreateTouchView();
12698             return cfg;;
12699         }
12700         
12701         /*
12702          *  Normal ComboBox
12703          */
12704         if(!this.tickable){
12705             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12706             return cfg;
12707         }
12708         
12709         /*
12710          *  ComboBox with tickable selections
12711          */
12712              
12713         var align = this.labelAlign || this.parentLabelAlign();
12714         
12715         cfg = {
12716             cls : 'form-group roo-combobox-tickable' //input-group
12717         };
12718         
12719         var btn_text_select = '';
12720         var btn_text_done = '';
12721         var btn_text_cancel = '';
12722         
12723         if (this.btn_text_show) {
12724             btn_text_select = 'Select';
12725             btn_text_done = 'Done';
12726             btn_text_cancel = 'Cancel'; 
12727         }
12728         
12729         var buttons = {
12730             tag : 'div',
12731             cls : 'tickable-buttons',
12732             cn : [
12733                 {
12734                     tag : 'button',
12735                     type : 'button',
12736                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12737                     //html : this.triggerText
12738                     html: btn_text_select
12739                 },
12740                 {
12741                     tag : 'button',
12742                     type : 'button',
12743                     name : 'ok',
12744                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12745                     //html : 'Done'
12746                     html: btn_text_done
12747                 },
12748                 {
12749                     tag : 'button',
12750                     type : 'button',
12751                     name : 'cancel',
12752                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12753                     //html : 'Cancel'
12754                     html: btn_text_cancel
12755                 }
12756             ]
12757         };
12758         
12759         if(this.editable){
12760             buttons.cn.unshift({
12761                 tag: 'input',
12762                 cls: 'roo-select2-search-field-input'
12763             });
12764         }
12765         
12766         var _this = this;
12767         
12768         Roo.each(buttons.cn, function(c){
12769             if (_this.size) {
12770                 c.cls += ' btn-' + _this.size;
12771             }
12772
12773             if (_this.disabled) {
12774                 c.disabled = true;
12775             }
12776         });
12777         
12778         var box = {
12779             tag: 'div',
12780             cn: [
12781                 {
12782                     tag: 'input',
12783                     type : 'hidden',
12784                     cls: 'form-hidden-field'
12785                 },
12786                 {
12787                     tag: 'ul',
12788                     cls: 'roo-select2-choices',
12789                     cn:[
12790                         {
12791                             tag: 'li',
12792                             cls: 'roo-select2-search-field',
12793                             cn: [
12794                                 buttons
12795                             ]
12796                         }
12797                     ]
12798                 }
12799             ]
12800         };
12801         
12802         var combobox = {
12803             cls: 'roo-select2-container input-group roo-select2-container-multi',
12804             cn: [
12805                 box
12806 //                {
12807 //                    tag: 'ul',
12808 //                    cls: 'typeahead typeahead-long dropdown-menu',
12809 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12810 //                }
12811             ]
12812         };
12813         
12814         if(this.hasFeedback && !this.allowBlank){
12815             
12816             var feedback = {
12817                 tag: 'span',
12818                 cls: 'glyphicon form-control-feedback'
12819             };
12820
12821             combobox.cn.push(feedback);
12822         }
12823         
12824         
12825         if (align ==='left' && this.fieldLabel.length) {
12826             
12827             cfg.cls += ' roo-form-group-label-left';
12828             
12829             cfg.cn = [
12830                 {
12831                     tag : 'i',
12832                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12833                     tooltip : 'This field is required'
12834                 },
12835                 {
12836                     tag: 'label',
12837                     'for' :  id,
12838                     cls : 'control-label',
12839                     html : this.fieldLabel
12840
12841                 },
12842                 {
12843                     cls : "", 
12844                     cn: [
12845                         combobox
12846                     ]
12847                 }
12848
12849             ];
12850             
12851             var labelCfg = cfg.cn[1];
12852             var contentCfg = cfg.cn[2];
12853             
12854
12855             if(this.indicatorpos == 'right'){
12856                 
12857                 cfg.cn = [
12858                     {
12859                         tag: 'label',
12860                         'for' :  id,
12861                         cls : 'control-label',
12862                         cn : [
12863                             {
12864                                 tag : 'span',
12865                                 html : this.fieldLabel
12866                             },
12867                             {
12868                                 tag : 'i',
12869                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12870                                 tooltip : 'This field is required'
12871                             }
12872                         ]
12873                     },
12874                     {
12875                         cls : "",
12876                         cn: [
12877                             combobox
12878                         ]
12879                     }
12880
12881                 ];
12882                 
12883                 
12884                 
12885                 labelCfg = cfg.cn[0];
12886                 contentCfg = cfg.cn[1];
12887             
12888             }
12889             
12890             if(this.labelWidth > 12){
12891                 labelCfg.style = "width: " + this.labelWidth + 'px';
12892             }
12893             
12894             if(this.labelWidth < 13 && this.labelmd == 0){
12895                 this.labelmd = this.labelWidth;
12896             }
12897             
12898             if(this.labellg > 0){
12899                 labelCfg.cls += ' col-lg-' + this.labellg;
12900                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12901             }
12902             
12903             if(this.labelmd > 0){
12904                 labelCfg.cls += ' col-md-' + this.labelmd;
12905                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12906             }
12907             
12908             if(this.labelsm > 0){
12909                 labelCfg.cls += ' col-sm-' + this.labelsm;
12910                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12911             }
12912             
12913             if(this.labelxs > 0){
12914                 labelCfg.cls += ' col-xs-' + this.labelxs;
12915                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12916             }
12917                 
12918                 
12919         } else if ( this.fieldLabel.length) {
12920 //                Roo.log(" label");
12921                  cfg.cn = [
12922                     {
12923                         tag : 'i',
12924                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12925                         tooltip : 'This field is required'
12926                     },
12927                     {
12928                         tag: 'label',
12929                         //cls : 'input-group-addon',
12930                         html : this.fieldLabel
12931                     },
12932                     combobox
12933                 ];
12934                 
12935                 if(this.indicatorpos == 'right'){
12936                     cfg.cn = [
12937                         {
12938                             tag: 'label',
12939                             //cls : 'input-group-addon',
12940                             html : this.fieldLabel
12941                         },
12942                         {
12943                             tag : 'i',
12944                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12945                             tooltip : 'This field is required'
12946                         },
12947                         combobox
12948                     ];
12949                     
12950                 }
12951
12952         } else {
12953             
12954 //                Roo.log(" no label && no align");
12955                 cfg = combobox
12956                      
12957                 
12958         }
12959          
12960         var settings=this;
12961         ['xs','sm','md','lg'].map(function(size){
12962             if (settings[size]) {
12963                 cfg.cls += ' col-' + size + '-' + settings[size];
12964             }
12965         });
12966         
12967         return cfg;
12968         
12969     },
12970     
12971     _initEventsCalled : false,
12972     
12973     // private
12974     initEvents: function()
12975     {   
12976         if (this._initEventsCalled) { // as we call render... prevent looping...
12977             return;
12978         }
12979         this._initEventsCalled = true;
12980         
12981         if (!this.store) {
12982             throw "can not find store for combo";
12983         }
12984         
12985         this.store = Roo.factory(this.store, Roo.data);
12986         this.store.parent = this;
12987         
12988         // if we are building from html. then this element is so complex, that we can not really
12989         // use the rendered HTML.
12990         // so we have to trash and replace the previous code.
12991         if (Roo.XComponent.build_from_html) {
12992             // remove this element....
12993             var e = this.el.dom, k=0;
12994             while (e ) { e = e.previousSibling;  ++k;}
12995
12996             this.el.remove();
12997             
12998             this.el=false;
12999             this.rendered = false;
13000             
13001             this.render(this.parent().getChildContainer(true), k);
13002         }
13003         
13004         if(Roo.isIOS && this.useNativeIOS){
13005             this.initIOSView();
13006             return;
13007         }
13008         
13009         /*
13010          * Touch Devices
13011          */
13012         
13013         if(Roo.isTouch && this.mobileTouchView){
13014             this.initTouchView();
13015             return;
13016         }
13017         
13018         if(this.tickable){
13019             this.initTickableEvents();
13020             return;
13021         }
13022         
13023         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13024         
13025         if(this.hiddenName){
13026             
13027             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13028             
13029             this.hiddenField.dom.value =
13030                 this.hiddenValue !== undefined ? this.hiddenValue :
13031                 this.value !== undefined ? this.value : '';
13032
13033             // prevent input submission
13034             this.el.dom.removeAttribute('name');
13035             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13036              
13037              
13038         }
13039         //if(Roo.isGecko){
13040         //    this.el.dom.setAttribute('autocomplete', 'off');
13041         //}
13042         
13043         var cls = 'x-combo-list';
13044         
13045         //this.list = new Roo.Layer({
13046         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13047         //});
13048         
13049         var _this = this;
13050         
13051         (function(){
13052             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13053             _this.list.setWidth(lw);
13054         }).defer(100);
13055         
13056         this.list.on('mouseover', this.onViewOver, this);
13057         this.list.on('mousemove', this.onViewMove, this);
13058         
13059         this.list.on('scroll', this.onViewScroll, this);
13060         
13061         /*
13062         this.list.swallowEvent('mousewheel');
13063         this.assetHeight = 0;
13064
13065         if(this.title){
13066             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13067             this.assetHeight += this.header.getHeight();
13068         }
13069
13070         this.innerList = this.list.createChild({cls:cls+'-inner'});
13071         this.innerList.on('mouseover', this.onViewOver, this);
13072         this.innerList.on('mousemove', this.onViewMove, this);
13073         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13074         
13075         if(this.allowBlank && !this.pageSize && !this.disableClear){
13076             this.footer = this.list.createChild({cls:cls+'-ft'});
13077             this.pageTb = new Roo.Toolbar(this.footer);
13078            
13079         }
13080         if(this.pageSize){
13081             this.footer = this.list.createChild({cls:cls+'-ft'});
13082             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13083                     {pageSize: this.pageSize});
13084             
13085         }
13086         
13087         if (this.pageTb && this.allowBlank && !this.disableClear) {
13088             var _this = this;
13089             this.pageTb.add(new Roo.Toolbar.Fill(), {
13090                 cls: 'x-btn-icon x-btn-clear',
13091                 text: '&#160;',
13092                 handler: function()
13093                 {
13094                     _this.collapse();
13095                     _this.clearValue();
13096                     _this.onSelect(false, -1);
13097                 }
13098             });
13099         }
13100         if (this.footer) {
13101             this.assetHeight += this.footer.getHeight();
13102         }
13103         */
13104             
13105         if(!this.tpl){
13106             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13107         }
13108
13109         this.view = new Roo.View(this.list, this.tpl, {
13110             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13111         });
13112         //this.view.wrapEl.setDisplayed(false);
13113         this.view.on('click', this.onViewClick, this);
13114         
13115         
13116         this.store.on('beforeload', this.onBeforeLoad, this);
13117         this.store.on('load', this.onLoad, this);
13118         this.store.on('loadexception', this.onLoadException, this);
13119         /*
13120         if(this.resizable){
13121             this.resizer = new Roo.Resizable(this.list,  {
13122                pinned:true, handles:'se'
13123             });
13124             this.resizer.on('resize', function(r, w, h){
13125                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13126                 this.listWidth = w;
13127                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13128                 this.restrictHeight();
13129             }, this);
13130             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13131         }
13132         */
13133         if(!this.editable){
13134             this.editable = true;
13135             this.setEditable(false);
13136         }
13137         
13138         /*
13139         
13140         if (typeof(this.events.add.listeners) != 'undefined') {
13141             
13142             this.addicon = this.wrap.createChild(
13143                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13144        
13145             this.addicon.on('click', function(e) {
13146                 this.fireEvent('add', this);
13147             }, this);
13148         }
13149         if (typeof(this.events.edit.listeners) != 'undefined') {
13150             
13151             this.editicon = this.wrap.createChild(
13152                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13153             if (this.addicon) {
13154                 this.editicon.setStyle('margin-left', '40px');
13155             }
13156             this.editicon.on('click', function(e) {
13157                 
13158                 // we fire even  if inothing is selected..
13159                 this.fireEvent('edit', this, this.lastData );
13160                 
13161             }, this);
13162         }
13163         */
13164         
13165         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13166             "up" : function(e){
13167                 this.inKeyMode = true;
13168                 this.selectPrev();
13169             },
13170
13171             "down" : function(e){
13172                 if(!this.isExpanded()){
13173                     this.onTriggerClick();
13174                 }else{
13175                     this.inKeyMode = true;
13176                     this.selectNext();
13177                 }
13178             },
13179
13180             "enter" : function(e){
13181 //                this.onViewClick();
13182                 //return true;
13183                 this.collapse();
13184                 
13185                 if(this.fireEvent("specialkey", this, e)){
13186                     this.onViewClick(false);
13187                 }
13188                 
13189                 return true;
13190             },
13191
13192             "esc" : function(e){
13193                 this.collapse();
13194             },
13195
13196             "tab" : function(e){
13197                 this.collapse();
13198                 
13199                 if(this.fireEvent("specialkey", this, e)){
13200                     this.onViewClick(false);
13201                 }
13202                 
13203                 return true;
13204             },
13205
13206             scope : this,
13207
13208             doRelay : function(foo, bar, hname){
13209                 if(hname == 'down' || this.scope.isExpanded()){
13210                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13211                 }
13212                 return true;
13213             },
13214
13215             forceKeyDown: true
13216         });
13217         
13218         
13219         this.queryDelay = Math.max(this.queryDelay || 10,
13220                 this.mode == 'local' ? 10 : 250);
13221         
13222         
13223         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13224         
13225         if(this.typeAhead){
13226             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13227         }
13228         if(this.editable !== false){
13229             this.inputEl().on("keyup", this.onKeyUp, this);
13230         }
13231         if(this.forceSelection){
13232             this.inputEl().on('blur', this.doForce, this);
13233         }
13234         
13235         if(this.multiple){
13236             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13237             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13238         }
13239     },
13240     
13241     initTickableEvents: function()
13242     {   
13243         this.createList();
13244         
13245         if(this.hiddenName){
13246             
13247             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13248             
13249             this.hiddenField.dom.value =
13250                 this.hiddenValue !== undefined ? this.hiddenValue :
13251                 this.value !== undefined ? this.value : '';
13252
13253             // prevent input submission
13254             this.el.dom.removeAttribute('name');
13255             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13256              
13257              
13258         }
13259         
13260 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13261         
13262         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13263         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13264         if(this.triggerList){
13265             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13266         }
13267          
13268         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13269         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13270         
13271         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13272         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13273         
13274         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13275         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13276         
13277         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13278         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13279         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13280         
13281         this.okBtn.hide();
13282         this.cancelBtn.hide();
13283         
13284         var _this = this;
13285         
13286         (function(){
13287             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13288             _this.list.setWidth(lw);
13289         }).defer(100);
13290         
13291         this.list.on('mouseover', this.onViewOver, this);
13292         this.list.on('mousemove', this.onViewMove, this);
13293         
13294         this.list.on('scroll', this.onViewScroll, this);
13295         
13296         if(!this.tpl){
13297             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>';
13298         }
13299
13300         this.view = new Roo.View(this.list, this.tpl, {
13301             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13302         });
13303         
13304         //this.view.wrapEl.setDisplayed(false);
13305         this.view.on('click', this.onViewClick, this);
13306         
13307         
13308         
13309         this.store.on('beforeload', this.onBeforeLoad, this);
13310         this.store.on('load', this.onLoad, this);
13311         this.store.on('loadexception', this.onLoadException, this);
13312         
13313         if(this.editable){
13314             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13315                 "up" : function(e){
13316                     this.inKeyMode = true;
13317                     this.selectPrev();
13318                 },
13319
13320                 "down" : function(e){
13321                     this.inKeyMode = true;
13322                     this.selectNext();
13323                 },
13324
13325                 "enter" : function(e){
13326                     if(this.fireEvent("specialkey", this, e)){
13327                         this.onViewClick(false);
13328                     }
13329                     
13330                     return true;
13331                 },
13332
13333                 "esc" : function(e){
13334                     this.onTickableFooterButtonClick(e, false, false);
13335                 },
13336
13337                 "tab" : function(e){
13338                     this.fireEvent("specialkey", this, e);
13339                     
13340                     this.onTickableFooterButtonClick(e, false, false);
13341                     
13342                     return true;
13343                 },
13344
13345                 scope : this,
13346
13347                 doRelay : function(e, fn, key){
13348                     if(this.scope.isExpanded()){
13349                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13350                     }
13351                     return true;
13352                 },
13353
13354                 forceKeyDown: true
13355             });
13356         }
13357         
13358         this.queryDelay = Math.max(this.queryDelay || 10,
13359                 this.mode == 'local' ? 10 : 250);
13360         
13361         
13362         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13363         
13364         if(this.typeAhead){
13365             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13366         }
13367         
13368         if(this.editable !== false){
13369             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13370         }
13371         
13372         this.indicator = this.indicatorEl();
13373         
13374         if(this.indicator){
13375             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13376             this.indicator.hide();
13377         }
13378         
13379     },
13380
13381     onDestroy : function(){
13382         if(this.view){
13383             this.view.setStore(null);
13384             this.view.el.removeAllListeners();
13385             this.view.el.remove();
13386             this.view.purgeListeners();
13387         }
13388         if(this.list){
13389             this.list.dom.innerHTML  = '';
13390         }
13391         
13392         if(this.store){
13393             this.store.un('beforeload', this.onBeforeLoad, this);
13394             this.store.un('load', this.onLoad, this);
13395             this.store.un('loadexception', this.onLoadException, this);
13396         }
13397         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13398     },
13399
13400     // private
13401     fireKey : function(e){
13402         if(e.isNavKeyPress() && !this.list.isVisible()){
13403             this.fireEvent("specialkey", this, e);
13404         }
13405     },
13406
13407     // private
13408     onResize: function(w, h){
13409 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13410 //        
13411 //        if(typeof w != 'number'){
13412 //            // we do not handle it!?!?
13413 //            return;
13414 //        }
13415 //        var tw = this.trigger.getWidth();
13416 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13417 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13418 //        var x = w - tw;
13419 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13420 //            
13421 //        //this.trigger.setStyle('left', x+'px');
13422 //        
13423 //        if(this.list && this.listWidth === undefined){
13424 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13425 //            this.list.setWidth(lw);
13426 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13427 //        }
13428         
13429     
13430         
13431     },
13432
13433     /**
13434      * Allow or prevent the user from directly editing the field text.  If false is passed,
13435      * the user will only be able to select from the items defined in the dropdown list.  This method
13436      * is the runtime equivalent of setting the 'editable' config option at config time.
13437      * @param {Boolean} value True to allow the user to directly edit the field text
13438      */
13439     setEditable : function(value){
13440         if(value == this.editable){
13441             return;
13442         }
13443         this.editable = value;
13444         if(!value){
13445             this.inputEl().dom.setAttribute('readOnly', true);
13446             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13447             this.inputEl().addClass('x-combo-noedit');
13448         }else{
13449             this.inputEl().dom.setAttribute('readOnly', false);
13450             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13451             this.inputEl().removeClass('x-combo-noedit');
13452         }
13453     },
13454
13455     // private
13456     
13457     onBeforeLoad : function(combo,opts){
13458         if(!this.hasFocus){
13459             return;
13460         }
13461          if (!opts.add) {
13462             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13463          }
13464         this.restrictHeight();
13465         this.selectedIndex = -1;
13466     },
13467
13468     // private
13469     onLoad : function(){
13470         
13471         this.hasQuery = false;
13472         
13473         if(!this.hasFocus){
13474             return;
13475         }
13476         
13477         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13478             this.loading.hide();
13479         }
13480         
13481         if(this.store.getCount() > 0){
13482             
13483             this.expand();
13484             this.restrictHeight();
13485             if(this.lastQuery == this.allQuery){
13486                 if(this.editable && !this.tickable){
13487                     this.inputEl().dom.select();
13488                 }
13489                 
13490                 if(
13491                     !this.selectByValue(this.value, true) &&
13492                     this.autoFocus && 
13493                     (
13494                         !this.store.lastOptions ||
13495                         typeof(this.store.lastOptions.add) == 'undefined' || 
13496                         this.store.lastOptions.add != true
13497                     )
13498                 ){
13499                     this.select(0, true);
13500                 }
13501             }else{
13502                 if(this.autoFocus){
13503                     this.selectNext();
13504                 }
13505                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13506                     this.taTask.delay(this.typeAheadDelay);
13507                 }
13508             }
13509         }else{
13510             this.onEmptyResults();
13511         }
13512         
13513         //this.el.focus();
13514     },
13515     // private
13516     onLoadException : function()
13517     {
13518         this.hasQuery = false;
13519         
13520         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13521             this.loading.hide();
13522         }
13523         
13524         if(this.tickable && this.editable){
13525             return;
13526         }
13527         
13528         this.collapse();
13529         // only causes errors at present
13530         //Roo.log(this.store.reader.jsonData);
13531         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13532             // fixme
13533             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13534         //}
13535         
13536         
13537     },
13538     // private
13539     onTypeAhead : function(){
13540         if(this.store.getCount() > 0){
13541             var r = this.store.getAt(0);
13542             var newValue = r.data[this.displayField];
13543             var len = newValue.length;
13544             var selStart = this.getRawValue().length;
13545             
13546             if(selStart != len){
13547                 this.setRawValue(newValue);
13548                 this.selectText(selStart, newValue.length);
13549             }
13550         }
13551     },
13552
13553     // private
13554     onSelect : function(record, index){
13555         
13556         if(this.fireEvent('beforeselect', this, record, index) !== false){
13557         
13558             this.setFromData(index > -1 ? record.data : false);
13559             
13560             this.collapse();
13561             this.fireEvent('select', this, record, index);
13562         }
13563     },
13564
13565     /**
13566      * Returns the currently selected field value or empty string if no value is set.
13567      * @return {String} value The selected value
13568      */
13569     getValue : function()
13570     {
13571         if(Roo.isIOS && this.useNativeIOS){
13572             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13573         }
13574         
13575         if(this.multiple){
13576             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13577         }
13578         
13579         if(this.valueField){
13580             return typeof this.value != 'undefined' ? this.value : '';
13581         }else{
13582             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13583         }
13584     },
13585     
13586     getRawValue : function()
13587     {
13588         if(Roo.isIOS && this.useNativeIOS){
13589             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13590         }
13591         
13592         var v = this.inputEl().getValue();
13593         
13594         return v;
13595     },
13596
13597     /**
13598      * Clears any text/value currently set in the field
13599      */
13600     clearValue : function(){
13601         
13602         if(this.hiddenField){
13603             this.hiddenField.dom.value = '';
13604         }
13605         this.value = '';
13606         this.setRawValue('');
13607         this.lastSelectionText = '';
13608         this.lastData = false;
13609         
13610         var close = this.closeTriggerEl();
13611         
13612         if(close){
13613             close.hide();
13614         }
13615         
13616         this.validate();
13617         
13618     },
13619
13620     /**
13621      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13622      * will be displayed in the field.  If the value does not match the data value of an existing item,
13623      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13624      * Otherwise the field will be blank (although the value will still be set).
13625      * @param {String} value The value to match
13626      */
13627     setValue : function(v)
13628     {
13629         if(Roo.isIOS && this.useNativeIOS){
13630             this.setIOSValue(v);
13631             return;
13632         }
13633         
13634         if(this.multiple){
13635             this.syncValue();
13636             return;
13637         }
13638         
13639         var text = v;
13640         if(this.valueField){
13641             var r = this.findRecord(this.valueField, v);
13642             if(r){
13643                 text = r.data[this.displayField];
13644             }else if(this.valueNotFoundText !== undefined){
13645                 text = this.valueNotFoundText;
13646             }
13647         }
13648         this.lastSelectionText = text;
13649         if(this.hiddenField){
13650             this.hiddenField.dom.value = v;
13651         }
13652         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13653         this.value = v;
13654         
13655         var close = this.closeTriggerEl();
13656         
13657         if(close){
13658             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13659         }
13660         
13661         this.validate();
13662     },
13663     /**
13664      * @property {Object} the last set data for the element
13665      */
13666     
13667     lastData : false,
13668     /**
13669      * Sets the value of the field based on a object which is related to the record format for the store.
13670      * @param {Object} value the value to set as. or false on reset?
13671      */
13672     setFromData : function(o){
13673         
13674         if(this.multiple){
13675             this.addItem(o);
13676             return;
13677         }
13678             
13679         var dv = ''; // display value
13680         var vv = ''; // value value..
13681         this.lastData = o;
13682         if (this.displayField) {
13683             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13684         } else {
13685             // this is an error condition!!!
13686             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13687         }
13688         
13689         if(this.valueField){
13690             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13691         }
13692         
13693         var close = this.closeTriggerEl();
13694         
13695         if(close){
13696             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
13697         }
13698         
13699         if(this.hiddenField){
13700             this.hiddenField.dom.value = vv;
13701             
13702             this.lastSelectionText = dv;
13703             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13704             this.value = vv;
13705             return;
13706         }
13707         // no hidden field.. - we store the value in 'value', but still display
13708         // display field!!!!
13709         this.lastSelectionText = dv;
13710         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13711         this.value = vv;
13712         
13713         
13714         
13715     },
13716     // private
13717     reset : function(){
13718         // overridden so that last data is reset..
13719         
13720         if(this.multiple){
13721             this.clearItem();
13722             return;
13723         }
13724         
13725         this.setValue(this.originalValue);
13726         //this.clearInvalid();
13727         this.lastData = false;
13728         if (this.view) {
13729             this.view.clearSelections();
13730         }
13731         
13732         this.validate();
13733     },
13734     // private
13735     findRecord : function(prop, value){
13736         var record;
13737         if(this.store.getCount() > 0){
13738             this.store.each(function(r){
13739                 if(r.data[prop] == value){
13740                     record = r;
13741                     return false;
13742                 }
13743                 return true;
13744             });
13745         }
13746         return record;
13747     },
13748     
13749     getName: function()
13750     {
13751         // returns hidden if it's set..
13752         if (!this.rendered) {return ''};
13753         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13754         
13755     },
13756     // private
13757     onViewMove : function(e, t){
13758         this.inKeyMode = false;
13759     },
13760
13761     // private
13762     onViewOver : function(e, t){
13763         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13764             return;
13765         }
13766         var item = this.view.findItemFromChild(t);
13767         
13768         if(item){
13769             var index = this.view.indexOf(item);
13770             this.select(index, false);
13771         }
13772     },
13773
13774     // private
13775     onViewClick : function(view, doFocus, el, e)
13776     {
13777         var index = this.view.getSelectedIndexes()[0];
13778         
13779         var r = this.store.getAt(index);
13780         
13781         if(this.tickable){
13782             
13783             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13784                 return;
13785             }
13786             
13787             var rm = false;
13788             var _this = this;
13789             
13790             Roo.each(this.tickItems, function(v,k){
13791                 
13792                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13793                     Roo.log(v);
13794                     _this.tickItems.splice(k, 1);
13795                     
13796                     if(typeof(e) == 'undefined' && view == false){
13797                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13798                     }
13799                     
13800                     rm = true;
13801                     return;
13802                 }
13803             });
13804             
13805             if(rm){
13806                 return;
13807             }
13808             
13809             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13810                 this.tickItems.push(r.data);
13811             }
13812             
13813             if(typeof(e) == 'undefined' && view == false){
13814                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13815             }
13816                     
13817             return;
13818         }
13819         
13820         if(r){
13821             this.onSelect(r, index);
13822         }
13823         if(doFocus !== false && !this.blockFocus){
13824             this.inputEl().focus();
13825         }
13826     },
13827
13828     // private
13829     restrictHeight : function(){
13830         //this.innerList.dom.style.height = '';
13831         //var inner = this.innerList.dom;
13832         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13833         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13834         //this.list.beginUpdate();
13835         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13836         this.list.alignTo(this.inputEl(), this.listAlign);
13837         this.list.alignTo(this.inputEl(), this.listAlign);
13838         //this.list.endUpdate();
13839     },
13840
13841     // private
13842     onEmptyResults : function(){
13843         
13844         if(this.tickable && this.editable){
13845             this.restrictHeight();
13846             return;
13847         }
13848         
13849         this.collapse();
13850     },
13851
13852     /**
13853      * Returns true if the dropdown list is expanded, else false.
13854      */
13855     isExpanded : function(){
13856         return this.list.isVisible();
13857     },
13858
13859     /**
13860      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13861      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13862      * @param {String} value The data value of the item to select
13863      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13864      * selected item if it is not currently in view (defaults to true)
13865      * @return {Boolean} True if the value matched an item in the list, else false
13866      */
13867     selectByValue : function(v, scrollIntoView){
13868         if(v !== undefined && v !== null){
13869             var r = this.findRecord(this.valueField || this.displayField, v);
13870             if(r){
13871                 this.select(this.store.indexOf(r), scrollIntoView);
13872                 return true;
13873             }
13874         }
13875         return false;
13876     },
13877
13878     /**
13879      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13880      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13881      * @param {Number} index The zero-based index of the list item to select
13882      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13883      * selected item if it is not currently in view (defaults to true)
13884      */
13885     select : function(index, scrollIntoView){
13886         this.selectedIndex = index;
13887         this.view.select(index);
13888         if(scrollIntoView !== false){
13889             var el = this.view.getNode(index);
13890             /*
13891              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13892              */
13893             if(el){
13894                 this.list.scrollChildIntoView(el, false);
13895             }
13896         }
13897     },
13898
13899     // private
13900     selectNext : function(){
13901         var ct = this.store.getCount();
13902         if(ct > 0){
13903             if(this.selectedIndex == -1){
13904                 this.select(0);
13905             }else if(this.selectedIndex < ct-1){
13906                 this.select(this.selectedIndex+1);
13907             }
13908         }
13909     },
13910
13911     // private
13912     selectPrev : function(){
13913         var ct = this.store.getCount();
13914         if(ct > 0){
13915             if(this.selectedIndex == -1){
13916                 this.select(0);
13917             }else if(this.selectedIndex != 0){
13918                 this.select(this.selectedIndex-1);
13919             }
13920         }
13921     },
13922
13923     // private
13924     onKeyUp : function(e){
13925         if(this.editable !== false && !e.isSpecialKey()){
13926             this.lastKey = e.getKey();
13927             this.dqTask.delay(this.queryDelay);
13928         }
13929     },
13930
13931     // private
13932     validateBlur : function(){
13933         return !this.list || !this.list.isVisible();   
13934     },
13935
13936     // private
13937     initQuery : function(){
13938         
13939         var v = this.getRawValue();
13940         
13941         if(this.tickable && this.editable){
13942             v = this.tickableInputEl().getValue();
13943         }
13944         
13945         this.doQuery(v);
13946     },
13947
13948     // private
13949     doForce : function(){
13950         if(this.inputEl().dom.value.length > 0){
13951             this.inputEl().dom.value =
13952                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13953              
13954         }
13955     },
13956
13957     /**
13958      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
13959      * query allowing the query action to be canceled if needed.
13960      * @param {String} query The SQL query to execute
13961      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13962      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
13963      * saved in the current store (defaults to false)
13964      */
13965     doQuery : function(q, forceAll){
13966         
13967         if(q === undefined || q === null){
13968             q = '';
13969         }
13970         var qe = {
13971             query: q,
13972             forceAll: forceAll,
13973             combo: this,
13974             cancel:false
13975         };
13976         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13977             return false;
13978         }
13979         q = qe.query;
13980         
13981         forceAll = qe.forceAll;
13982         if(forceAll === true || (q.length >= this.minChars)){
13983             
13984             this.hasQuery = true;
13985             
13986             if(this.lastQuery != q || this.alwaysQuery){
13987                 this.lastQuery = q;
13988                 if(this.mode == 'local'){
13989                     this.selectedIndex = -1;
13990                     if(forceAll){
13991                         this.store.clearFilter();
13992                     }else{
13993                         
13994                         if(this.specialFilter){
13995                             this.fireEvent('specialfilter', this);
13996                             this.onLoad();
13997                             return;
13998                         }
13999                         
14000                         this.store.filter(this.displayField, q);
14001                     }
14002                     
14003                     this.store.fireEvent("datachanged", this.store);
14004                     
14005                     this.onLoad();
14006                     
14007                     
14008                 }else{
14009                     
14010                     this.store.baseParams[this.queryParam] = q;
14011                     
14012                     var options = {params : this.getParams(q)};
14013                     
14014                     if(this.loadNext){
14015                         options.add = true;
14016                         options.params.start = this.page * this.pageSize;
14017                     }
14018                     
14019                     this.store.load(options);
14020                     
14021                     /*
14022                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14023                      *  we should expand the list on onLoad
14024                      *  so command out it
14025                      */
14026 //                    this.expand();
14027                 }
14028             }else{
14029                 this.selectedIndex = -1;
14030                 this.onLoad();   
14031             }
14032         }
14033         
14034         this.loadNext = false;
14035     },
14036     
14037     // private
14038     getParams : function(q){
14039         var p = {};
14040         //p[this.queryParam] = q;
14041         
14042         if(this.pageSize){
14043             p.start = 0;
14044             p.limit = this.pageSize;
14045         }
14046         return p;
14047     },
14048
14049     /**
14050      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14051      */
14052     collapse : function(){
14053         if(!this.isExpanded()){
14054             return;
14055         }
14056         
14057         this.list.hide();
14058         
14059         this.hasFocus = false;
14060         
14061         if(this.tickable){
14062             this.okBtn.hide();
14063             this.cancelBtn.hide();
14064             this.trigger.show();
14065             
14066             if(this.editable){
14067                 this.tickableInputEl().dom.value = '';
14068                 this.tickableInputEl().blur();
14069             }
14070             
14071         }
14072         
14073         Roo.get(document).un('mousedown', this.collapseIf, this);
14074         Roo.get(document).un('mousewheel', this.collapseIf, this);
14075         if (!this.editable) {
14076             Roo.get(document).un('keydown', this.listKeyPress, this);
14077         }
14078         this.fireEvent('collapse', this);
14079         
14080         this.validate();
14081     },
14082
14083     // private
14084     collapseIf : function(e){
14085         var in_combo  = e.within(this.el);
14086         var in_list =  e.within(this.list);
14087         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14088         
14089         if (in_combo || in_list || is_list) {
14090             //e.stopPropagation();
14091             return;
14092         }
14093         
14094         if(this.tickable){
14095             this.onTickableFooterButtonClick(e, false, false);
14096         }
14097
14098         this.collapse();
14099         
14100     },
14101
14102     /**
14103      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14104      */
14105     expand : function(){
14106        
14107         if(this.isExpanded() || !this.hasFocus){
14108             return;
14109         }
14110         
14111         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14112         this.list.setWidth(lw);
14113         
14114         Roo.log('expand');
14115         
14116         this.list.show();
14117         
14118         this.restrictHeight();
14119         
14120         if(this.tickable){
14121             
14122             this.tickItems = Roo.apply([], this.item);
14123             
14124             this.okBtn.show();
14125             this.cancelBtn.show();
14126             this.trigger.hide();
14127             
14128             if(this.editable){
14129                 this.tickableInputEl().focus();
14130             }
14131             
14132         }
14133         
14134         Roo.get(document).on('mousedown', this.collapseIf, this);
14135         Roo.get(document).on('mousewheel', this.collapseIf, this);
14136         if (!this.editable) {
14137             Roo.get(document).on('keydown', this.listKeyPress, this);
14138         }
14139         
14140         this.fireEvent('expand', this);
14141     },
14142
14143     // private
14144     // Implements the default empty TriggerField.onTriggerClick function
14145     onTriggerClick : function(e)
14146     {
14147         Roo.log('trigger click');
14148         
14149         if(this.disabled || !this.triggerList){
14150             return;
14151         }
14152         
14153         this.page = 0;
14154         this.loadNext = false;
14155         
14156         if(this.isExpanded()){
14157             this.collapse();
14158             if (!this.blockFocus) {
14159                 this.inputEl().focus();
14160             }
14161             
14162         }else {
14163             this.hasFocus = true;
14164             if(this.triggerAction == 'all') {
14165                 this.doQuery(this.allQuery, true);
14166             } else {
14167                 this.doQuery(this.getRawValue());
14168             }
14169             if (!this.blockFocus) {
14170                 this.inputEl().focus();
14171             }
14172         }
14173     },
14174     
14175     onTickableTriggerClick : function(e)
14176     {
14177         if(this.disabled){
14178             return;
14179         }
14180         
14181         this.page = 0;
14182         this.loadNext = false;
14183         this.hasFocus = true;
14184         
14185         if(this.triggerAction == 'all') {
14186             this.doQuery(this.allQuery, true);
14187         } else {
14188             this.doQuery(this.getRawValue());
14189         }
14190     },
14191     
14192     onSearchFieldClick : function(e)
14193     {
14194         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14195             this.onTickableFooterButtonClick(e, false, false);
14196             return;
14197         }
14198         
14199         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14200             return;
14201         }
14202         
14203         this.page = 0;
14204         this.loadNext = false;
14205         this.hasFocus = true;
14206         
14207         if(this.triggerAction == 'all') {
14208             this.doQuery(this.allQuery, true);
14209         } else {
14210             this.doQuery(this.getRawValue());
14211         }
14212     },
14213     
14214     listKeyPress : function(e)
14215     {
14216         //Roo.log('listkeypress');
14217         // scroll to first matching element based on key pres..
14218         if (e.isSpecialKey()) {
14219             return false;
14220         }
14221         var k = String.fromCharCode(e.getKey()).toUpperCase();
14222         //Roo.log(k);
14223         var match  = false;
14224         var csel = this.view.getSelectedNodes();
14225         var cselitem = false;
14226         if (csel.length) {
14227             var ix = this.view.indexOf(csel[0]);
14228             cselitem  = this.store.getAt(ix);
14229             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14230                 cselitem = false;
14231             }
14232             
14233         }
14234         
14235         this.store.each(function(v) { 
14236             if (cselitem) {
14237                 // start at existing selection.
14238                 if (cselitem.id == v.id) {
14239                     cselitem = false;
14240                 }
14241                 return true;
14242             }
14243                 
14244             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14245                 match = this.store.indexOf(v);
14246                 return false;
14247             }
14248             return true;
14249         }, this);
14250         
14251         if (match === false) {
14252             return true; // no more action?
14253         }
14254         // scroll to?
14255         this.view.select(match);
14256         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14257         sn.scrollIntoView(sn.dom.parentNode, false);
14258     },
14259     
14260     onViewScroll : function(e, t){
14261         
14262         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){
14263             return;
14264         }
14265         
14266         this.hasQuery = true;
14267         
14268         this.loading = this.list.select('.loading', true).first();
14269         
14270         if(this.loading === null){
14271             this.list.createChild({
14272                 tag: 'div',
14273                 cls: 'loading roo-select2-more-results roo-select2-active',
14274                 html: 'Loading more results...'
14275             });
14276             
14277             this.loading = this.list.select('.loading', true).first();
14278             
14279             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14280             
14281             this.loading.hide();
14282         }
14283         
14284         this.loading.show();
14285         
14286         var _combo = this;
14287         
14288         this.page++;
14289         this.loadNext = true;
14290         
14291         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14292         
14293         return;
14294     },
14295     
14296     addItem : function(o)
14297     {   
14298         var dv = ''; // display value
14299         
14300         if (this.displayField) {
14301             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14302         } else {
14303             // this is an error condition!!!
14304             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14305         }
14306         
14307         if(!dv.length){
14308             return;
14309         }
14310         
14311         var choice = this.choices.createChild({
14312             tag: 'li',
14313             cls: 'roo-select2-search-choice',
14314             cn: [
14315                 {
14316                     tag: 'div',
14317                     html: dv
14318                 },
14319                 {
14320                     tag: 'a',
14321                     href: '#',
14322                     cls: 'roo-select2-search-choice-close fa fa-times',
14323                     tabindex: '-1'
14324                 }
14325             ]
14326             
14327         }, this.searchField);
14328         
14329         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14330         
14331         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14332         
14333         this.item.push(o);
14334         
14335         this.lastData = o;
14336         
14337         this.syncValue();
14338         
14339         this.inputEl().dom.value = '';
14340         
14341         this.validate();
14342     },
14343     
14344     onRemoveItem : function(e, _self, o)
14345     {
14346         e.preventDefault();
14347         
14348         this.lastItem = Roo.apply([], this.item);
14349         
14350         var index = this.item.indexOf(o.data) * 1;
14351         
14352         if( index < 0){
14353             Roo.log('not this item?!');
14354             return;
14355         }
14356         
14357         this.item.splice(index, 1);
14358         o.item.remove();
14359         
14360         this.syncValue();
14361         
14362         this.fireEvent('remove', this, e);
14363         
14364         this.validate();
14365         
14366     },
14367     
14368     syncValue : function()
14369     {
14370         if(!this.item.length){
14371             this.clearValue();
14372             return;
14373         }
14374             
14375         var value = [];
14376         var _this = this;
14377         Roo.each(this.item, function(i){
14378             if(_this.valueField){
14379                 value.push(i[_this.valueField]);
14380                 return;
14381             }
14382
14383             value.push(i);
14384         });
14385
14386         this.value = value.join(',');
14387
14388         if(this.hiddenField){
14389             this.hiddenField.dom.value = this.value;
14390         }
14391         
14392         this.store.fireEvent("datachanged", this.store);
14393         
14394         this.validate();
14395     },
14396     
14397     clearItem : function()
14398     {
14399         if(!this.multiple){
14400             return;
14401         }
14402         
14403         this.item = [];
14404         
14405         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14406            c.remove();
14407         });
14408         
14409         this.syncValue();
14410         
14411         this.validate();
14412         
14413         if(this.tickable && !Roo.isTouch){
14414             this.view.refresh();
14415         }
14416     },
14417     
14418     inputEl: function ()
14419     {
14420         if(Roo.isIOS && this.useNativeIOS){
14421             return this.el.select('select.roo-ios-select', true).first();
14422         }
14423         
14424         if(Roo.isTouch && this.mobileTouchView){
14425             return this.el.select('input.form-control',true).first();
14426         }
14427         
14428         if(this.tickable){
14429             return this.searchField;
14430         }
14431         
14432         return this.el.select('input.form-control',true).first();
14433     },
14434     
14435     onTickableFooterButtonClick : function(e, btn, el)
14436     {
14437         e.preventDefault();
14438         
14439         this.lastItem = Roo.apply([], this.item);
14440         
14441         if(btn && btn.name == 'cancel'){
14442             this.tickItems = Roo.apply([], this.item);
14443             this.collapse();
14444             return;
14445         }
14446         
14447         this.clearItem();
14448         
14449         var _this = this;
14450         
14451         Roo.each(this.tickItems, function(o){
14452             _this.addItem(o);
14453         });
14454         
14455         this.collapse();
14456         
14457     },
14458     
14459     validate : function()
14460     {
14461         var v = this.getRawValue();
14462         
14463         if(this.multiple){
14464             v = this.getValue();
14465         }
14466         
14467         if(this.disabled || this.allowBlank || v.length){
14468             this.markValid();
14469             return true;
14470         }
14471         
14472         this.markInvalid();
14473         return false;
14474     },
14475     
14476     tickableInputEl : function()
14477     {
14478         if(!this.tickable || !this.editable){
14479             return this.inputEl();
14480         }
14481         
14482         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14483     },
14484     
14485     
14486     getAutoCreateTouchView : function()
14487     {
14488         var id = Roo.id();
14489         
14490         var cfg = {
14491             cls: 'form-group' //input-group
14492         };
14493         
14494         var input =  {
14495             tag: 'input',
14496             id : id,
14497             type : this.inputType,
14498             cls : 'form-control x-combo-noedit',
14499             autocomplete: 'new-password',
14500             placeholder : this.placeholder || '',
14501             readonly : true
14502         };
14503         
14504         if (this.name) {
14505             input.name = this.name;
14506         }
14507         
14508         if (this.size) {
14509             input.cls += ' input-' + this.size;
14510         }
14511         
14512         if (this.disabled) {
14513             input.disabled = true;
14514         }
14515         
14516         var inputblock = {
14517             cls : '',
14518             cn : [
14519                 input
14520             ]
14521         };
14522         
14523         if(this.before){
14524             inputblock.cls += ' input-group';
14525             
14526             inputblock.cn.unshift({
14527                 tag :'span',
14528                 cls : 'input-group-addon',
14529                 html : this.before
14530             });
14531         }
14532         
14533         if(this.removable && !this.multiple){
14534             inputblock.cls += ' roo-removable';
14535             
14536             inputblock.cn.push({
14537                 tag: 'button',
14538                 html : 'x',
14539                 cls : 'roo-combo-removable-btn close'
14540             });
14541         }
14542
14543         if(this.hasFeedback && !this.allowBlank){
14544             
14545             inputblock.cls += ' has-feedback';
14546             
14547             inputblock.cn.push({
14548                 tag: 'span',
14549                 cls: 'glyphicon form-control-feedback'
14550             });
14551             
14552         }
14553         
14554         if (this.after) {
14555             
14556             inputblock.cls += (this.before) ? '' : ' input-group';
14557             
14558             inputblock.cn.push({
14559                 tag :'span',
14560                 cls : 'input-group-addon',
14561                 html : this.after
14562             });
14563         }
14564
14565         var box = {
14566             tag: 'div',
14567             cn: [
14568                 {
14569                     tag: 'input',
14570                     type : 'hidden',
14571                     cls: 'form-hidden-field'
14572                 },
14573                 inputblock
14574             ]
14575             
14576         };
14577         
14578         if(this.multiple){
14579             box = {
14580                 tag: 'div',
14581                 cn: [
14582                     {
14583                         tag: 'input',
14584                         type : 'hidden',
14585                         cls: 'form-hidden-field'
14586                     },
14587                     {
14588                         tag: 'ul',
14589                         cls: 'roo-select2-choices',
14590                         cn:[
14591                             {
14592                                 tag: 'li',
14593                                 cls: 'roo-select2-search-field',
14594                                 cn: [
14595
14596                                     inputblock
14597                                 ]
14598                             }
14599                         ]
14600                     }
14601                 ]
14602             }
14603         };
14604         
14605         var combobox = {
14606             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14607             cn: [
14608                 box
14609             ]
14610         };
14611         
14612         if(!this.multiple && this.showToggleBtn){
14613             
14614             var caret = {
14615                         tag: 'span',
14616                         cls: 'caret'
14617             };
14618             
14619             if (this.caret != false) {
14620                 caret = {
14621                      tag: 'i',
14622                      cls: 'fa fa-' + this.caret
14623                 };
14624                 
14625             }
14626             
14627             combobox.cn.push({
14628                 tag :'span',
14629                 cls : 'input-group-addon btn dropdown-toggle',
14630                 cn : [
14631                     caret,
14632                     {
14633                         tag: 'span',
14634                         cls: 'combobox-clear',
14635                         cn  : [
14636                             {
14637                                 tag : 'i',
14638                                 cls: 'icon-remove'
14639                             }
14640                         ]
14641                     }
14642                 ]
14643
14644             })
14645         }
14646         
14647         if(this.multiple){
14648             combobox.cls += ' roo-select2-container-multi';
14649         }
14650         
14651         var align = this.labelAlign || this.parentLabelAlign();
14652         
14653         if (align ==='left' && this.fieldLabel.length) {
14654
14655             cfg.cn = [
14656                 {
14657                    tag : 'i',
14658                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14659                    tooltip : 'This field is required'
14660                 },
14661                 {
14662                     tag: 'label',
14663                     cls : 'control-label',
14664                     html : this.fieldLabel
14665
14666                 },
14667                 {
14668                     cls : '', 
14669                     cn: [
14670                         combobox
14671                     ]
14672                 }
14673             ];
14674             
14675             var labelCfg = cfg.cn[1];
14676             var contentCfg = cfg.cn[2];
14677             
14678
14679             if(this.indicatorpos == 'right'){
14680                 cfg.cn = [
14681                     {
14682                         tag: 'label',
14683                         cls : 'control-label',
14684                         html : this.fieldLabel,
14685                         cn : [
14686                             {
14687                                tag : 'i',
14688                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14689                                tooltip : 'This field is required'
14690                             }
14691                         ]
14692                     },
14693                     {
14694                         cls : '', 
14695                         cn: [
14696                             combobox
14697                         ]
14698                     }
14699                 ];
14700             }
14701             
14702             labelCfg = cfg.cn[0];
14703             contentCfg = cfg.cn[2];
14704             
14705             if(this.labelWidth > 12){
14706                 labelCfg.style = "width: " + this.labelWidth + 'px';
14707             }
14708             
14709             if(this.labelWidth < 13 && this.labelmd == 0){
14710                 this.labelmd = this.labelWidth;
14711             }
14712             
14713             if(this.labellg > 0){
14714                 labelCfg.cls += ' col-lg-' + this.labellg;
14715                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14716             }
14717             
14718             if(this.labelmd > 0){
14719                 labelCfg.cls += ' col-md-' + this.labelmd;
14720                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14721             }
14722             
14723             if(this.labelsm > 0){
14724                 labelCfg.cls += ' col-sm-' + this.labelsm;
14725                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14726             }
14727             
14728             if(this.labelxs > 0){
14729                 labelCfg.cls += ' col-xs-' + this.labelxs;
14730                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14731             }
14732                 
14733                 
14734         } else if ( this.fieldLabel.length) {
14735             cfg.cn = [
14736                 {
14737                    tag : 'i',
14738                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14739                    tooltip : 'This field is required'
14740                 },
14741                 {
14742                     tag: 'label',
14743                     cls : 'control-label',
14744                     html : this.fieldLabel
14745
14746                 },
14747                 {
14748                     cls : '', 
14749                     cn: [
14750                         combobox
14751                     ]
14752                 }
14753             ];
14754             
14755             if(this.indicatorpos == 'right'){
14756                 cfg.cn = [
14757                     {
14758                         tag: 'label',
14759                         cls : 'control-label',
14760                         html : this.fieldLabel,
14761                         cn : [
14762                             {
14763                                tag : 'i',
14764                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14765                                tooltip : 'This field is required'
14766                             }
14767                         ]
14768                     },
14769                     {
14770                         cls : '', 
14771                         cn: [
14772                             combobox
14773                         ]
14774                     }
14775                 ];
14776             }
14777         } else {
14778             cfg.cn = combobox;    
14779         }
14780         
14781         
14782         var settings = this;
14783         
14784         ['xs','sm','md','lg'].map(function(size){
14785             if (settings[size]) {
14786                 cfg.cls += ' col-' + size + '-' + settings[size];
14787             }
14788         });
14789         
14790         return cfg;
14791     },
14792     
14793     initTouchView : function()
14794     {
14795         this.renderTouchView();
14796         
14797         this.touchViewEl.on('scroll', function(){
14798             this.el.dom.scrollTop = 0;
14799         }, this);
14800         
14801         this.originalValue = this.getValue();
14802         
14803         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14804         
14805         this.inputEl().on("click", this.showTouchView, this);
14806         if (this.triggerEl) {
14807             this.triggerEl.on("click", this.showTouchView, this);
14808         }
14809         
14810         
14811         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14812         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14813         
14814         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14815         
14816         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14817         this.store.on('load', this.onTouchViewLoad, this);
14818         this.store.on('loadexception', this.onTouchViewLoadException, this);
14819         
14820         if(this.hiddenName){
14821             
14822             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14823             
14824             this.hiddenField.dom.value =
14825                 this.hiddenValue !== undefined ? this.hiddenValue :
14826                 this.value !== undefined ? this.value : '';
14827         
14828             this.el.dom.removeAttribute('name');
14829             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14830         }
14831         
14832         if(this.multiple){
14833             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14834             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14835         }
14836         
14837         if(this.removable && !this.multiple){
14838             var close = this.closeTriggerEl();
14839             if(close){
14840                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14841                 close.on('click', this.removeBtnClick, this, close);
14842             }
14843         }
14844         /*
14845          * fix the bug in Safari iOS8
14846          */
14847         this.inputEl().on("focus", function(e){
14848             document.activeElement.blur();
14849         }, this);
14850         
14851         return;
14852         
14853         
14854     },
14855     
14856     renderTouchView : function()
14857     {
14858         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14859         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14860         
14861         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14862         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14863         
14864         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14865         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14866         this.touchViewBodyEl.setStyle('overflow', 'auto');
14867         
14868         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14869         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14870         
14871         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14872         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14873         
14874     },
14875     
14876     showTouchView : function()
14877     {
14878         if(this.disabled){
14879             return;
14880         }
14881         
14882         this.touchViewHeaderEl.hide();
14883
14884         if(this.modalTitle.length){
14885             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14886             this.touchViewHeaderEl.show();
14887         }
14888
14889         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
14890         this.touchViewEl.show();
14891
14892         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14893         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14894                 Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14895
14896         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14897
14898         if(this.modalTitle.length){
14899             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14900         }
14901         
14902         this.touchViewBodyEl.setHeight(bodyHeight);
14903
14904         if(this.animate){
14905             var _this = this;
14906             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14907         }else{
14908             this.touchViewEl.addClass('in');
14909         }
14910
14911         this.doTouchViewQuery();
14912         
14913     },
14914     
14915     hideTouchView : function()
14916     {
14917         this.touchViewEl.removeClass('in');
14918
14919         if(this.animate){
14920             var _this = this;
14921             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14922         }else{
14923             this.touchViewEl.setStyle('display', 'none');
14924         }
14925         
14926     },
14927     
14928     setTouchViewValue : function()
14929     {
14930         if(this.multiple){
14931             this.clearItem();
14932         
14933             var _this = this;
14934
14935             Roo.each(this.tickItems, function(o){
14936                 this.addItem(o);
14937             }, this);
14938         }
14939         
14940         this.hideTouchView();
14941     },
14942     
14943     doTouchViewQuery : function()
14944     {
14945         var qe = {
14946             query: '',
14947             forceAll: true,
14948             combo: this,
14949             cancel:false
14950         };
14951         
14952         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14953             return false;
14954         }
14955         
14956         if(!this.alwaysQuery || this.mode == 'local'){
14957             this.onTouchViewLoad();
14958             return;
14959         }
14960         
14961         this.store.load();
14962     },
14963     
14964     onTouchViewBeforeLoad : function(combo,opts)
14965     {
14966         return;
14967     },
14968
14969     // private
14970     onTouchViewLoad : function()
14971     {
14972         if(this.store.getCount() < 1){
14973             this.onTouchViewEmptyResults();
14974             return;
14975         }
14976         
14977         this.clearTouchView();
14978         
14979         var rawValue = this.getRawValue();
14980         
14981         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14982         
14983         this.tickItems = [];
14984         
14985         this.store.data.each(function(d, rowIndex){
14986             var row = this.touchViewListGroup.createChild(template);
14987             
14988             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14989                 row.addClass(d.data.cls);
14990             }
14991             
14992             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14993                 var cfg = {
14994                     data : d.data,
14995                     html : d.data[this.displayField]
14996                 };
14997                 
14998                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
14999                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15000                 }
15001             }
15002             row.removeClass('selected');
15003             if(!this.multiple && this.valueField &&
15004                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15005             {
15006                 // radio buttons..
15007                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15008                 row.addClass('selected');
15009             }
15010             
15011             if(this.multiple && this.valueField &&
15012                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15013             {
15014                 
15015                 // checkboxes...
15016                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15017                 this.tickItems.push(d.data);
15018             }
15019             
15020             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15021             
15022         }, this);
15023         
15024         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15025         
15026         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15027
15028         if(this.modalTitle.length){
15029             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15030         }
15031
15032         var listHeight = this.touchViewListGroup.getHeight();
15033         
15034         var _this = this;
15035         
15036         if(firstChecked && listHeight > bodyHeight){
15037             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15038         }
15039         
15040     },
15041     
15042     onTouchViewLoadException : function()
15043     {
15044         this.hideTouchView();
15045     },
15046     
15047     onTouchViewEmptyResults : function()
15048     {
15049         this.clearTouchView();
15050         
15051         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15052         
15053         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15054         
15055     },
15056     
15057     clearTouchView : function()
15058     {
15059         this.touchViewListGroup.dom.innerHTML = '';
15060     },
15061     
15062     onTouchViewClick : function(e, el, o)
15063     {
15064         e.preventDefault();
15065         
15066         var row = o.row;
15067         var rowIndex = o.rowIndex;
15068         
15069         var r = this.store.getAt(rowIndex);
15070         
15071         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15072             
15073             if(!this.multiple){
15074                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15075                     c.dom.removeAttribute('checked');
15076                 }, this);
15077
15078                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15079
15080                 this.setFromData(r.data);
15081
15082                 var close = this.closeTriggerEl();
15083
15084                 if(close){
15085                     close.show();
15086                 }
15087
15088                 this.hideTouchView();
15089
15090                 this.fireEvent('select', this, r, rowIndex);
15091
15092                 return;
15093             }
15094
15095             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15096                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15097                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15098                 return;
15099             }
15100
15101             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15102             this.addItem(r.data);
15103             this.tickItems.push(r.data);
15104         }
15105     },
15106     
15107     getAutoCreateNativeIOS : function()
15108     {
15109         var cfg = {
15110             cls: 'form-group' //input-group,
15111         };
15112         
15113         var combobox =  {
15114             tag: 'select',
15115             cls : 'roo-ios-select'
15116         };
15117         
15118         if (this.name) {
15119             combobox.name = this.name;
15120         }
15121         
15122         if (this.disabled) {
15123             combobox.disabled = true;
15124         }
15125         
15126         var settings = this;
15127         
15128         ['xs','sm','md','lg'].map(function(size){
15129             if (settings[size]) {
15130                 cfg.cls += ' col-' + size + '-' + settings[size];
15131             }
15132         });
15133         
15134         cfg.cn = combobox;
15135         
15136         return cfg;
15137         
15138     },
15139     
15140     initIOSView : function()
15141     {
15142         this.store.on('load', this.onIOSViewLoad, this);
15143         
15144         return;
15145     },
15146     
15147     onIOSViewLoad : function()
15148     {
15149         if(this.store.getCount() < 1){
15150             return;
15151         }
15152         
15153         this.clearIOSView();
15154         
15155         if(this.allowBlank) {
15156             
15157             var default_text = '-- SELECT --';
15158             
15159             var opt = this.inputEl().createChild({
15160                 tag: 'option',
15161                 value : 0,
15162                 html : default_text
15163             });
15164             
15165             var o = {};
15166             o[this.valueField] = 0;
15167             o[this.displayField] = default_text;
15168             
15169             this.ios_options.push({
15170                 data : o,
15171                 el : opt
15172             });
15173             
15174         }
15175         
15176         this.store.data.each(function(d, rowIndex){
15177             
15178             var html = '';
15179             
15180             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15181                 html = d.data[this.displayField];
15182             }
15183             
15184             var value = '';
15185             
15186             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15187                 value = d.data[this.valueField];
15188             }
15189             
15190             var option = {
15191                 tag: 'option',
15192                 value : value,
15193                 html : html
15194             };
15195             
15196             if(this.value == d.data[this.valueField]){
15197                 option['selected'] = true;
15198             }
15199             
15200             var opt = this.inputEl().createChild(option);
15201             
15202             this.ios_options.push({
15203                 data : d.data,
15204                 el : opt
15205             });
15206             
15207         }, this);
15208         
15209         this.inputEl().on('change', function(){
15210            this.fireEvent('select', this);
15211         }, this);
15212         
15213     },
15214     
15215     clearIOSView: function()
15216     {
15217         this.inputEl().dom.innerHTML = '';
15218         
15219         this.ios_options = [];
15220     },
15221     
15222     setIOSValue: function(v)
15223     {
15224         this.value = v;
15225         
15226         if(!this.ios_options){
15227             return;
15228         }
15229         
15230         Roo.each(this.ios_options, function(opts){
15231            
15232            opts.el.dom.removeAttribute('selected');
15233            
15234            if(opts.data[this.valueField] != v){
15235                return;
15236            }
15237            
15238            opts.el.dom.setAttribute('selected', true);
15239            
15240         }, this);
15241     }
15242
15243     /** 
15244     * @cfg {Boolean} grow 
15245     * @hide 
15246     */
15247     /** 
15248     * @cfg {Number} growMin 
15249     * @hide 
15250     */
15251     /** 
15252     * @cfg {Number} growMax 
15253     * @hide 
15254     */
15255     /**
15256      * @hide
15257      * @method autoSize
15258      */
15259 });
15260
15261 Roo.apply(Roo.bootstrap.ComboBox,  {
15262     
15263     header : {
15264         tag: 'div',
15265         cls: 'modal-header',
15266         cn: [
15267             {
15268                 tag: 'h4',
15269                 cls: 'modal-title'
15270             }
15271         ]
15272     },
15273     
15274     body : {
15275         tag: 'div',
15276         cls: 'modal-body',
15277         cn: [
15278             {
15279                 tag: 'ul',
15280                 cls: 'list-group'
15281             }
15282         ]
15283     },
15284     
15285     listItemRadio : {
15286         tag: 'li',
15287         cls: 'list-group-item',
15288         cn: [
15289             {
15290                 tag: 'span',
15291                 cls: 'roo-combobox-list-group-item-value'
15292             },
15293             {
15294                 tag: 'div',
15295                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15296                 cn: [
15297                     {
15298                         tag: 'input',
15299                         type: 'radio'
15300                     },
15301                     {
15302                         tag: 'label'
15303                     }
15304                 ]
15305             }
15306         ]
15307     },
15308     
15309     listItemCheckbox : {
15310         tag: 'li',
15311         cls: 'list-group-item',
15312         cn: [
15313             {
15314                 tag: 'span',
15315                 cls: 'roo-combobox-list-group-item-value'
15316             },
15317             {
15318                 tag: 'div',
15319                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15320                 cn: [
15321                     {
15322                         tag: 'input',
15323                         type: 'checkbox'
15324                     },
15325                     {
15326                         tag: 'label'
15327                     }
15328                 ]
15329             }
15330         ]
15331     },
15332     
15333     emptyResult : {
15334         tag: 'div',
15335         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15336     },
15337     
15338     footer : {
15339         tag: 'div',
15340         cls: 'modal-footer',
15341         cn: [
15342             {
15343                 tag: 'div',
15344                 cls: 'row',
15345                 cn: [
15346                     {
15347                         tag: 'div',
15348                         cls: 'col-xs-6 text-left',
15349                         cn: {
15350                             tag: 'button',
15351                             cls: 'btn btn-danger roo-touch-view-cancel',
15352                             html: 'Cancel'
15353                         }
15354                     },
15355                     {
15356                         tag: 'div',
15357                         cls: 'col-xs-6 text-right',
15358                         cn: {
15359                             tag: 'button',
15360                             cls: 'btn btn-success roo-touch-view-ok',
15361                             html: 'OK'
15362                         }
15363                     }
15364                 ]
15365             }
15366         ]
15367         
15368     }
15369 });
15370
15371 Roo.apply(Roo.bootstrap.ComboBox,  {
15372     
15373     touchViewTemplate : {
15374         tag: 'div',
15375         cls: 'modal fade roo-combobox-touch-view',
15376         cn: [
15377             {
15378                 tag: 'div',
15379                 cls: 'modal-dialog',
15380                 style : 'position:fixed', // we have to fix position....
15381                 cn: [
15382                     {
15383                         tag: 'div',
15384                         cls: 'modal-content',
15385                         cn: [
15386                             Roo.bootstrap.ComboBox.header,
15387                             Roo.bootstrap.ComboBox.body,
15388                             Roo.bootstrap.ComboBox.footer
15389                         ]
15390                     }
15391                 ]
15392             }
15393         ]
15394     }
15395 });/*
15396  * Based on:
15397  * Ext JS Library 1.1.1
15398  * Copyright(c) 2006-2007, Ext JS, LLC.
15399  *
15400  * Originally Released Under LGPL - original licence link has changed is not relivant.
15401  *
15402  * Fork - LGPL
15403  * <script type="text/javascript">
15404  */
15405
15406 /**
15407  * @class Roo.View
15408  * @extends Roo.util.Observable
15409  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15410  * This class also supports single and multi selection modes. <br>
15411  * Create a data model bound view:
15412  <pre><code>
15413  var store = new Roo.data.Store(...);
15414
15415  var view = new Roo.View({
15416     el : "my-element",
15417     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15418  
15419     singleSelect: true,
15420     selectedClass: "ydataview-selected",
15421     store: store
15422  });
15423
15424  // listen for node click?
15425  view.on("click", function(vw, index, node, e){
15426  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15427  });
15428
15429  // load XML data
15430  dataModel.load("foobar.xml");
15431  </code></pre>
15432  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15433  * <br><br>
15434  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15435  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15436  * 
15437  * Note: old style constructor is still suported (container, template, config)
15438  * 
15439  * @constructor
15440  * Create a new View
15441  * @param {Object} config The config object
15442  * 
15443  */
15444 Roo.View = function(config, depreciated_tpl, depreciated_config){
15445     
15446     this.parent = false;
15447     
15448     if (typeof(depreciated_tpl) == 'undefined') {
15449         // new way.. - universal constructor.
15450         Roo.apply(this, config);
15451         this.el  = Roo.get(this.el);
15452     } else {
15453         // old format..
15454         this.el  = Roo.get(config);
15455         this.tpl = depreciated_tpl;
15456         Roo.apply(this, depreciated_config);
15457     }
15458     this.wrapEl  = this.el.wrap().wrap();
15459     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15460     
15461     
15462     if(typeof(this.tpl) == "string"){
15463         this.tpl = new Roo.Template(this.tpl);
15464     } else {
15465         // support xtype ctors..
15466         this.tpl = new Roo.factory(this.tpl, Roo);
15467     }
15468     
15469     
15470     this.tpl.compile();
15471     
15472     /** @private */
15473     this.addEvents({
15474         /**
15475          * @event beforeclick
15476          * Fires before a click is processed. Returns false to cancel the default action.
15477          * @param {Roo.View} this
15478          * @param {Number} index The index of the target node
15479          * @param {HTMLElement} node The target node
15480          * @param {Roo.EventObject} e The raw event object
15481          */
15482             "beforeclick" : true,
15483         /**
15484          * @event click
15485          * Fires when a template node is clicked.
15486          * @param {Roo.View} this
15487          * @param {Number} index The index of the target node
15488          * @param {HTMLElement} node The target node
15489          * @param {Roo.EventObject} e The raw event object
15490          */
15491             "click" : true,
15492         /**
15493          * @event dblclick
15494          * Fires when a template node is double clicked.
15495          * @param {Roo.View} this
15496          * @param {Number} index The index of the target node
15497          * @param {HTMLElement} node The target node
15498          * @param {Roo.EventObject} e The raw event object
15499          */
15500             "dblclick" : true,
15501         /**
15502          * @event contextmenu
15503          * Fires when a template node is right clicked.
15504          * @param {Roo.View} this
15505          * @param {Number} index The index of the target node
15506          * @param {HTMLElement} node The target node
15507          * @param {Roo.EventObject} e The raw event object
15508          */
15509             "contextmenu" : true,
15510         /**
15511          * @event selectionchange
15512          * Fires when the selected nodes change.
15513          * @param {Roo.View} this
15514          * @param {Array} selections Array of the selected nodes
15515          */
15516             "selectionchange" : true,
15517     
15518         /**
15519          * @event beforeselect
15520          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15521          * @param {Roo.View} this
15522          * @param {HTMLElement} node The node to be selected
15523          * @param {Array} selections Array of currently selected nodes
15524          */
15525             "beforeselect" : true,
15526         /**
15527          * @event preparedata
15528          * Fires on every row to render, to allow you to change the data.
15529          * @param {Roo.View} this
15530          * @param {Object} data to be rendered (change this)
15531          */
15532           "preparedata" : true
15533           
15534           
15535         });
15536
15537
15538
15539     this.el.on({
15540         "click": this.onClick,
15541         "dblclick": this.onDblClick,
15542         "contextmenu": this.onContextMenu,
15543         scope:this
15544     });
15545
15546     this.selections = [];
15547     this.nodes = [];
15548     this.cmp = new Roo.CompositeElementLite([]);
15549     if(this.store){
15550         this.store = Roo.factory(this.store, Roo.data);
15551         this.setStore(this.store, true);
15552     }
15553     
15554     if ( this.footer && this.footer.xtype) {
15555            
15556          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15557         
15558         this.footer.dataSource = this.store;
15559         this.footer.container = fctr;
15560         this.footer = Roo.factory(this.footer, Roo);
15561         fctr.insertFirst(this.el);
15562         
15563         // this is a bit insane - as the paging toolbar seems to detach the el..
15564 //        dom.parentNode.parentNode.parentNode
15565          // they get detached?
15566     }
15567     
15568     
15569     Roo.View.superclass.constructor.call(this);
15570     
15571     
15572 };
15573
15574 Roo.extend(Roo.View, Roo.util.Observable, {
15575     
15576      /**
15577      * @cfg {Roo.data.Store} store Data store to load data from.
15578      */
15579     store : false,
15580     
15581     /**
15582      * @cfg {String|Roo.Element} el The container element.
15583      */
15584     el : '',
15585     
15586     /**
15587      * @cfg {String|Roo.Template} tpl The template used by this View 
15588      */
15589     tpl : false,
15590     /**
15591      * @cfg {String} dataName the named area of the template to use as the data area
15592      *                          Works with domtemplates roo-name="name"
15593      */
15594     dataName: false,
15595     /**
15596      * @cfg {String} selectedClass The css class to add to selected nodes
15597      */
15598     selectedClass : "x-view-selected",
15599      /**
15600      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15601      */
15602     emptyText : "",
15603     
15604     /**
15605      * @cfg {String} text to display on mask (default Loading)
15606      */
15607     mask : false,
15608     /**
15609      * @cfg {Boolean} multiSelect Allow multiple selection
15610      */
15611     multiSelect : false,
15612     /**
15613      * @cfg {Boolean} singleSelect Allow single selection
15614      */
15615     singleSelect:  false,
15616     
15617     /**
15618      * @cfg {Boolean} toggleSelect - selecting 
15619      */
15620     toggleSelect : false,
15621     
15622     /**
15623      * @cfg {Boolean} tickable - selecting 
15624      */
15625     tickable : false,
15626     
15627     /**
15628      * Returns the element this view is bound to.
15629      * @return {Roo.Element}
15630      */
15631     getEl : function(){
15632         return this.wrapEl;
15633     },
15634     
15635     
15636
15637     /**
15638      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15639      */
15640     refresh : function(){
15641         //Roo.log('refresh');
15642         var t = this.tpl;
15643         
15644         // if we are using something like 'domtemplate', then
15645         // the what gets used is:
15646         // t.applySubtemplate(NAME, data, wrapping data..)
15647         // the outer template then get' applied with
15648         //     the store 'extra data'
15649         // and the body get's added to the
15650         //      roo-name="data" node?
15651         //      <span class='roo-tpl-{name}'></span> ?????
15652         
15653         
15654         
15655         this.clearSelections();
15656         this.el.update("");
15657         var html = [];
15658         var records = this.store.getRange();
15659         if(records.length < 1) {
15660             
15661             // is this valid??  = should it render a template??
15662             
15663             this.el.update(this.emptyText);
15664             return;
15665         }
15666         var el = this.el;
15667         if (this.dataName) {
15668             this.el.update(t.apply(this.store.meta)); //????
15669             el = this.el.child('.roo-tpl-' + this.dataName);
15670         }
15671         
15672         for(var i = 0, len = records.length; i < len; i++){
15673             var data = this.prepareData(records[i].data, i, records[i]);
15674             this.fireEvent("preparedata", this, data, i, records[i]);
15675             
15676             var d = Roo.apply({}, data);
15677             
15678             if(this.tickable){
15679                 Roo.apply(d, {'roo-id' : Roo.id()});
15680                 
15681                 var _this = this;
15682             
15683                 Roo.each(this.parent.item, function(item){
15684                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15685                         return;
15686                     }
15687                     Roo.apply(d, {'roo-data-checked' : 'checked'});
15688                 });
15689             }
15690             
15691             html[html.length] = Roo.util.Format.trim(
15692                 this.dataName ?
15693                     t.applySubtemplate(this.dataName, d, this.store.meta) :
15694                     t.apply(d)
15695             );
15696         }
15697         
15698         
15699         
15700         el.update(html.join(""));
15701         this.nodes = el.dom.childNodes;
15702         this.updateIndexes(0);
15703     },
15704     
15705
15706     /**
15707      * Function to override to reformat the data that is sent to
15708      * the template for each node.
15709      * DEPRICATED - use the preparedata event handler.
15710      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15711      * a JSON object for an UpdateManager bound view).
15712      */
15713     prepareData : function(data, index, record)
15714     {
15715         this.fireEvent("preparedata", this, data, index, record);
15716         return data;
15717     },
15718
15719     onUpdate : function(ds, record){
15720         // Roo.log('on update');   
15721         this.clearSelections();
15722         var index = this.store.indexOf(record);
15723         var n = this.nodes[index];
15724         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15725         n.parentNode.removeChild(n);
15726         this.updateIndexes(index, index);
15727     },
15728
15729     
15730     
15731 // --------- FIXME     
15732     onAdd : function(ds, records, index)
15733     {
15734         //Roo.log(['on Add', ds, records, index] );        
15735         this.clearSelections();
15736         if(this.nodes.length == 0){
15737             this.refresh();
15738             return;
15739         }
15740         var n = this.nodes[index];
15741         for(var i = 0, len = records.length; i < len; i++){
15742             var d = this.prepareData(records[i].data, i, records[i]);
15743             if(n){
15744                 this.tpl.insertBefore(n, d);
15745             }else{
15746                 
15747                 this.tpl.append(this.el, d);
15748             }
15749         }
15750         this.updateIndexes(index);
15751     },
15752
15753     onRemove : function(ds, record, index){
15754        // Roo.log('onRemove');
15755         this.clearSelections();
15756         var el = this.dataName  ?
15757             this.el.child('.roo-tpl-' + this.dataName) :
15758             this.el; 
15759         
15760         el.dom.removeChild(this.nodes[index]);
15761         this.updateIndexes(index);
15762     },
15763
15764     /**
15765      * Refresh an individual node.
15766      * @param {Number} index
15767      */
15768     refreshNode : function(index){
15769         this.onUpdate(this.store, this.store.getAt(index));
15770     },
15771
15772     updateIndexes : function(startIndex, endIndex){
15773         var ns = this.nodes;
15774         startIndex = startIndex || 0;
15775         endIndex = endIndex || ns.length - 1;
15776         for(var i = startIndex; i <= endIndex; i++){
15777             ns[i].nodeIndex = i;
15778         }
15779     },
15780
15781     /**
15782      * Changes the data store this view uses and refresh the view.
15783      * @param {Store} store
15784      */
15785     setStore : function(store, initial){
15786         if(!initial && this.store){
15787             this.store.un("datachanged", this.refresh);
15788             this.store.un("add", this.onAdd);
15789             this.store.un("remove", this.onRemove);
15790             this.store.un("update", this.onUpdate);
15791             this.store.un("clear", this.refresh);
15792             this.store.un("beforeload", this.onBeforeLoad);
15793             this.store.un("load", this.onLoad);
15794             this.store.un("loadexception", this.onLoad);
15795         }
15796         if(store){
15797           
15798             store.on("datachanged", this.refresh, this);
15799             store.on("add", this.onAdd, this);
15800             store.on("remove", this.onRemove, this);
15801             store.on("update", this.onUpdate, this);
15802             store.on("clear", this.refresh, this);
15803             store.on("beforeload", this.onBeforeLoad, this);
15804             store.on("load", this.onLoad, this);
15805             store.on("loadexception", this.onLoad, this);
15806         }
15807         
15808         if(store){
15809             this.refresh();
15810         }
15811     },
15812     /**
15813      * onbeforeLoad - masks the loading area.
15814      *
15815      */
15816     onBeforeLoad : function(store,opts)
15817     {
15818          //Roo.log('onBeforeLoad');   
15819         if (!opts.add) {
15820             this.el.update("");
15821         }
15822         this.el.mask(this.mask ? this.mask : "Loading" ); 
15823     },
15824     onLoad : function ()
15825     {
15826         this.el.unmask();
15827     },
15828     
15829
15830     /**
15831      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15832      * @param {HTMLElement} node
15833      * @return {HTMLElement} The template node
15834      */
15835     findItemFromChild : function(node){
15836         var el = this.dataName  ?
15837             this.el.child('.roo-tpl-' + this.dataName,true) :
15838             this.el.dom; 
15839         
15840         if(!node || node.parentNode == el){
15841                     return node;
15842             }
15843             var p = node.parentNode;
15844             while(p && p != el){
15845             if(p.parentNode == el){
15846                 return p;
15847             }
15848             p = p.parentNode;
15849         }
15850             return null;
15851     },
15852
15853     /** @ignore */
15854     onClick : function(e){
15855         var item = this.findItemFromChild(e.getTarget());
15856         if(item){
15857             var index = this.indexOf(item);
15858             if(this.onItemClick(item, index, e) !== false){
15859                 this.fireEvent("click", this, index, item, e);
15860             }
15861         }else{
15862             this.clearSelections();
15863         }
15864     },
15865
15866     /** @ignore */
15867     onContextMenu : function(e){
15868         var item = this.findItemFromChild(e.getTarget());
15869         if(item){
15870             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15871         }
15872     },
15873
15874     /** @ignore */
15875     onDblClick : function(e){
15876         var item = this.findItemFromChild(e.getTarget());
15877         if(item){
15878             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15879         }
15880     },
15881
15882     onItemClick : function(item, index, e)
15883     {
15884         if(this.fireEvent("beforeclick", this, index, item, e) === false){
15885             return false;
15886         }
15887         if (this.toggleSelect) {
15888             var m = this.isSelected(item) ? 'unselect' : 'select';
15889             //Roo.log(m);
15890             var _t = this;
15891             _t[m](item, true, false);
15892             return true;
15893         }
15894         if(this.multiSelect || this.singleSelect){
15895             if(this.multiSelect && e.shiftKey && this.lastSelection){
15896                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15897             }else{
15898                 this.select(item, this.multiSelect && e.ctrlKey);
15899                 this.lastSelection = item;
15900             }
15901             
15902             if(!this.tickable){
15903                 e.preventDefault();
15904             }
15905             
15906         }
15907         return true;
15908     },
15909
15910     /**
15911      * Get the number of selected nodes.
15912      * @return {Number}
15913      */
15914     getSelectionCount : function(){
15915         return this.selections.length;
15916     },
15917
15918     /**
15919      * Get the currently selected nodes.
15920      * @return {Array} An array of HTMLElements
15921      */
15922     getSelectedNodes : function(){
15923         return this.selections;
15924     },
15925
15926     /**
15927      * Get the indexes of the selected nodes.
15928      * @return {Array}
15929      */
15930     getSelectedIndexes : function(){
15931         var indexes = [], s = this.selections;
15932         for(var i = 0, len = s.length; i < len; i++){
15933             indexes.push(s[i].nodeIndex);
15934         }
15935         return indexes;
15936     },
15937
15938     /**
15939      * Clear all selections
15940      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
15941      */
15942     clearSelections : function(suppressEvent){
15943         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
15944             this.cmp.elements = this.selections;
15945             this.cmp.removeClass(this.selectedClass);
15946             this.selections = [];
15947             if(!suppressEvent){
15948                 this.fireEvent("selectionchange", this, this.selections);
15949             }
15950         }
15951     },
15952
15953     /**
15954      * Returns true if the passed node is selected
15955      * @param {HTMLElement/Number} node The node or node index
15956      * @return {Boolean}
15957      */
15958     isSelected : function(node){
15959         var s = this.selections;
15960         if(s.length < 1){
15961             return false;
15962         }
15963         node = this.getNode(node);
15964         return s.indexOf(node) !== -1;
15965     },
15966
15967     /**
15968      * Selects nodes.
15969      * @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
15970      * @param {Boolean} keepExisting (optional) true to keep existing selections
15971      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15972      */
15973     select : function(nodeInfo, keepExisting, suppressEvent){
15974         if(nodeInfo instanceof Array){
15975             if(!keepExisting){
15976                 this.clearSelections(true);
15977             }
15978             for(var i = 0, len = nodeInfo.length; i < len; i++){
15979                 this.select(nodeInfo[i], true, true);
15980             }
15981             return;
15982         } 
15983         var node = this.getNode(nodeInfo);
15984         if(!node || this.isSelected(node)){
15985             return; // already selected.
15986         }
15987         if(!keepExisting){
15988             this.clearSelections(true);
15989         }
15990         
15991         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
15992             Roo.fly(node).addClass(this.selectedClass);
15993             this.selections.push(node);
15994             if(!suppressEvent){
15995                 this.fireEvent("selectionchange", this, this.selections);
15996             }
15997         }
15998         
15999         
16000     },
16001       /**
16002      * Unselects nodes.
16003      * @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
16004      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16005      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16006      */
16007     unselect : function(nodeInfo, keepExisting, suppressEvent)
16008     {
16009         if(nodeInfo instanceof Array){
16010             Roo.each(this.selections, function(s) {
16011                 this.unselect(s, nodeInfo);
16012             }, this);
16013             return;
16014         }
16015         var node = this.getNode(nodeInfo);
16016         if(!node || !this.isSelected(node)){
16017             //Roo.log("not selected");
16018             return; // not selected.
16019         }
16020         // fireevent???
16021         var ns = [];
16022         Roo.each(this.selections, function(s) {
16023             if (s == node ) {
16024                 Roo.fly(node).removeClass(this.selectedClass);
16025
16026                 return;
16027             }
16028             ns.push(s);
16029         },this);
16030         
16031         this.selections= ns;
16032         this.fireEvent("selectionchange", this, this.selections);
16033     },
16034
16035     /**
16036      * Gets a template node.
16037      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16038      * @return {HTMLElement} The node or null if it wasn't found
16039      */
16040     getNode : function(nodeInfo){
16041         if(typeof nodeInfo == "string"){
16042             return document.getElementById(nodeInfo);
16043         }else if(typeof nodeInfo == "number"){
16044             return this.nodes[nodeInfo];
16045         }
16046         return nodeInfo;
16047     },
16048
16049     /**
16050      * Gets a range template nodes.
16051      * @param {Number} startIndex
16052      * @param {Number} endIndex
16053      * @return {Array} An array of nodes
16054      */
16055     getNodes : function(start, end){
16056         var ns = this.nodes;
16057         start = start || 0;
16058         end = typeof end == "undefined" ? ns.length - 1 : end;
16059         var nodes = [];
16060         if(start <= end){
16061             for(var i = start; i <= end; i++){
16062                 nodes.push(ns[i]);
16063             }
16064         } else{
16065             for(var i = start; i >= end; i--){
16066                 nodes.push(ns[i]);
16067             }
16068         }
16069         return nodes;
16070     },
16071
16072     /**
16073      * Finds the index of the passed node
16074      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16075      * @return {Number} The index of the node or -1
16076      */
16077     indexOf : function(node){
16078         node = this.getNode(node);
16079         if(typeof node.nodeIndex == "number"){
16080             return node.nodeIndex;
16081         }
16082         var ns = this.nodes;
16083         for(var i = 0, len = ns.length; i < len; i++){
16084             if(ns[i] == node){
16085                 return i;
16086             }
16087         }
16088         return -1;
16089     }
16090 });
16091 /*
16092  * - LGPL
16093  *
16094  * based on jquery fullcalendar
16095  * 
16096  */
16097
16098 Roo.bootstrap = Roo.bootstrap || {};
16099 /**
16100  * @class Roo.bootstrap.Calendar
16101  * @extends Roo.bootstrap.Component
16102  * Bootstrap Calendar class
16103  * @cfg {Boolean} loadMask (true|false) default false
16104  * @cfg {Object} header generate the user specific header of the calendar, default false
16105
16106  * @constructor
16107  * Create a new Container
16108  * @param {Object} config The config object
16109  */
16110
16111
16112
16113 Roo.bootstrap.Calendar = function(config){
16114     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16115      this.addEvents({
16116         /**
16117              * @event select
16118              * Fires when a date is selected
16119              * @param {DatePicker} this
16120              * @param {Date} date The selected date
16121              */
16122         'select': true,
16123         /**
16124              * @event monthchange
16125              * Fires when the displayed month changes 
16126              * @param {DatePicker} this
16127              * @param {Date} date The selected month
16128              */
16129         'monthchange': true,
16130         /**
16131              * @event evententer
16132              * Fires when mouse over an event
16133              * @param {Calendar} this
16134              * @param {event} Event
16135              */
16136         'evententer': true,
16137         /**
16138              * @event eventleave
16139              * Fires when the mouse leaves an
16140              * @param {Calendar} this
16141              * @param {event}
16142              */
16143         'eventleave': true,
16144         /**
16145              * @event eventclick
16146              * Fires when the mouse click an
16147              * @param {Calendar} this
16148              * @param {event}
16149              */
16150         'eventclick': true
16151         
16152     });
16153
16154 };
16155
16156 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16157     
16158      /**
16159      * @cfg {Number} startDay
16160      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16161      */
16162     startDay : 0,
16163     
16164     loadMask : false,
16165     
16166     header : false,
16167       
16168     getAutoCreate : function(){
16169         
16170         
16171         var fc_button = function(name, corner, style, content ) {
16172             return Roo.apply({},{
16173                 tag : 'span',
16174                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16175                          (corner.length ?
16176                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16177                             ''
16178                         ),
16179                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16180                 unselectable: 'on'
16181             });
16182         };
16183         
16184         var header = {};
16185         
16186         if(!this.header){
16187             header = {
16188                 tag : 'table',
16189                 cls : 'fc-header',
16190                 style : 'width:100%',
16191                 cn : [
16192                     {
16193                         tag: 'tr',
16194                         cn : [
16195                             {
16196                                 tag : 'td',
16197                                 cls : 'fc-header-left',
16198                                 cn : [
16199                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16200                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16201                                     { tag: 'span', cls: 'fc-header-space' },
16202                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16203
16204
16205                                 ]
16206                             },
16207
16208                             {
16209                                 tag : 'td',
16210                                 cls : 'fc-header-center',
16211                                 cn : [
16212                                     {
16213                                         tag: 'span',
16214                                         cls: 'fc-header-title',
16215                                         cn : {
16216                                             tag: 'H2',
16217                                             html : 'month / year'
16218                                         }
16219                                     }
16220
16221                                 ]
16222                             },
16223                             {
16224                                 tag : 'td',
16225                                 cls : 'fc-header-right',
16226                                 cn : [
16227                               /*      fc_button('month', 'left', '', 'month' ),
16228                                     fc_button('week', '', '', 'week' ),
16229                                     fc_button('day', 'right', '', 'day' )
16230                                 */    
16231
16232                                 ]
16233                             }
16234
16235                         ]
16236                     }
16237                 ]
16238             };
16239         }
16240         
16241         header = this.header;
16242         
16243        
16244         var cal_heads = function() {
16245             var ret = [];
16246             // fixme - handle this.
16247             
16248             for (var i =0; i < Date.dayNames.length; i++) {
16249                 var d = Date.dayNames[i];
16250                 ret.push({
16251                     tag: 'th',
16252                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16253                     html : d.substring(0,3)
16254                 });
16255                 
16256             }
16257             ret[0].cls += ' fc-first';
16258             ret[6].cls += ' fc-last';
16259             return ret;
16260         };
16261         var cal_cell = function(n) {
16262             return  {
16263                 tag: 'td',
16264                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16265                 cn : [
16266                     {
16267                         cn : [
16268                             {
16269                                 cls: 'fc-day-number',
16270                                 html: 'D'
16271                             },
16272                             {
16273                                 cls: 'fc-day-content',
16274                              
16275                                 cn : [
16276                                      {
16277                                         style: 'position: relative;' // height: 17px;
16278                                     }
16279                                 ]
16280                             }
16281                             
16282                             
16283                         ]
16284                     }
16285                 ]
16286                 
16287             }
16288         };
16289         var cal_rows = function() {
16290             
16291             var ret = [];
16292             for (var r = 0; r < 6; r++) {
16293                 var row= {
16294                     tag : 'tr',
16295                     cls : 'fc-week',
16296                     cn : []
16297                 };
16298                 
16299                 for (var i =0; i < Date.dayNames.length; i++) {
16300                     var d = Date.dayNames[i];
16301                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16302
16303                 }
16304                 row.cn[0].cls+=' fc-first';
16305                 row.cn[0].cn[0].style = 'min-height:90px';
16306                 row.cn[6].cls+=' fc-last';
16307                 ret.push(row);
16308                 
16309             }
16310             ret[0].cls += ' fc-first';
16311             ret[4].cls += ' fc-prev-last';
16312             ret[5].cls += ' fc-last';
16313             return ret;
16314             
16315         };
16316         
16317         var cal_table = {
16318             tag: 'table',
16319             cls: 'fc-border-separate',
16320             style : 'width:100%',
16321             cellspacing  : 0,
16322             cn : [
16323                 { 
16324                     tag: 'thead',
16325                     cn : [
16326                         { 
16327                             tag: 'tr',
16328                             cls : 'fc-first fc-last',
16329                             cn : cal_heads()
16330                         }
16331                     ]
16332                 },
16333                 { 
16334                     tag: 'tbody',
16335                     cn : cal_rows()
16336                 }
16337                   
16338             ]
16339         };
16340          
16341          var cfg = {
16342             cls : 'fc fc-ltr',
16343             cn : [
16344                 header,
16345                 {
16346                     cls : 'fc-content',
16347                     style : "position: relative;",
16348                     cn : [
16349                         {
16350                             cls : 'fc-view fc-view-month fc-grid',
16351                             style : 'position: relative',
16352                             unselectable : 'on',
16353                             cn : [
16354                                 {
16355                                     cls : 'fc-event-container',
16356                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16357                                 },
16358                                 cal_table
16359                             ]
16360                         }
16361                     ]
16362     
16363                 }
16364            ] 
16365             
16366         };
16367         
16368          
16369         
16370         return cfg;
16371     },
16372     
16373     
16374     initEvents : function()
16375     {
16376         if(!this.store){
16377             throw "can not find store for calendar";
16378         }
16379         
16380         var mark = {
16381             tag: "div",
16382             cls:"x-dlg-mask",
16383             style: "text-align:center",
16384             cn: [
16385                 {
16386                     tag: "div",
16387                     style: "background-color:white;width:50%;margin:250 auto",
16388                     cn: [
16389                         {
16390                             tag: "img",
16391                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16392                         },
16393                         {
16394                             tag: "span",
16395                             html: "Loading"
16396                         }
16397                         
16398                     ]
16399                 }
16400             ]
16401         };
16402         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16403         
16404         var size = this.el.select('.fc-content', true).first().getSize();
16405         this.maskEl.setSize(size.width, size.height);
16406         this.maskEl.enableDisplayMode("block");
16407         if(!this.loadMask){
16408             this.maskEl.hide();
16409         }
16410         
16411         this.store = Roo.factory(this.store, Roo.data);
16412         this.store.on('load', this.onLoad, this);
16413         this.store.on('beforeload', this.onBeforeLoad, this);
16414         
16415         this.resize();
16416         
16417         this.cells = this.el.select('.fc-day',true);
16418         //Roo.log(this.cells);
16419         this.textNodes = this.el.query('.fc-day-number');
16420         this.cells.addClassOnOver('fc-state-hover');
16421         
16422         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16423         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16424         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16425         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16426         
16427         this.on('monthchange', this.onMonthChange, this);
16428         
16429         this.update(new Date().clearTime());
16430     },
16431     
16432     resize : function() {
16433         var sz  = this.el.getSize();
16434         
16435         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16436         this.el.select('.fc-day-content div',true).setHeight(34);
16437     },
16438     
16439     
16440     // private
16441     showPrevMonth : function(e){
16442         this.update(this.activeDate.add("mo", -1));
16443     },
16444     showToday : function(e){
16445         this.update(new Date().clearTime());
16446     },
16447     // private
16448     showNextMonth : function(e){
16449         this.update(this.activeDate.add("mo", 1));
16450     },
16451
16452     // private
16453     showPrevYear : function(){
16454         this.update(this.activeDate.add("y", -1));
16455     },
16456
16457     // private
16458     showNextYear : function(){
16459         this.update(this.activeDate.add("y", 1));
16460     },
16461
16462     
16463    // private
16464     update : function(date)
16465     {
16466         var vd = this.activeDate;
16467         this.activeDate = date;
16468 //        if(vd && this.el){
16469 //            var t = date.getTime();
16470 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16471 //                Roo.log('using add remove');
16472 //                
16473 //                this.fireEvent('monthchange', this, date);
16474 //                
16475 //                this.cells.removeClass("fc-state-highlight");
16476 //                this.cells.each(function(c){
16477 //                   if(c.dateValue == t){
16478 //                       c.addClass("fc-state-highlight");
16479 //                       setTimeout(function(){
16480 //                            try{c.dom.firstChild.focus();}catch(e){}
16481 //                       }, 50);
16482 //                       return false;
16483 //                   }
16484 //                   return true;
16485 //                });
16486 //                return;
16487 //            }
16488 //        }
16489         
16490         var days = date.getDaysInMonth();
16491         
16492         var firstOfMonth = date.getFirstDateOfMonth();
16493         var startingPos = firstOfMonth.getDay()-this.startDay;
16494         
16495         if(startingPos < this.startDay){
16496             startingPos += 7;
16497         }
16498         
16499         var pm = date.add(Date.MONTH, -1);
16500         var prevStart = pm.getDaysInMonth()-startingPos;
16501 //        
16502         this.cells = this.el.select('.fc-day',true);
16503         this.textNodes = this.el.query('.fc-day-number');
16504         this.cells.addClassOnOver('fc-state-hover');
16505         
16506         var cells = this.cells.elements;
16507         var textEls = this.textNodes;
16508         
16509         Roo.each(cells, function(cell){
16510             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16511         });
16512         
16513         days += startingPos;
16514
16515         // convert everything to numbers so it's fast
16516         var day = 86400000;
16517         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16518         //Roo.log(d);
16519         //Roo.log(pm);
16520         //Roo.log(prevStart);
16521         
16522         var today = new Date().clearTime().getTime();
16523         var sel = date.clearTime().getTime();
16524         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16525         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16526         var ddMatch = this.disabledDatesRE;
16527         var ddText = this.disabledDatesText;
16528         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16529         var ddaysText = this.disabledDaysText;
16530         var format = this.format;
16531         
16532         var setCellClass = function(cal, cell){
16533             cell.row = 0;
16534             cell.events = [];
16535             cell.more = [];
16536             //Roo.log('set Cell Class');
16537             cell.title = "";
16538             var t = d.getTime();
16539             
16540             //Roo.log(d);
16541             
16542             cell.dateValue = t;
16543             if(t == today){
16544                 cell.className += " fc-today";
16545                 cell.className += " fc-state-highlight";
16546                 cell.title = cal.todayText;
16547             }
16548             if(t == sel){
16549                 // disable highlight in other month..
16550                 //cell.className += " fc-state-highlight";
16551                 
16552             }
16553             // disabling
16554             if(t < min) {
16555                 cell.className = " fc-state-disabled";
16556                 cell.title = cal.minText;
16557                 return;
16558             }
16559             if(t > max) {
16560                 cell.className = " fc-state-disabled";
16561                 cell.title = cal.maxText;
16562                 return;
16563             }
16564             if(ddays){
16565                 if(ddays.indexOf(d.getDay()) != -1){
16566                     cell.title = ddaysText;
16567                     cell.className = " fc-state-disabled";
16568                 }
16569             }
16570             if(ddMatch && format){
16571                 var fvalue = d.dateFormat(format);
16572                 if(ddMatch.test(fvalue)){
16573                     cell.title = ddText.replace("%0", fvalue);
16574                     cell.className = " fc-state-disabled";
16575                 }
16576             }
16577             
16578             if (!cell.initialClassName) {
16579                 cell.initialClassName = cell.dom.className;
16580             }
16581             
16582             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16583         };
16584
16585         var i = 0;
16586         
16587         for(; i < startingPos; i++) {
16588             textEls[i].innerHTML = (++prevStart);
16589             d.setDate(d.getDate()+1);
16590             
16591             cells[i].className = "fc-past fc-other-month";
16592             setCellClass(this, cells[i]);
16593         }
16594         
16595         var intDay = 0;
16596         
16597         for(; i < days; i++){
16598             intDay = i - startingPos + 1;
16599             textEls[i].innerHTML = (intDay);
16600             d.setDate(d.getDate()+1);
16601             
16602             cells[i].className = ''; // "x-date-active";
16603             setCellClass(this, cells[i]);
16604         }
16605         var extraDays = 0;
16606         
16607         for(; i < 42; i++) {
16608             textEls[i].innerHTML = (++extraDays);
16609             d.setDate(d.getDate()+1);
16610             
16611             cells[i].className = "fc-future fc-other-month";
16612             setCellClass(this, cells[i]);
16613         }
16614         
16615         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16616         
16617         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16618         
16619         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16620         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16621         
16622         if(totalRows != 6){
16623             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16624             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16625         }
16626         
16627         this.fireEvent('monthchange', this, date);
16628         
16629         
16630         /*
16631         if(!this.internalRender){
16632             var main = this.el.dom.firstChild;
16633             var w = main.offsetWidth;
16634             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16635             Roo.fly(main).setWidth(w);
16636             this.internalRender = true;
16637             // opera does not respect the auto grow header center column
16638             // then, after it gets a width opera refuses to recalculate
16639             // without a second pass
16640             if(Roo.isOpera && !this.secondPass){
16641                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16642                 this.secondPass = true;
16643                 this.update.defer(10, this, [date]);
16644             }
16645         }
16646         */
16647         
16648     },
16649     
16650     findCell : function(dt) {
16651         dt = dt.clearTime().getTime();
16652         var ret = false;
16653         this.cells.each(function(c){
16654             //Roo.log("check " +c.dateValue + '?=' + dt);
16655             if(c.dateValue == dt){
16656                 ret = c;
16657                 return false;
16658             }
16659             return true;
16660         });
16661         
16662         return ret;
16663     },
16664     
16665     findCells : function(ev) {
16666         var s = ev.start.clone().clearTime().getTime();
16667        // Roo.log(s);
16668         var e= ev.end.clone().clearTime().getTime();
16669        // Roo.log(e);
16670         var ret = [];
16671         this.cells.each(function(c){
16672              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16673             
16674             if(c.dateValue > e){
16675                 return ;
16676             }
16677             if(c.dateValue < s){
16678                 return ;
16679             }
16680             ret.push(c);
16681         });
16682         
16683         return ret;    
16684     },
16685     
16686 //    findBestRow: function(cells)
16687 //    {
16688 //        var ret = 0;
16689 //        
16690 //        for (var i =0 ; i < cells.length;i++) {
16691 //            ret  = Math.max(cells[i].rows || 0,ret);
16692 //        }
16693 //        return ret;
16694 //        
16695 //    },
16696     
16697     
16698     addItem : function(ev)
16699     {
16700         // look for vertical location slot in
16701         var cells = this.findCells(ev);
16702         
16703 //        ev.row = this.findBestRow(cells);
16704         
16705         // work out the location.
16706         
16707         var crow = false;
16708         var rows = [];
16709         for(var i =0; i < cells.length; i++) {
16710             
16711             cells[i].row = cells[0].row;
16712             
16713             if(i == 0){
16714                 cells[i].row = cells[i].row + 1;
16715             }
16716             
16717             if (!crow) {
16718                 crow = {
16719                     start : cells[i],
16720                     end :  cells[i]
16721                 };
16722                 continue;
16723             }
16724             if (crow.start.getY() == cells[i].getY()) {
16725                 // on same row.
16726                 crow.end = cells[i];
16727                 continue;
16728             }
16729             // different row.
16730             rows.push(crow);
16731             crow = {
16732                 start: cells[i],
16733                 end : cells[i]
16734             };
16735             
16736         }
16737         
16738         rows.push(crow);
16739         ev.els = [];
16740         ev.rows = rows;
16741         ev.cells = cells;
16742         
16743         cells[0].events.push(ev);
16744         
16745         this.calevents.push(ev);
16746     },
16747     
16748     clearEvents: function() {
16749         
16750         if(!this.calevents){
16751             return;
16752         }
16753         
16754         Roo.each(this.cells.elements, function(c){
16755             c.row = 0;
16756             c.events = [];
16757             c.more = [];
16758         });
16759         
16760         Roo.each(this.calevents, function(e) {
16761             Roo.each(e.els, function(el) {
16762                 el.un('mouseenter' ,this.onEventEnter, this);
16763                 el.un('mouseleave' ,this.onEventLeave, this);
16764                 el.remove();
16765             },this);
16766         },this);
16767         
16768         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16769             e.remove();
16770         });
16771         
16772     },
16773     
16774     renderEvents: function()
16775     {   
16776         var _this = this;
16777         
16778         this.cells.each(function(c) {
16779             
16780             if(c.row < 5){
16781                 return;
16782             }
16783             
16784             var ev = c.events;
16785             
16786             var r = 4;
16787             if(c.row != c.events.length){
16788                 r = 4 - (4 - (c.row - c.events.length));
16789             }
16790             
16791             c.events = ev.slice(0, r);
16792             c.more = ev.slice(r);
16793             
16794             if(c.more.length && c.more.length == 1){
16795                 c.events.push(c.more.pop());
16796             }
16797             
16798             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16799             
16800         });
16801             
16802         this.cells.each(function(c) {
16803             
16804             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16805             
16806             
16807             for (var e = 0; e < c.events.length; e++){
16808                 var ev = c.events[e];
16809                 var rows = ev.rows;
16810                 
16811                 for(var i = 0; i < rows.length; i++) {
16812                 
16813                     // how many rows should it span..
16814
16815                     var  cfg = {
16816                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16817                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16818
16819                         unselectable : "on",
16820                         cn : [
16821                             {
16822                                 cls: 'fc-event-inner',
16823                                 cn : [
16824     //                                {
16825     //                                  tag:'span',
16826     //                                  cls: 'fc-event-time',
16827     //                                  html : cells.length > 1 ? '' : ev.time
16828     //                                },
16829                                     {
16830                                       tag:'span',
16831                                       cls: 'fc-event-title',
16832                                       html : String.format('{0}', ev.title)
16833                                     }
16834
16835
16836                                 ]
16837                             },
16838                             {
16839                                 cls: 'ui-resizable-handle ui-resizable-e',
16840                                 html : '&nbsp;&nbsp;&nbsp'
16841                             }
16842
16843                         ]
16844                     };
16845
16846                     if (i == 0) {
16847                         cfg.cls += ' fc-event-start';
16848                     }
16849                     if ((i+1) == rows.length) {
16850                         cfg.cls += ' fc-event-end';
16851                     }
16852
16853                     var ctr = _this.el.select('.fc-event-container',true).first();
16854                     var cg = ctr.createChild(cfg);
16855
16856                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16857                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16858
16859                     var r = (c.more.length) ? 1 : 0;
16860                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
16861                     cg.setWidth(ebox.right - sbox.x -2);
16862
16863                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16864                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16865                     cg.on('click', _this.onEventClick, _this, ev);
16866
16867                     ev.els.push(cg);
16868                     
16869                 }
16870                 
16871             }
16872             
16873             
16874             if(c.more.length){
16875                 var  cfg = {
16876                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16877                     style : 'position: absolute',
16878                     unselectable : "on",
16879                     cn : [
16880                         {
16881                             cls: 'fc-event-inner',
16882                             cn : [
16883                                 {
16884                                   tag:'span',
16885                                   cls: 'fc-event-title',
16886                                   html : 'More'
16887                                 }
16888
16889
16890                             ]
16891                         },
16892                         {
16893                             cls: 'ui-resizable-handle ui-resizable-e',
16894                             html : '&nbsp;&nbsp;&nbsp'
16895                         }
16896
16897                     ]
16898                 };
16899
16900                 var ctr = _this.el.select('.fc-event-container',true).first();
16901                 var cg = ctr.createChild(cfg);
16902
16903                 var sbox = c.select('.fc-day-content',true).first().getBox();
16904                 var ebox = c.select('.fc-day-content',true).first().getBox();
16905                 //Roo.log(cg);
16906                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
16907                 cg.setWidth(ebox.right - sbox.x -2);
16908
16909                 cg.on('click', _this.onMoreEventClick, _this, c.more);
16910                 
16911             }
16912             
16913         });
16914         
16915         
16916         
16917     },
16918     
16919     onEventEnter: function (e, el,event,d) {
16920         this.fireEvent('evententer', this, el, event);
16921     },
16922     
16923     onEventLeave: function (e, el,event,d) {
16924         this.fireEvent('eventleave', this, el, event);
16925     },
16926     
16927     onEventClick: function (e, el,event,d) {
16928         this.fireEvent('eventclick', this, el, event);
16929     },
16930     
16931     onMonthChange: function () {
16932         this.store.load();
16933     },
16934     
16935     onMoreEventClick: function(e, el, more)
16936     {
16937         var _this = this;
16938         
16939         this.calpopover.placement = 'right';
16940         this.calpopover.setTitle('More');
16941         
16942         this.calpopover.setContent('');
16943         
16944         var ctr = this.calpopover.el.select('.popover-content', true).first();
16945         
16946         Roo.each(more, function(m){
16947             var cfg = {
16948                 cls : 'fc-event-hori fc-event-draggable',
16949                 html : m.title
16950             };
16951             var cg = ctr.createChild(cfg);
16952             
16953             cg.on('click', _this.onEventClick, _this, m);
16954         });
16955         
16956         this.calpopover.show(el);
16957         
16958         
16959     },
16960     
16961     onLoad: function () 
16962     {   
16963         this.calevents = [];
16964         var cal = this;
16965         
16966         if(this.store.getCount() > 0){
16967             this.store.data.each(function(d){
16968                cal.addItem({
16969                     id : d.data.id,
16970                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
16971                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
16972                     time : d.data.start_time,
16973                     title : d.data.title,
16974                     description : d.data.description,
16975                     venue : d.data.venue
16976                 });
16977             });
16978         }
16979         
16980         this.renderEvents();
16981         
16982         if(this.calevents.length && this.loadMask){
16983             this.maskEl.hide();
16984         }
16985     },
16986     
16987     onBeforeLoad: function()
16988     {
16989         this.clearEvents();
16990         if(this.loadMask){
16991             this.maskEl.show();
16992         }
16993     }
16994 });
16995
16996  
16997  /*
16998  * - LGPL
16999  *
17000  * element
17001  * 
17002  */
17003
17004 /**
17005  * @class Roo.bootstrap.Popover
17006  * @extends Roo.bootstrap.Component
17007  * Bootstrap Popover class
17008  * @cfg {String} html contents of the popover   (or false to use children..)
17009  * @cfg {String} title of popover (or false to hide)
17010  * @cfg {String} placement how it is placed
17011  * @cfg {String} trigger click || hover (or false to trigger manually)
17012  * @cfg {String} over what (parent or false to trigger manually.)
17013  * @cfg {Number} delay - delay before showing
17014  
17015  * @constructor
17016  * Create a new Popover
17017  * @param {Object} config The config object
17018  */
17019
17020 Roo.bootstrap.Popover = function(config){
17021     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17022     
17023     this.addEvents({
17024         // raw events
17025          /**
17026          * @event show
17027          * After the popover show
17028          * 
17029          * @param {Roo.bootstrap.Popover} this
17030          */
17031         "show" : true,
17032         /**
17033          * @event hide
17034          * After the popover hide
17035          * 
17036          * @param {Roo.bootstrap.Popover} this
17037          */
17038         "hide" : true
17039     });
17040 };
17041
17042 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17043     
17044     title: 'Fill in a title',
17045     html: false,
17046     
17047     placement : 'right',
17048     trigger : 'hover', // hover
17049     
17050     delay : 0,
17051     
17052     over: 'parent',
17053     
17054     can_build_overlaid : false,
17055     
17056     getChildContainer : function()
17057     {
17058         return this.el.select('.popover-content',true).first();
17059     },
17060     
17061     getAutoCreate : function(){
17062          
17063         var cfg = {
17064            cls : 'popover roo-dynamic',
17065            style: 'display:block',
17066            cn : [
17067                 {
17068                     cls : 'arrow'
17069                 },
17070                 {
17071                     cls : 'popover-inner',
17072                     cn : [
17073                         {
17074                             tag: 'h3',
17075                             cls: 'popover-title',
17076                             html : this.title
17077                         },
17078                         {
17079                             cls : 'popover-content',
17080                             html : this.html
17081                         }
17082                     ]
17083                     
17084                 }
17085            ]
17086         };
17087         
17088         return cfg;
17089     },
17090     setTitle: function(str)
17091     {
17092         this.title = str;
17093         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17094     },
17095     setContent: function(str)
17096     {
17097         this.html = str;
17098         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17099     },
17100     // as it get's added to the bottom of the page.
17101     onRender : function(ct, position)
17102     {
17103         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17104         if(!this.el){
17105             var cfg = Roo.apply({},  this.getAutoCreate());
17106             cfg.id = Roo.id();
17107             
17108             if (this.cls) {
17109                 cfg.cls += ' ' + this.cls;
17110             }
17111             if (this.style) {
17112                 cfg.style = this.style;
17113             }
17114             //Roo.log("adding to ");
17115             this.el = Roo.get(document.body).createChild(cfg, position);
17116 //            Roo.log(this.el);
17117         }
17118         this.initEvents();
17119     },
17120     
17121     initEvents : function()
17122     {
17123         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17124         this.el.enableDisplayMode('block');
17125         this.el.hide();
17126         if (this.over === false) {
17127             return; 
17128         }
17129         if (this.triggers === false) {
17130             return;
17131         }
17132         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17133         var triggers = this.trigger ? this.trigger.split(' ') : [];
17134         Roo.each(triggers, function(trigger) {
17135         
17136             if (trigger == 'click') {
17137                 on_el.on('click', this.toggle, this);
17138             } else if (trigger != 'manual') {
17139                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17140                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17141       
17142                 on_el.on(eventIn  ,this.enter, this);
17143                 on_el.on(eventOut, this.leave, this);
17144             }
17145         }, this);
17146         
17147     },
17148     
17149     
17150     // private
17151     timeout : null,
17152     hoverState : null,
17153     
17154     toggle : function () {
17155         this.hoverState == 'in' ? this.leave() : this.enter();
17156     },
17157     
17158     enter : function () {
17159         
17160         clearTimeout(this.timeout);
17161     
17162         this.hoverState = 'in';
17163     
17164         if (!this.delay || !this.delay.show) {
17165             this.show();
17166             return;
17167         }
17168         var _t = this;
17169         this.timeout = setTimeout(function () {
17170             if (_t.hoverState == 'in') {
17171                 _t.show();
17172             }
17173         }, this.delay.show)
17174     },
17175     
17176     leave : function() {
17177         clearTimeout(this.timeout);
17178     
17179         this.hoverState = 'out';
17180     
17181         if (!this.delay || !this.delay.hide) {
17182             this.hide();
17183             return;
17184         }
17185         var _t = this;
17186         this.timeout = setTimeout(function () {
17187             if (_t.hoverState == 'out') {
17188                 _t.hide();
17189             }
17190         }, this.delay.hide)
17191     },
17192     
17193     show : function (on_el)
17194     {
17195         if (!on_el) {
17196             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17197         }
17198         
17199         // set content.
17200         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17201         if (this.html !== false) {
17202             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17203         }
17204         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17205         if (!this.title.length) {
17206             this.el.select('.popover-title',true).hide();
17207         }
17208         
17209         var placement = typeof this.placement == 'function' ?
17210             this.placement.call(this, this.el, on_el) :
17211             this.placement;
17212             
17213         var autoToken = /\s?auto?\s?/i;
17214         var autoPlace = autoToken.test(placement);
17215         if (autoPlace) {
17216             placement = placement.replace(autoToken, '') || 'top';
17217         }
17218         
17219         //this.el.detach()
17220         //this.el.setXY([0,0]);
17221         this.el.show();
17222         this.el.dom.style.display='block';
17223         this.el.addClass(placement);
17224         
17225         //this.el.appendTo(on_el);
17226         
17227         var p = this.getPosition();
17228         var box = this.el.getBox();
17229         
17230         if (autoPlace) {
17231             // fixme..
17232         }
17233         var align = Roo.bootstrap.Popover.alignment[placement];
17234         this.el.alignTo(on_el, align[0],align[1]);
17235         //var arrow = this.el.select('.arrow',true).first();
17236         //arrow.set(align[2], 
17237         
17238         this.el.addClass('in');
17239         
17240         
17241         if (this.el.hasClass('fade')) {
17242             // fade it?
17243         }
17244         
17245         this.hoverState = 'in';
17246         
17247         this.fireEvent('show', this);
17248         
17249     },
17250     hide : function()
17251     {
17252         this.el.setXY([0,0]);
17253         this.el.removeClass('in');
17254         this.el.hide();
17255         this.hoverState = null;
17256         
17257         this.fireEvent('hide', this);
17258     }
17259     
17260 });
17261
17262 Roo.bootstrap.Popover.alignment = {
17263     'left' : ['r-l', [-10,0], 'right'],
17264     'right' : ['l-r', [10,0], 'left'],
17265     'bottom' : ['t-b', [0,10], 'top'],
17266     'top' : [ 'b-t', [0,-10], 'bottom']
17267 };
17268
17269  /*
17270  * - LGPL
17271  *
17272  * Progress
17273  * 
17274  */
17275
17276 /**
17277  * @class Roo.bootstrap.Progress
17278  * @extends Roo.bootstrap.Component
17279  * Bootstrap Progress class
17280  * @cfg {Boolean} striped striped of the progress bar
17281  * @cfg {Boolean} active animated of the progress bar
17282  * 
17283  * 
17284  * @constructor
17285  * Create a new Progress
17286  * @param {Object} config The config object
17287  */
17288
17289 Roo.bootstrap.Progress = function(config){
17290     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17291 };
17292
17293 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17294     
17295     striped : false,
17296     active: false,
17297     
17298     getAutoCreate : function(){
17299         var cfg = {
17300             tag: 'div',
17301             cls: 'progress'
17302         };
17303         
17304         
17305         if(this.striped){
17306             cfg.cls += ' progress-striped';
17307         }
17308       
17309         if(this.active){
17310             cfg.cls += ' active';
17311         }
17312         
17313         
17314         return cfg;
17315     }
17316    
17317 });
17318
17319  
17320
17321  /*
17322  * - LGPL
17323  *
17324  * ProgressBar
17325  * 
17326  */
17327
17328 /**
17329  * @class Roo.bootstrap.ProgressBar
17330  * @extends Roo.bootstrap.Component
17331  * Bootstrap ProgressBar class
17332  * @cfg {Number} aria_valuenow aria-value now
17333  * @cfg {Number} aria_valuemin aria-value min
17334  * @cfg {Number} aria_valuemax aria-value max
17335  * @cfg {String} label label for the progress bar
17336  * @cfg {String} panel (success | info | warning | danger )
17337  * @cfg {String} role role of the progress bar
17338  * @cfg {String} sr_only text
17339  * 
17340  * 
17341  * @constructor
17342  * Create a new ProgressBar
17343  * @param {Object} config The config object
17344  */
17345
17346 Roo.bootstrap.ProgressBar = function(config){
17347     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17348 };
17349
17350 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17351     
17352     aria_valuenow : 0,
17353     aria_valuemin : 0,
17354     aria_valuemax : 100,
17355     label : false,
17356     panel : false,
17357     role : false,
17358     sr_only: false,
17359     
17360     getAutoCreate : function()
17361     {
17362         
17363         var cfg = {
17364             tag: 'div',
17365             cls: 'progress-bar',
17366             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17367         };
17368         
17369         if(this.sr_only){
17370             cfg.cn = {
17371                 tag: 'span',
17372                 cls: 'sr-only',
17373                 html: this.sr_only
17374             }
17375         }
17376         
17377         if(this.role){
17378             cfg.role = this.role;
17379         }
17380         
17381         if(this.aria_valuenow){
17382             cfg['aria-valuenow'] = this.aria_valuenow;
17383         }
17384         
17385         if(this.aria_valuemin){
17386             cfg['aria-valuemin'] = this.aria_valuemin;
17387         }
17388         
17389         if(this.aria_valuemax){
17390             cfg['aria-valuemax'] = this.aria_valuemax;
17391         }
17392         
17393         if(this.label && !this.sr_only){
17394             cfg.html = this.label;
17395         }
17396         
17397         if(this.panel){
17398             cfg.cls += ' progress-bar-' + this.panel;
17399         }
17400         
17401         return cfg;
17402     },
17403     
17404     update : function(aria_valuenow)
17405     {
17406         this.aria_valuenow = aria_valuenow;
17407         
17408         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17409     }
17410    
17411 });
17412
17413  
17414
17415  /*
17416  * - LGPL
17417  *
17418  * column
17419  * 
17420  */
17421
17422 /**
17423  * @class Roo.bootstrap.TabGroup
17424  * @extends Roo.bootstrap.Column
17425  * Bootstrap Column class
17426  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17427  * @cfg {Boolean} carousel true to make the group behave like a carousel
17428  * @cfg {Boolean} bullets show bullets for the panels
17429  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17430  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17431  * @cfg {Boolean} showarrow (true|false) show arrow default true
17432  * 
17433  * @constructor
17434  * Create a new TabGroup
17435  * @param {Object} config The config object
17436  */
17437
17438 Roo.bootstrap.TabGroup = function(config){
17439     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17440     if (!this.navId) {
17441         this.navId = Roo.id();
17442     }
17443     this.tabs = [];
17444     Roo.bootstrap.TabGroup.register(this);
17445     
17446 };
17447
17448 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17449     
17450     carousel : false,
17451     transition : false,
17452     bullets : 0,
17453     timer : 0,
17454     autoslide : false,
17455     slideFn : false,
17456     slideOnTouch : false,
17457     showarrow : true,
17458     
17459     getAutoCreate : function()
17460     {
17461         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17462         
17463         cfg.cls += ' tab-content';
17464         
17465         if (this.carousel) {
17466             cfg.cls += ' carousel slide';
17467             
17468             cfg.cn = [{
17469                cls : 'carousel-inner',
17470                cn : []
17471             }];
17472         
17473             if(this.bullets  && !Roo.isTouch){
17474                 
17475                 var bullets = {
17476                     cls : 'carousel-bullets',
17477                     cn : []
17478                 };
17479                
17480                 if(this.bullets_cls){
17481                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17482                 }
17483                 
17484                 bullets.cn.push({
17485                     cls : 'clear'
17486                 });
17487                 
17488                 cfg.cn[0].cn.push(bullets);
17489             }
17490             
17491             if(this.showarrow){
17492                 cfg.cn[0].cn.push({
17493                     tag : 'div',
17494                     class : 'carousel-arrow',
17495                     cn : [
17496                         {
17497                             tag : 'div',
17498                             class : 'carousel-prev',
17499                             cn : [
17500                                 {
17501                                     tag : 'i',
17502                                     class : 'fa fa-chevron-left'
17503                                 }
17504                             ]
17505                         },
17506                         {
17507                             tag : 'div',
17508                             class : 'carousel-next',
17509                             cn : [
17510                                 {
17511                                     tag : 'i',
17512                                     class : 'fa fa-chevron-right'
17513                                 }
17514                             ]
17515                         }
17516                     ]
17517                 });
17518             }
17519             
17520         }
17521         
17522         return cfg;
17523     },
17524     
17525     initEvents:  function()
17526     {
17527 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17528 //            this.el.on("touchstart", this.onTouchStart, this);
17529 //        }
17530         
17531         if(this.autoslide){
17532             var _this = this;
17533             
17534             this.slideFn = window.setInterval(function() {
17535                 _this.showPanelNext();
17536             }, this.timer);
17537         }
17538         
17539         if(this.showarrow){
17540             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17541             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17542         }
17543         
17544         
17545     },
17546     
17547 //    onTouchStart : function(e, el, o)
17548 //    {
17549 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17550 //            return;
17551 //        }
17552 //        
17553 //        this.showPanelNext();
17554 //    },
17555     
17556     
17557     getChildContainer : function()
17558     {
17559         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17560     },
17561     
17562     /**
17563     * register a Navigation item
17564     * @param {Roo.bootstrap.NavItem} the navitem to add
17565     */
17566     register : function(item)
17567     {
17568         this.tabs.push( item);
17569         item.navId = this.navId; // not really needed..
17570         this.addBullet();
17571     
17572     },
17573     
17574     getActivePanel : function()
17575     {
17576         var r = false;
17577         Roo.each(this.tabs, function(t) {
17578             if (t.active) {
17579                 r = t;
17580                 return false;
17581             }
17582             return null;
17583         });
17584         return r;
17585         
17586     },
17587     getPanelByName : function(n)
17588     {
17589         var r = false;
17590         Roo.each(this.tabs, function(t) {
17591             if (t.tabId == n) {
17592                 r = t;
17593                 return false;
17594             }
17595             return null;
17596         });
17597         return r;
17598     },
17599     indexOfPanel : function(p)
17600     {
17601         var r = false;
17602         Roo.each(this.tabs, function(t,i) {
17603             if (t.tabId == p.tabId) {
17604                 r = i;
17605                 return false;
17606             }
17607             return null;
17608         });
17609         return r;
17610     },
17611     /**
17612      * show a specific panel
17613      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17614      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17615      */
17616     showPanel : function (pan)
17617     {
17618         if(this.transition || typeof(pan) == 'undefined'){
17619             Roo.log("waiting for the transitionend");
17620             return;
17621         }
17622         
17623         if (typeof(pan) == 'number') {
17624             pan = this.tabs[pan];
17625         }
17626         
17627         if (typeof(pan) == 'string') {
17628             pan = this.getPanelByName(pan);
17629         }
17630         
17631         var cur = this.getActivePanel();
17632         
17633         if(!pan || !cur){
17634             Roo.log('pan or acitve pan is undefined');
17635             return false;
17636         }
17637         
17638         if (pan.tabId == this.getActivePanel().tabId) {
17639             return true;
17640         }
17641         
17642         if (false === cur.fireEvent('beforedeactivate')) {
17643             return false;
17644         }
17645         
17646         if(this.bullets > 0 && !Roo.isTouch){
17647             this.setActiveBullet(this.indexOfPanel(pan));
17648         }
17649         
17650         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17651             
17652             this.transition = true;
17653             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17654             var lr = dir == 'next' ? 'left' : 'right';
17655             pan.el.addClass(dir); // or prev
17656             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17657             cur.el.addClass(lr); // or right
17658             pan.el.addClass(lr);
17659             
17660             var _this = this;
17661             cur.el.on('transitionend', function() {
17662                 Roo.log("trans end?");
17663                 
17664                 pan.el.removeClass([lr,dir]);
17665                 pan.setActive(true);
17666                 
17667                 cur.el.removeClass([lr]);
17668                 cur.setActive(false);
17669                 
17670                 _this.transition = false;
17671                 
17672             }, this, { single:  true } );
17673             
17674             return true;
17675         }
17676         
17677         cur.setActive(false);
17678         pan.setActive(true);
17679         
17680         return true;
17681         
17682     },
17683     showPanelNext : function()
17684     {
17685         var i = this.indexOfPanel(this.getActivePanel());
17686         
17687         if (i >= this.tabs.length - 1 && !this.autoslide) {
17688             return;
17689         }
17690         
17691         if (i >= this.tabs.length - 1 && this.autoslide) {
17692             i = -1;
17693         }
17694         
17695         this.showPanel(this.tabs[i+1]);
17696     },
17697     
17698     showPanelPrev : function()
17699     {
17700         var i = this.indexOfPanel(this.getActivePanel());
17701         
17702         if (i  < 1 && !this.autoslide) {
17703             return;
17704         }
17705         
17706         if (i < 1 && this.autoslide) {
17707             i = this.tabs.length;
17708         }
17709         
17710         this.showPanel(this.tabs[i-1]);
17711     },
17712     
17713     
17714     addBullet: function()
17715     {
17716         if(!this.bullets || Roo.isTouch){
17717             return;
17718         }
17719         var ctr = this.el.select('.carousel-bullets',true).first();
17720         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17721         var bullet = ctr.createChild({
17722             cls : 'bullet bullet-' + i
17723         },ctr.dom.lastChild);
17724         
17725         
17726         var _this = this;
17727         
17728         bullet.on('click', (function(e, el, o, ii, t){
17729
17730             e.preventDefault();
17731
17732             this.showPanel(ii);
17733
17734             if(this.autoslide && this.slideFn){
17735                 clearInterval(this.slideFn);
17736                 this.slideFn = window.setInterval(function() {
17737                     _this.showPanelNext();
17738                 }, this.timer);
17739             }
17740
17741         }).createDelegate(this, [i, bullet], true));
17742                 
17743         
17744     },
17745      
17746     setActiveBullet : function(i)
17747     {
17748         if(Roo.isTouch){
17749             return;
17750         }
17751         
17752         Roo.each(this.el.select('.bullet', true).elements, function(el){
17753             el.removeClass('selected');
17754         });
17755
17756         var bullet = this.el.select('.bullet-' + i, true).first();
17757         
17758         if(!bullet){
17759             return;
17760         }
17761         
17762         bullet.addClass('selected');
17763     }
17764     
17765     
17766   
17767 });
17768
17769  
17770
17771  
17772  
17773 Roo.apply(Roo.bootstrap.TabGroup, {
17774     
17775     groups: {},
17776      /**
17777     * register a Navigation Group
17778     * @param {Roo.bootstrap.NavGroup} the navgroup to add
17779     */
17780     register : function(navgrp)
17781     {
17782         this.groups[navgrp.navId] = navgrp;
17783         
17784     },
17785     /**
17786     * fetch a Navigation Group based on the navigation ID
17787     * if one does not exist , it will get created.
17788     * @param {string} the navgroup to add
17789     * @returns {Roo.bootstrap.NavGroup} the navgroup 
17790     */
17791     get: function(navId) {
17792         if (typeof(this.groups[navId]) == 'undefined') {
17793             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17794         }
17795         return this.groups[navId] ;
17796     }
17797     
17798     
17799     
17800 });
17801
17802  /*
17803  * - LGPL
17804  *
17805  * TabPanel
17806  * 
17807  */
17808
17809 /**
17810  * @class Roo.bootstrap.TabPanel
17811  * @extends Roo.bootstrap.Component
17812  * Bootstrap TabPanel class
17813  * @cfg {Boolean} active panel active
17814  * @cfg {String} html panel content
17815  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17816  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17817  * @cfg {String} href click to link..
17818  * 
17819  * 
17820  * @constructor
17821  * Create a new TabPanel
17822  * @param {Object} config The config object
17823  */
17824
17825 Roo.bootstrap.TabPanel = function(config){
17826     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17827     this.addEvents({
17828         /**
17829              * @event changed
17830              * Fires when the active status changes
17831              * @param {Roo.bootstrap.TabPanel} this
17832              * @param {Boolean} state the new state
17833             
17834          */
17835         'changed': true,
17836         /**
17837              * @event beforedeactivate
17838              * Fires before a tab is de-activated - can be used to do validation on a form.
17839              * @param {Roo.bootstrap.TabPanel} this
17840              * @return {Boolean} false if there is an error
17841             
17842          */
17843         'beforedeactivate': true
17844      });
17845     
17846     this.tabId = this.tabId || Roo.id();
17847   
17848 };
17849
17850 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
17851     
17852     active: false,
17853     html: false,
17854     tabId: false,
17855     navId : false,
17856     href : '',
17857     
17858     getAutoCreate : function(){
17859         var cfg = {
17860             tag: 'div',
17861             // item is needed for carousel - not sure if it has any effect otherwise
17862             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17863             html: this.html || ''
17864         };
17865         
17866         if(this.active){
17867             cfg.cls += ' active';
17868         }
17869         
17870         if(this.tabId){
17871             cfg.tabId = this.tabId;
17872         }
17873         
17874         
17875         return cfg;
17876     },
17877     
17878     initEvents:  function()
17879     {
17880         var p = this.parent();
17881         
17882         this.navId = this.navId || p.navId;
17883         
17884         if (typeof(this.navId) != 'undefined') {
17885             // not really needed.. but just in case.. parent should be a NavGroup.
17886             var tg = Roo.bootstrap.TabGroup.get(this.navId);
17887             
17888             tg.register(this);
17889             
17890             var i = tg.tabs.length - 1;
17891             
17892             if(this.active && tg.bullets > 0 && i < tg.bullets){
17893                 tg.setActiveBullet(i);
17894             }
17895         }
17896         
17897         this.el.on('click', this.onClick, this);
17898         
17899         if(Roo.isTouch){
17900             this.el.on("touchstart", this.onTouchStart, this);
17901             this.el.on("touchmove", this.onTouchMove, this);
17902             this.el.on("touchend", this.onTouchEnd, this);
17903         }
17904         
17905     },
17906     
17907     onRender : function(ct, position)
17908     {
17909         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17910     },
17911     
17912     setActive : function(state)
17913     {
17914         Roo.log("panel - set active " + this.tabId + "=" + state);
17915         
17916         this.active = state;
17917         if (!state) {
17918             this.el.removeClass('active');
17919             
17920         } else  if (!this.el.hasClass('active')) {
17921             this.el.addClass('active');
17922         }
17923         
17924         this.fireEvent('changed', this, state);
17925     },
17926     
17927     onClick : function(e)
17928     {
17929         e.preventDefault();
17930         
17931         if(!this.href.length){
17932             return;
17933         }
17934         
17935         window.location.href = this.href;
17936     },
17937     
17938     startX : 0,
17939     startY : 0,
17940     endX : 0,
17941     endY : 0,
17942     swiping : false,
17943     
17944     onTouchStart : function(e)
17945     {
17946         this.swiping = false;
17947         
17948         this.startX = e.browserEvent.touches[0].clientX;
17949         this.startY = e.browserEvent.touches[0].clientY;
17950     },
17951     
17952     onTouchMove : function(e)
17953     {
17954         this.swiping = true;
17955         
17956         this.endX = e.browserEvent.touches[0].clientX;
17957         this.endY = e.browserEvent.touches[0].clientY;
17958     },
17959     
17960     onTouchEnd : function(e)
17961     {
17962         if(!this.swiping){
17963             this.onClick(e);
17964             return;
17965         }
17966         
17967         var tabGroup = this.parent();
17968         
17969         if(this.endX > this.startX){ // swiping right
17970             tabGroup.showPanelPrev();
17971             return;
17972         }
17973         
17974         if(this.startX > this.endX){ // swiping left
17975             tabGroup.showPanelNext();
17976             return;
17977         }
17978     }
17979     
17980     
17981 });
17982  
17983
17984  
17985
17986  /*
17987  * - LGPL
17988  *
17989  * DateField
17990  * 
17991  */
17992
17993 /**
17994  * @class Roo.bootstrap.DateField
17995  * @extends Roo.bootstrap.Input
17996  * Bootstrap DateField class
17997  * @cfg {Number} weekStart default 0
17998  * @cfg {String} viewMode default empty, (months|years)
17999  * @cfg {String} minViewMode default empty, (months|years)
18000  * @cfg {Number} startDate default -Infinity
18001  * @cfg {Number} endDate default Infinity
18002  * @cfg {Boolean} todayHighlight default false
18003  * @cfg {Boolean} todayBtn default false
18004  * @cfg {Boolean} calendarWeeks default false
18005  * @cfg {Object} daysOfWeekDisabled default empty
18006  * @cfg {Boolean} singleMode default false (true | false)
18007  * 
18008  * @cfg {Boolean} keyboardNavigation default true
18009  * @cfg {String} language default en
18010  * 
18011  * @constructor
18012  * Create a new DateField
18013  * @param {Object} config The config object
18014  */
18015
18016 Roo.bootstrap.DateField = function(config){
18017     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18018      this.addEvents({
18019             /**
18020              * @event show
18021              * Fires when this field show.
18022              * @param {Roo.bootstrap.DateField} this
18023              * @param {Mixed} date The date value
18024              */
18025             show : true,
18026             /**
18027              * @event show
18028              * Fires when this field hide.
18029              * @param {Roo.bootstrap.DateField} this
18030              * @param {Mixed} date The date value
18031              */
18032             hide : true,
18033             /**
18034              * @event select
18035              * Fires when select a date.
18036              * @param {Roo.bootstrap.DateField} this
18037              * @param {Mixed} date The date value
18038              */
18039             select : true,
18040             /**
18041              * @event beforeselect
18042              * Fires when before select a date.
18043              * @param {Roo.bootstrap.DateField} this
18044              * @param {Mixed} date The date value
18045              */
18046             beforeselect : true
18047         });
18048 };
18049
18050 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18051     
18052     /**
18053      * @cfg {String} format
18054      * The default date format string which can be overriden for localization support.  The format must be
18055      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18056      */
18057     format : "m/d/y",
18058     /**
18059      * @cfg {String} altFormats
18060      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18061      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18062      */
18063     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18064     
18065     weekStart : 0,
18066     
18067     viewMode : '',
18068     
18069     minViewMode : '',
18070     
18071     todayHighlight : false,
18072     
18073     todayBtn: false,
18074     
18075     language: 'en',
18076     
18077     keyboardNavigation: true,
18078     
18079     calendarWeeks: false,
18080     
18081     startDate: -Infinity,
18082     
18083     endDate: Infinity,
18084     
18085     daysOfWeekDisabled: [],
18086     
18087     _events: [],
18088     
18089     singleMode : false,
18090     
18091     UTCDate: function()
18092     {
18093         return new Date(Date.UTC.apply(Date, arguments));
18094     },
18095     
18096     UTCToday: function()
18097     {
18098         var today = new Date();
18099         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18100     },
18101     
18102     getDate: function() {
18103             var d = this.getUTCDate();
18104             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18105     },
18106     
18107     getUTCDate: function() {
18108             return this.date;
18109     },
18110     
18111     setDate: function(d) {
18112             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18113     },
18114     
18115     setUTCDate: function(d) {
18116             this.date = d;
18117             this.setValue(this.formatDate(this.date));
18118     },
18119         
18120     onRender: function(ct, position)
18121     {
18122         
18123         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18124         
18125         this.language = this.language || 'en';
18126         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18127         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18128         
18129         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18130         this.format = this.format || 'm/d/y';
18131         this.isInline = false;
18132         this.isInput = true;
18133         this.component = this.el.select('.add-on', true).first() || false;
18134         this.component = (this.component && this.component.length === 0) ? false : this.component;
18135         this.hasInput = this.component && this.inputEl().length;
18136         
18137         if (typeof(this.minViewMode === 'string')) {
18138             switch (this.minViewMode) {
18139                 case 'months':
18140                     this.minViewMode = 1;
18141                     break;
18142                 case 'years':
18143                     this.minViewMode = 2;
18144                     break;
18145                 default:
18146                     this.minViewMode = 0;
18147                     break;
18148             }
18149         }
18150         
18151         if (typeof(this.viewMode === 'string')) {
18152             switch (this.viewMode) {
18153                 case 'months':
18154                     this.viewMode = 1;
18155                     break;
18156                 case 'years':
18157                     this.viewMode = 2;
18158                     break;
18159                 default:
18160                     this.viewMode = 0;
18161                     break;
18162             }
18163         }
18164                 
18165         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18166         
18167 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18168         
18169         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18170         
18171         this.picker().on('mousedown', this.onMousedown, this);
18172         this.picker().on('click', this.onClick, this);
18173         
18174         this.picker().addClass('datepicker-dropdown');
18175         
18176         this.startViewMode = this.viewMode;
18177         
18178         if(this.singleMode){
18179             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18180                 v.setVisibilityMode(Roo.Element.DISPLAY);
18181                 v.hide();
18182             });
18183             
18184             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18185                 v.setStyle('width', '189px');
18186             });
18187         }
18188         
18189         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18190             if(!this.calendarWeeks){
18191                 v.remove();
18192                 return;
18193             }
18194             
18195             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18196             v.attr('colspan', function(i, val){
18197                 return parseInt(val) + 1;
18198             });
18199         });
18200                         
18201         
18202         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18203         
18204         this.setStartDate(this.startDate);
18205         this.setEndDate(this.endDate);
18206         
18207         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18208         
18209         this.fillDow();
18210         this.fillMonths();
18211         this.update();
18212         this.showMode();
18213         
18214         if(this.isInline) {
18215             this.show();
18216         }
18217     },
18218     
18219     picker : function()
18220     {
18221         return this.pickerEl;
18222 //        return this.el.select('.datepicker', true).first();
18223     },
18224     
18225     fillDow: function()
18226     {
18227         var dowCnt = this.weekStart;
18228         
18229         var dow = {
18230             tag: 'tr',
18231             cn: [
18232                 
18233             ]
18234         };
18235         
18236         if(this.calendarWeeks){
18237             dow.cn.push({
18238                 tag: 'th',
18239                 cls: 'cw',
18240                 html: '&nbsp;'
18241             })
18242         }
18243         
18244         while (dowCnt < this.weekStart + 7) {
18245             dow.cn.push({
18246                 tag: 'th',
18247                 cls: 'dow',
18248                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18249             });
18250         }
18251         
18252         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18253     },
18254     
18255     fillMonths: function()
18256     {    
18257         var i = 0;
18258         var months = this.picker().select('>.datepicker-months td', true).first();
18259         
18260         months.dom.innerHTML = '';
18261         
18262         while (i < 12) {
18263             var month = {
18264                 tag: 'span',
18265                 cls: 'month',
18266                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18267             };
18268             
18269             months.createChild(month);
18270         }
18271         
18272     },
18273     
18274     update: function()
18275     {
18276         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;
18277         
18278         if (this.date < this.startDate) {
18279             this.viewDate = new Date(this.startDate);
18280         } else if (this.date > this.endDate) {
18281             this.viewDate = new Date(this.endDate);
18282         } else {
18283             this.viewDate = new Date(this.date);
18284         }
18285         
18286         this.fill();
18287     },
18288     
18289     fill: function() 
18290     {
18291         var d = new Date(this.viewDate),
18292                 year = d.getUTCFullYear(),
18293                 month = d.getUTCMonth(),
18294                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18295                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18296                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18297                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18298                 currentDate = this.date && this.date.valueOf(),
18299                 today = this.UTCToday();
18300         
18301         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18302         
18303 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18304         
18305 //        this.picker.select('>tfoot th.today').
18306 //                                              .text(dates[this.language].today)
18307 //                                              .toggle(this.todayBtn !== false);
18308     
18309         this.updateNavArrows();
18310         this.fillMonths();
18311                                                 
18312         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18313         
18314         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18315          
18316         prevMonth.setUTCDate(day);
18317         
18318         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18319         
18320         var nextMonth = new Date(prevMonth);
18321         
18322         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18323         
18324         nextMonth = nextMonth.valueOf();
18325         
18326         var fillMonths = false;
18327         
18328         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18329         
18330         while(prevMonth.valueOf() < nextMonth) {
18331             var clsName = '';
18332             
18333             if (prevMonth.getUTCDay() === this.weekStart) {
18334                 if(fillMonths){
18335                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18336                 }
18337                     
18338                 fillMonths = {
18339                     tag: 'tr',
18340                     cn: []
18341                 };
18342                 
18343                 if(this.calendarWeeks){
18344                     // ISO 8601: First week contains first thursday.
18345                     // ISO also states week starts on Monday, but we can be more abstract here.
18346                     var
18347                     // Start of current week: based on weekstart/current date
18348                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18349                     // Thursday of this week
18350                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18351                     // First Thursday of year, year from thursday
18352                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18353                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18354                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18355                     
18356                     fillMonths.cn.push({
18357                         tag: 'td',
18358                         cls: 'cw',
18359                         html: calWeek
18360                     });
18361                 }
18362             }
18363             
18364             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18365                 clsName += ' old';
18366             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18367                 clsName += ' new';
18368             }
18369             if (this.todayHighlight &&
18370                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18371                 prevMonth.getUTCMonth() == today.getMonth() &&
18372                 prevMonth.getUTCDate() == today.getDate()) {
18373                 clsName += ' today';
18374             }
18375             
18376             if (currentDate && prevMonth.valueOf() === currentDate) {
18377                 clsName += ' active';
18378             }
18379             
18380             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18381                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18382                     clsName += ' disabled';
18383             }
18384             
18385             fillMonths.cn.push({
18386                 tag: 'td',
18387                 cls: 'day ' + clsName,
18388                 html: prevMonth.getDate()
18389             });
18390             
18391             prevMonth.setDate(prevMonth.getDate()+1);
18392         }
18393           
18394         var currentYear = this.date && this.date.getUTCFullYear();
18395         var currentMonth = this.date && this.date.getUTCMonth();
18396         
18397         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18398         
18399         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18400             v.removeClass('active');
18401             
18402             if(currentYear === year && k === currentMonth){
18403                 v.addClass('active');
18404             }
18405             
18406             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18407                 v.addClass('disabled');
18408             }
18409             
18410         });
18411         
18412         
18413         year = parseInt(year/10, 10) * 10;
18414         
18415         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18416         
18417         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18418         
18419         year -= 1;
18420         for (var i = -1; i < 11; i++) {
18421             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18422                 tag: 'span',
18423                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18424                 html: year
18425             });
18426             
18427             year += 1;
18428         }
18429     },
18430     
18431     showMode: function(dir) 
18432     {
18433         if (dir) {
18434             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18435         }
18436         
18437         Roo.each(this.picker().select('>div',true).elements, function(v){
18438             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18439             v.hide();
18440         });
18441         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18442     },
18443     
18444     place: function()
18445     {
18446         if(this.isInline) {
18447             return;
18448         }
18449         
18450         this.picker().removeClass(['bottom', 'top']);
18451         
18452         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18453             /*
18454              * place to the top of element!
18455              *
18456              */
18457             
18458             this.picker().addClass('top');
18459             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18460             
18461             return;
18462         }
18463         
18464         this.picker().addClass('bottom');
18465         
18466         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18467     },
18468     
18469     parseDate : function(value)
18470     {
18471         if(!value || value instanceof Date){
18472             return value;
18473         }
18474         var v = Date.parseDate(value, this.format);
18475         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18476             v = Date.parseDate(value, 'Y-m-d');
18477         }
18478         if(!v && this.altFormats){
18479             if(!this.altFormatsArray){
18480                 this.altFormatsArray = this.altFormats.split("|");
18481             }
18482             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18483                 v = Date.parseDate(value, this.altFormatsArray[i]);
18484             }
18485         }
18486         return v;
18487     },
18488     
18489     formatDate : function(date, fmt)
18490     {   
18491         return (!date || !(date instanceof Date)) ?
18492         date : date.dateFormat(fmt || this.format);
18493     },
18494     
18495     onFocus : function()
18496     {
18497         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18498         this.show();
18499     },
18500     
18501     onBlur : function()
18502     {
18503         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18504         
18505         var d = this.inputEl().getValue();
18506         
18507         this.setValue(d);
18508                 
18509         this.hide();
18510     },
18511     
18512     show : function()
18513     {
18514         this.picker().show();
18515         this.update();
18516         this.place();
18517         
18518         this.fireEvent('show', this, this.date);
18519     },
18520     
18521     hide : function()
18522     {
18523         if(this.isInline) {
18524             return;
18525         }
18526         this.picker().hide();
18527         this.viewMode = this.startViewMode;
18528         this.showMode();
18529         
18530         this.fireEvent('hide', this, this.date);
18531         
18532     },
18533     
18534     onMousedown: function(e)
18535     {
18536         e.stopPropagation();
18537         e.preventDefault();
18538     },
18539     
18540     keyup: function(e)
18541     {
18542         Roo.bootstrap.DateField.superclass.keyup.call(this);
18543         this.update();
18544     },
18545
18546     setValue: function(v)
18547     {
18548         if(this.fireEvent('beforeselect', this, v) !== false){
18549             var d = new Date(this.parseDate(v) ).clearTime();
18550         
18551             if(isNaN(d.getTime())){
18552                 this.date = this.viewDate = '';
18553                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18554                 return;
18555             }
18556
18557             v = this.formatDate(d);
18558
18559             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18560
18561             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18562
18563             this.update();
18564
18565             this.fireEvent('select', this, this.date);
18566         }
18567     },
18568     
18569     getValue: function()
18570     {
18571         return this.formatDate(this.date);
18572     },
18573     
18574     fireKey: function(e)
18575     {
18576         if (!this.picker().isVisible()){
18577             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18578                 this.show();
18579             }
18580             return;
18581         }
18582         
18583         var dateChanged = false,
18584         dir, day, month,
18585         newDate, newViewDate;
18586         
18587         switch(e.keyCode){
18588             case 27: // escape
18589                 this.hide();
18590                 e.preventDefault();
18591                 break;
18592             case 37: // left
18593             case 39: // right
18594                 if (!this.keyboardNavigation) {
18595                     break;
18596                 }
18597                 dir = e.keyCode == 37 ? -1 : 1;
18598                 
18599                 if (e.ctrlKey){
18600                     newDate = this.moveYear(this.date, dir);
18601                     newViewDate = this.moveYear(this.viewDate, dir);
18602                 } else if (e.shiftKey){
18603                     newDate = this.moveMonth(this.date, dir);
18604                     newViewDate = this.moveMonth(this.viewDate, dir);
18605                 } else {
18606                     newDate = new Date(this.date);
18607                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18608                     newViewDate = new Date(this.viewDate);
18609                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18610                 }
18611                 if (this.dateWithinRange(newDate)){
18612                     this.date = newDate;
18613                     this.viewDate = newViewDate;
18614                     this.setValue(this.formatDate(this.date));
18615 //                    this.update();
18616                     e.preventDefault();
18617                     dateChanged = true;
18618                 }
18619                 break;
18620             case 38: // up
18621             case 40: // down
18622                 if (!this.keyboardNavigation) {
18623                     break;
18624                 }
18625                 dir = e.keyCode == 38 ? -1 : 1;
18626                 if (e.ctrlKey){
18627                     newDate = this.moveYear(this.date, dir);
18628                     newViewDate = this.moveYear(this.viewDate, dir);
18629                 } else if (e.shiftKey){
18630                     newDate = this.moveMonth(this.date, dir);
18631                     newViewDate = this.moveMonth(this.viewDate, dir);
18632                 } else {
18633                     newDate = new Date(this.date);
18634                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18635                     newViewDate = new Date(this.viewDate);
18636                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18637                 }
18638                 if (this.dateWithinRange(newDate)){
18639                     this.date = newDate;
18640                     this.viewDate = newViewDate;
18641                     this.setValue(this.formatDate(this.date));
18642 //                    this.update();
18643                     e.preventDefault();
18644                     dateChanged = true;
18645                 }
18646                 break;
18647             case 13: // enter
18648                 this.setValue(this.formatDate(this.date));
18649                 this.hide();
18650                 e.preventDefault();
18651                 break;
18652             case 9: // tab
18653                 this.setValue(this.formatDate(this.date));
18654                 this.hide();
18655                 break;
18656             case 16: // shift
18657             case 17: // ctrl
18658             case 18: // alt
18659                 break;
18660             default :
18661                 this.hide();
18662                 
18663         }
18664     },
18665     
18666     
18667     onClick: function(e) 
18668     {
18669         e.stopPropagation();
18670         e.preventDefault();
18671         
18672         var target = e.getTarget();
18673         
18674         if(target.nodeName.toLowerCase() === 'i'){
18675             target = Roo.get(target).dom.parentNode;
18676         }
18677         
18678         var nodeName = target.nodeName;
18679         var className = target.className;
18680         var html = target.innerHTML;
18681         //Roo.log(nodeName);
18682         
18683         switch(nodeName.toLowerCase()) {
18684             case 'th':
18685                 switch(className) {
18686                     case 'switch':
18687                         this.showMode(1);
18688                         break;
18689                     case 'prev':
18690                     case 'next':
18691                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18692                         switch(this.viewMode){
18693                                 case 0:
18694                                         this.viewDate = this.moveMonth(this.viewDate, dir);
18695                                         break;
18696                                 case 1:
18697                                 case 2:
18698                                         this.viewDate = this.moveYear(this.viewDate, dir);
18699                                         break;
18700                         }
18701                         this.fill();
18702                         break;
18703                     case 'today':
18704                         var date = new Date();
18705                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18706 //                        this.fill()
18707                         this.setValue(this.formatDate(this.date));
18708                         
18709                         this.hide();
18710                         break;
18711                 }
18712                 break;
18713             case 'span':
18714                 if (className.indexOf('disabled') < 0) {
18715                     this.viewDate.setUTCDate(1);
18716                     if (className.indexOf('month') > -1) {
18717                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18718                     } else {
18719                         var year = parseInt(html, 10) || 0;
18720                         this.viewDate.setUTCFullYear(year);
18721                         
18722                     }
18723                     
18724                     if(this.singleMode){
18725                         this.setValue(this.formatDate(this.viewDate));
18726                         this.hide();
18727                         return;
18728                     }
18729                     
18730                     this.showMode(-1);
18731                     this.fill();
18732                 }
18733                 break;
18734                 
18735             case 'td':
18736                 //Roo.log(className);
18737                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18738                     var day = parseInt(html, 10) || 1;
18739                     var year = this.viewDate.getUTCFullYear(),
18740                         month = this.viewDate.getUTCMonth();
18741
18742                     if (className.indexOf('old') > -1) {
18743                         if(month === 0 ){
18744                             month = 11;
18745                             year -= 1;
18746                         }else{
18747                             month -= 1;
18748                         }
18749                     } else if (className.indexOf('new') > -1) {
18750                         if (month == 11) {
18751                             month = 0;
18752                             year += 1;
18753                         } else {
18754                             month += 1;
18755                         }
18756                     }
18757                     //Roo.log([year,month,day]);
18758                     this.date = this.UTCDate(year, month, day,0,0,0,0);
18759                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18760 //                    this.fill();
18761                     //Roo.log(this.formatDate(this.date));
18762                     this.setValue(this.formatDate(this.date));
18763                     this.hide();
18764                 }
18765                 break;
18766         }
18767     },
18768     
18769     setStartDate: function(startDate)
18770     {
18771         this.startDate = startDate || -Infinity;
18772         if (this.startDate !== -Infinity) {
18773             this.startDate = this.parseDate(this.startDate);
18774         }
18775         this.update();
18776         this.updateNavArrows();
18777     },
18778
18779     setEndDate: function(endDate)
18780     {
18781         this.endDate = endDate || Infinity;
18782         if (this.endDate !== Infinity) {
18783             this.endDate = this.parseDate(this.endDate);
18784         }
18785         this.update();
18786         this.updateNavArrows();
18787     },
18788     
18789     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18790     {
18791         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18792         if (typeof(this.daysOfWeekDisabled) !== 'object') {
18793             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18794         }
18795         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18796             return parseInt(d, 10);
18797         });
18798         this.update();
18799         this.updateNavArrows();
18800     },
18801     
18802     updateNavArrows: function() 
18803     {
18804         if(this.singleMode){
18805             return;
18806         }
18807         
18808         var d = new Date(this.viewDate),
18809         year = d.getUTCFullYear(),
18810         month = d.getUTCMonth();
18811         
18812         Roo.each(this.picker().select('.prev', true).elements, function(v){
18813             v.show();
18814             switch (this.viewMode) {
18815                 case 0:
18816
18817                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18818                         v.hide();
18819                     }
18820                     break;
18821                 case 1:
18822                 case 2:
18823                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18824                         v.hide();
18825                     }
18826                     break;
18827             }
18828         });
18829         
18830         Roo.each(this.picker().select('.next', true).elements, function(v){
18831             v.show();
18832             switch (this.viewMode) {
18833                 case 0:
18834
18835                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18836                         v.hide();
18837                     }
18838                     break;
18839                 case 1:
18840                 case 2:
18841                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18842                         v.hide();
18843                     }
18844                     break;
18845             }
18846         })
18847     },
18848     
18849     moveMonth: function(date, dir)
18850     {
18851         if (!dir) {
18852             return date;
18853         }
18854         var new_date = new Date(date.valueOf()),
18855         day = new_date.getUTCDate(),
18856         month = new_date.getUTCMonth(),
18857         mag = Math.abs(dir),
18858         new_month, test;
18859         dir = dir > 0 ? 1 : -1;
18860         if (mag == 1){
18861             test = dir == -1
18862             // If going back one month, make sure month is not current month
18863             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18864             ? function(){
18865                 return new_date.getUTCMonth() == month;
18866             }
18867             // If going forward one month, make sure month is as expected
18868             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18869             : function(){
18870                 return new_date.getUTCMonth() != new_month;
18871             };
18872             new_month = month + dir;
18873             new_date.setUTCMonth(new_month);
18874             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18875             if (new_month < 0 || new_month > 11) {
18876                 new_month = (new_month + 12) % 12;
18877             }
18878         } else {
18879             // For magnitudes >1, move one month at a time...
18880             for (var i=0; i<mag; i++) {
18881                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18882                 new_date = this.moveMonth(new_date, dir);
18883             }
18884             // ...then reset the day, keeping it in the new month
18885             new_month = new_date.getUTCMonth();
18886             new_date.setUTCDate(day);
18887             test = function(){
18888                 return new_month != new_date.getUTCMonth();
18889             };
18890         }
18891         // Common date-resetting loop -- if date is beyond end of month, make it
18892         // end of month
18893         while (test()){
18894             new_date.setUTCDate(--day);
18895             new_date.setUTCMonth(new_month);
18896         }
18897         return new_date;
18898     },
18899
18900     moveYear: function(date, dir)
18901     {
18902         return this.moveMonth(date, dir*12);
18903     },
18904
18905     dateWithinRange: function(date)
18906     {
18907         return date >= this.startDate && date <= this.endDate;
18908     },
18909
18910     
18911     remove: function() 
18912     {
18913         this.picker().remove();
18914     },
18915     
18916     validateValue : function(value)
18917     {
18918         if(value.length < 1)  {
18919             if(this.allowBlank){
18920                 return true;
18921             }
18922             return false;
18923         }
18924         
18925         if(value.length < this.minLength){
18926             return false;
18927         }
18928         if(value.length > this.maxLength){
18929             return false;
18930         }
18931         if(this.vtype){
18932             var vt = Roo.form.VTypes;
18933             if(!vt[this.vtype](value, this)){
18934                 return false;
18935             }
18936         }
18937         if(typeof this.validator == "function"){
18938             var msg = this.validator(value);
18939             if(msg !== true){
18940                 return false;
18941             }
18942         }
18943         
18944         if(this.regex && !this.regex.test(value)){
18945             return false;
18946         }
18947         
18948         if(typeof(this.parseDate(value)) == 'undefined'){
18949             return false;
18950         }
18951         
18952         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
18953             return false;
18954         }      
18955         
18956         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
18957             return false;
18958         } 
18959         
18960         
18961         return true;
18962     }
18963    
18964 });
18965
18966 Roo.apply(Roo.bootstrap.DateField,  {
18967     
18968     head : {
18969         tag: 'thead',
18970         cn: [
18971         {
18972             tag: 'tr',
18973             cn: [
18974             {
18975                 tag: 'th',
18976                 cls: 'prev',
18977                 html: '<i class="fa fa-arrow-left"/>'
18978             },
18979             {
18980                 tag: 'th',
18981                 cls: 'switch',
18982                 colspan: '5'
18983             },
18984             {
18985                 tag: 'th',
18986                 cls: 'next',
18987                 html: '<i class="fa fa-arrow-right"/>'
18988             }
18989
18990             ]
18991         }
18992         ]
18993     },
18994     
18995     content : {
18996         tag: 'tbody',
18997         cn: [
18998         {
18999             tag: 'tr',
19000             cn: [
19001             {
19002                 tag: 'td',
19003                 colspan: '7'
19004             }
19005             ]
19006         }
19007         ]
19008     },
19009     
19010     footer : {
19011         tag: 'tfoot',
19012         cn: [
19013         {
19014             tag: 'tr',
19015             cn: [
19016             {
19017                 tag: 'th',
19018                 colspan: '7',
19019                 cls: 'today'
19020             }
19021                     
19022             ]
19023         }
19024         ]
19025     },
19026     
19027     dates:{
19028         en: {
19029             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19030             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19031             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19032             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19033             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19034             today: "Today"
19035         }
19036     },
19037     
19038     modes: [
19039     {
19040         clsName: 'days',
19041         navFnc: 'Month',
19042         navStep: 1
19043     },
19044     {
19045         clsName: 'months',
19046         navFnc: 'FullYear',
19047         navStep: 1
19048     },
19049     {
19050         clsName: 'years',
19051         navFnc: 'FullYear',
19052         navStep: 10
19053     }]
19054 });
19055
19056 Roo.apply(Roo.bootstrap.DateField,  {
19057   
19058     template : {
19059         tag: 'div',
19060         cls: 'datepicker dropdown-menu roo-dynamic',
19061         cn: [
19062         {
19063             tag: 'div',
19064             cls: 'datepicker-days',
19065             cn: [
19066             {
19067                 tag: 'table',
19068                 cls: 'table-condensed',
19069                 cn:[
19070                 Roo.bootstrap.DateField.head,
19071                 {
19072                     tag: 'tbody'
19073                 },
19074                 Roo.bootstrap.DateField.footer
19075                 ]
19076             }
19077             ]
19078         },
19079         {
19080             tag: 'div',
19081             cls: 'datepicker-months',
19082             cn: [
19083             {
19084                 tag: 'table',
19085                 cls: 'table-condensed',
19086                 cn:[
19087                 Roo.bootstrap.DateField.head,
19088                 Roo.bootstrap.DateField.content,
19089                 Roo.bootstrap.DateField.footer
19090                 ]
19091             }
19092             ]
19093         },
19094         {
19095             tag: 'div',
19096             cls: 'datepicker-years',
19097             cn: [
19098             {
19099                 tag: 'table',
19100                 cls: 'table-condensed',
19101                 cn:[
19102                 Roo.bootstrap.DateField.head,
19103                 Roo.bootstrap.DateField.content,
19104                 Roo.bootstrap.DateField.footer
19105                 ]
19106             }
19107             ]
19108         }
19109         ]
19110     }
19111 });
19112
19113  
19114
19115  /*
19116  * - LGPL
19117  *
19118  * TimeField
19119  * 
19120  */
19121
19122 /**
19123  * @class Roo.bootstrap.TimeField
19124  * @extends Roo.bootstrap.Input
19125  * Bootstrap DateField class
19126  * 
19127  * 
19128  * @constructor
19129  * Create a new TimeField
19130  * @param {Object} config The config object
19131  */
19132
19133 Roo.bootstrap.TimeField = function(config){
19134     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19135     this.addEvents({
19136             /**
19137              * @event show
19138              * Fires when this field show.
19139              * @param {Roo.bootstrap.DateField} thisthis
19140              * @param {Mixed} date The date value
19141              */
19142             show : true,
19143             /**
19144              * @event show
19145              * Fires when this field hide.
19146              * @param {Roo.bootstrap.DateField} this
19147              * @param {Mixed} date The date value
19148              */
19149             hide : true,
19150             /**
19151              * @event select
19152              * Fires when select a date.
19153              * @param {Roo.bootstrap.DateField} this
19154              * @param {Mixed} date The date value
19155              */
19156             select : true
19157         });
19158 };
19159
19160 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19161     
19162     /**
19163      * @cfg {String} format
19164      * The default time format string which can be overriden for localization support.  The format must be
19165      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19166      */
19167     format : "H:i",
19168        
19169     onRender: function(ct, position)
19170     {
19171         
19172         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19173                 
19174         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19175         
19176         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19177         
19178         this.pop = this.picker().select('>.datepicker-time',true).first();
19179         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19180         
19181         this.picker().on('mousedown', this.onMousedown, this);
19182         this.picker().on('click', this.onClick, this);
19183         
19184         this.picker().addClass('datepicker-dropdown');
19185     
19186         this.fillTime();
19187         this.update();
19188             
19189         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19190         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19191         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19192         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19193         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19194         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19195
19196     },
19197     
19198     fireKey: function(e){
19199         if (!this.picker().isVisible()){
19200             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19201                 this.show();
19202             }
19203             return;
19204         }
19205
19206         e.preventDefault();
19207         
19208         switch(e.keyCode){
19209             case 27: // escape
19210                 this.hide();
19211                 break;
19212             case 37: // left
19213             case 39: // right
19214                 this.onTogglePeriod();
19215                 break;
19216             case 38: // up
19217                 this.onIncrementMinutes();
19218                 break;
19219             case 40: // down
19220                 this.onDecrementMinutes();
19221                 break;
19222             case 13: // enter
19223             case 9: // tab
19224                 this.setTime();
19225                 break;
19226         }
19227     },
19228     
19229     onClick: function(e) {
19230         e.stopPropagation();
19231         e.preventDefault();
19232     },
19233     
19234     picker : function()
19235     {
19236         return this.el.select('.datepicker', true).first();
19237     },
19238     
19239     fillTime: function()
19240     {    
19241         var time = this.pop.select('tbody', true).first();
19242         
19243         time.dom.innerHTML = '';
19244         
19245         time.createChild({
19246             tag: 'tr',
19247             cn: [
19248                 {
19249                     tag: 'td',
19250                     cn: [
19251                         {
19252                             tag: 'a',
19253                             href: '#',
19254                             cls: 'btn',
19255                             cn: [
19256                                 {
19257                                     tag: 'span',
19258                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19259                                 }
19260                             ]
19261                         } 
19262                     ]
19263                 },
19264                 {
19265                     tag: 'td',
19266                     cls: 'separator'
19267                 },
19268                 {
19269                     tag: 'td',
19270                     cn: [
19271                         {
19272                             tag: 'a',
19273                             href: '#',
19274                             cls: 'btn',
19275                             cn: [
19276                                 {
19277                                     tag: 'span',
19278                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19279                                 }
19280                             ]
19281                         }
19282                     ]
19283                 },
19284                 {
19285                     tag: 'td',
19286                     cls: 'separator'
19287                 }
19288             ]
19289         });
19290         
19291         time.createChild({
19292             tag: 'tr',
19293             cn: [
19294                 {
19295                     tag: 'td',
19296                     cn: [
19297                         {
19298                             tag: 'span',
19299                             cls: 'timepicker-hour',
19300                             html: '00'
19301                         }  
19302                     ]
19303                 },
19304                 {
19305                     tag: 'td',
19306                     cls: 'separator',
19307                     html: ':'
19308                 },
19309                 {
19310                     tag: 'td',
19311                     cn: [
19312                         {
19313                             tag: 'span',
19314                             cls: 'timepicker-minute',
19315                             html: '00'
19316                         }  
19317                     ]
19318                 },
19319                 {
19320                     tag: 'td',
19321                     cls: 'separator'
19322                 },
19323                 {
19324                     tag: 'td',
19325                     cn: [
19326                         {
19327                             tag: 'button',
19328                             type: 'button',
19329                             cls: 'btn btn-primary period',
19330                             html: 'AM'
19331                             
19332                         }
19333                     ]
19334                 }
19335             ]
19336         });
19337         
19338         time.createChild({
19339             tag: 'tr',
19340             cn: [
19341                 {
19342                     tag: 'td',
19343                     cn: [
19344                         {
19345                             tag: 'a',
19346                             href: '#',
19347                             cls: 'btn',
19348                             cn: [
19349                                 {
19350                                     tag: 'span',
19351                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19352                                 }
19353                             ]
19354                         }
19355                     ]
19356                 },
19357                 {
19358                     tag: 'td',
19359                     cls: 'separator'
19360                 },
19361                 {
19362                     tag: 'td',
19363                     cn: [
19364                         {
19365                             tag: 'a',
19366                             href: '#',
19367                             cls: 'btn',
19368                             cn: [
19369                                 {
19370                                     tag: 'span',
19371                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19372                                 }
19373                             ]
19374                         }
19375                     ]
19376                 },
19377                 {
19378                     tag: 'td',
19379                     cls: 'separator'
19380                 }
19381             ]
19382         });
19383         
19384     },
19385     
19386     update: function()
19387     {
19388         
19389         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19390         
19391         this.fill();
19392     },
19393     
19394     fill: function() 
19395     {
19396         var hours = this.time.getHours();
19397         var minutes = this.time.getMinutes();
19398         var period = 'AM';
19399         
19400         if(hours > 11){
19401             period = 'PM';
19402         }
19403         
19404         if(hours == 0){
19405             hours = 12;
19406         }
19407         
19408         
19409         if(hours > 12){
19410             hours = hours - 12;
19411         }
19412         
19413         if(hours < 10){
19414             hours = '0' + hours;
19415         }
19416         
19417         if(minutes < 10){
19418             minutes = '0' + minutes;
19419         }
19420         
19421         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19422         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19423         this.pop.select('button', true).first().dom.innerHTML = period;
19424         
19425     },
19426     
19427     place: function()
19428     {   
19429         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19430         
19431         var cls = ['bottom'];
19432         
19433         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19434             cls.pop();
19435             cls.push('top');
19436         }
19437         
19438         cls.push('right');
19439         
19440         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19441             cls.pop();
19442             cls.push('left');
19443         }
19444         
19445         this.picker().addClass(cls.join('-'));
19446         
19447         var _this = this;
19448         
19449         Roo.each(cls, function(c){
19450             if(c == 'bottom'){
19451                 _this.picker().setTop(_this.inputEl().getHeight());
19452                 return;
19453             }
19454             if(c == 'top'){
19455                 _this.picker().setTop(0 - _this.picker().getHeight());
19456                 return;
19457             }
19458             
19459             if(c == 'left'){
19460                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19461                 return;
19462             }
19463             if(c == 'right'){
19464                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19465                 return;
19466             }
19467         });
19468         
19469     },
19470   
19471     onFocus : function()
19472     {
19473         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19474         this.show();
19475     },
19476     
19477     onBlur : function()
19478     {
19479         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19480         this.hide();
19481     },
19482     
19483     show : function()
19484     {
19485         this.picker().show();
19486         this.pop.show();
19487         this.update();
19488         this.place();
19489         
19490         this.fireEvent('show', this, this.date);
19491     },
19492     
19493     hide : function()
19494     {
19495         this.picker().hide();
19496         this.pop.hide();
19497         
19498         this.fireEvent('hide', this, this.date);
19499     },
19500     
19501     setTime : function()
19502     {
19503         this.hide();
19504         this.setValue(this.time.format(this.format));
19505         
19506         this.fireEvent('select', this, this.date);
19507         
19508         
19509     },
19510     
19511     onMousedown: function(e){
19512         e.stopPropagation();
19513         e.preventDefault();
19514     },
19515     
19516     onIncrementHours: function()
19517     {
19518         Roo.log('onIncrementHours');
19519         this.time = this.time.add(Date.HOUR, 1);
19520         this.update();
19521         
19522     },
19523     
19524     onDecrementHours: function()
19525     {
19526         Roo.log('onDecrementHours');
19527         this.time = this.time.add(Date.HOUR, -1);
19528         this.update();
19529     },
19530     
19531     onIncrementMinutes: function()
19532     {
19533         Roo.log('onIncrementMinutes');
19534         this.time = this.time.add(Date.MINUTE, 1);
19535         this.update();
19536     },
19537     
19538     onDecrementMinutes: function()
19539     {
19540         Roo.log('onDecrementMinutes');
19541         this.time = this.time.add(Date.MINUTE, -1);
19542         this.update();
19543     },
19544     
19545     onTogglePeriod: function()
19546     {
19547         Roo.log('onTogglePeriod');
19548         this.time = this.time.add(Date.HOUR, 12);
19549         this.update();
19550     }
19551     
19552    
19553 });
19554
19555 Roo.apply(Roo.bootstrap.TimeField,  {
19556     
19557     content : {
19558         tag: 'tbody',
19559         cn: [
19560             {
19561                 tag: 'tr',
19562                 cn: [
19563                 {
19564                     tag: 'td',
19565                     colspan: '7'
19566                 }
19567                 ]
19568             }
19569         ]
19570     },
19571     
19572     footer : {
19573         tag: 'tfoot',
19574         cn: [
19575             {
19576                 tag: 'tr',
19577                 cn: [
19578                 {
19579                     tag: 'th',
19580                     colspan: '7',
19581                     cls: '',
19582                     cn: [
19583                         {
19584                             tag: 'button',
19585                             cls: 'btn btn-info ok',
19586                             html: 'OK'
19587                         }
19588                     ]
19589                 }
19590
19591                 ]
19592             }
19593         ]
19594     }
19595 });
19596
19597 Roo.apply(Roo.bootstrap.TimeField,  {
19598   
19599     template : {
19600         tag: 'div',
19601         cls: 'datepicker dropdown-menu',
19602         cn: [
19603             {
19604                 tag: 'div',
19605                 cls: 'datepicker-time',
19606                 cn: [
19607                 {
19608                     tag: 'table',
19609                     cls: 'table-condensed',
19610                     cn:[
19611                     Roo.bootstrap.TimeField.content,
19612                     Roo.bootstrap.TimeField.footer
19613                     ]
19614                 }
19615                 ]
19616             }
19617         ]
19618     }
19619 });
19620
19621  
19622
19623  /*
19624  * - LGPL
19625  *
19626  * MonthField
19627  * 
19628  */
19629
19630 /**
19631  * @class Roo.bootstrap.MonthField
19632  * @extends Roo.bootstrap.Input
19633  * Bootstrap MonthField class
19634  * 
19635  * @cfg {String} language default en
19636  * 
19637  * @constructor
19638  * Create a new MonthField
19639  * @param {Object} config The config object
19640  */
19641
19642 Roo.bootstrap.MonthField = function(config){
19643     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19644     
19645     this.addEvents({
19646         /**
19647          * @event show
19648          * Fires when this field show.
19649          * @param {Roo.bootstrap.MonthField} this
19650          * @param {Mixed} date The date value
19651          */
19652         show : true,
19653         /**
19654          * @event show
19655          * Fires when this field hide.
19656          * @param {Roo.bootstrap.MonthField} this
19657          * @param {Mixed} date The date value
19658          */
19659         hide : true,
19660         /**
19661          * @event select
19662          * Fires when select a date.
19663          * @param {Roo.bootstrap.MonthField} this
19664          * @param {String} oldvalue The old value
19665          * @param {String} newvalue The new value
19666          */
19667         select : true
19668     });
19669 };
19670
19671 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
19672     
19673     onRender: function(ct, position)
19674     {
19675         
19676         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19677         
19678         this.language = this.language || 'en';
19679         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19680         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19681         
19682         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19683         this.isInline = false;
19684         this.isInput = true;
19685         this.component = this.el.select('.add-on', true).first() || false;
19686         this.component = (this.component && this.component.length === 0) ? false : this.component;
19687         this.hasInput = this.component && this.inputEL().length;
19688         
19689         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19690         
19691         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19692         
19693         this.picker().on('mousedown', this.onMousedown, this);
19694         this.picker().on('click', this.onClick, this);
19695         
19696         this.picker().addClass('datepicker-dropdown');
19697         
19698         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19699             v.setStyle('width', '189px');
19700         });
19701         
19702         this.fillMonths();
19703         
19704         this.update();
19705         
19706         if(this.isInline) {
19707             this.show();
19708         }
19709         
19710     },
19711     
19712     setValue: function(v, suppressEvent)
19713     {   
19714         var o = this.getValue();
19715         
19716         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19717         
19718         this.update();
19719
19720         if(suppressEvent !== true){
19721             this.fireEvent('select', this, o, v);
19722         }
19723         
19724     },
19725     
19726     getValue: function()
19727     {
19728         return this.value;
19729     },
19730     
19731     onClick: function(e) 
19732     {
19733         e.stopPropagation();
19734         e.preventDefault();
19735         
19736         var target = e.getTarget();
19737         
19738         if(target.nodeName.toLowerCase() === 'i'){
19739             target = Roo.get(target).dom.parentNode;
19740         }
19741         
19742         var nodeName = target.nodeName;
19743         var className = target.className;
19744         var html = target.innerHTML;
19745         
19746         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19747             return;
19748         }
19749         
19750         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19751         
19752         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19753         
19754         this.hide();
19755                         
19756     },
19757     
19758     picker : function()
19759     {
19760         return this.pickerEl;
19761     },
19762     
19763     fillMonths: function()
19764     {    
19765         var i = 0;
19766         var months = this.picker().select('>.datepicker-months td', true).first();
19767         
19768         months.dom.innerHTML = '';
19769         
19770         while (i < 12) {
19771             var month = {
19772                 tag: 'span',
19773                 cls: 'month',
19774                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19775             };
19776             
19777             months.createChild(month);
19778         }
19779         
19780     },
19781     
19782     update: function()
19783     {
19784         var _this = this;
19785         
19786         if(typeof(this.vIndex) == 'undefined' && this.value.length){
19787             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19788         }
19789         
19790         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19791             e.removeClass('active');
19792             
19793             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19794                 e.addClass('active');
19795             }
19796         })
19797     },
19798     
19799     place: function()
19800     {
19801         if(this.isInline) {
19802             return;
19803         }
19804         
19805         this.picker().removeClass(['bottom', 'top']);
19806         
19807         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19808             /*
19809              * place to the top of element!
19810              *
19811              */
19812             
19813             this.picker().addClass('top');
19814             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19815             
19816             return;
19817         }
19818         
19819         this.picker().addClass('bottom');
19820         
19821         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19822     },
19823     
19824     onFocus : function()
19825     {
19826         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19827         this.show();
19828     },
19829     
19830     onBlur : function()
19831     {
19832         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19833         
19834         var d = this.inputEl().getValue();
19835         
19836         this.setValue(d);
19837                 
19838         this.hide();
19839     },
19840     
19841     show : function()
19842     {
19843         this.picker().show();
19844         this.picker().select('>.datepicker-months', true).first().show();
19845         this.update();
19846         this.place();
19847         
19848         this.fireEvent('show', this, this.date);
19849     },
19850     
19851     hide : function()
19852     {
19853         if(this.isInline) {
19854             return;
19855         }
19856         this.picker().hide();
19857         this.fireEvent('hide', this, this.date);
19858         
19859     },
19860     
19861     onMousedown: function(e)
19862     {
19863         e.stopPropagation();
19864         e.preventDefault();
19865     },
19866     
19867     keyup: function(e)
19868     {
19869         Roo.bootstrap.MonthField.superclass.keyup.call(this);
19870         this.update();
19871     },
19872
19873     fireKey: function(e)
19874     {
19875         if (!this.picker().isVisible()){
19876             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
19877                 this.show();
19878             }
19879             return;
19880         }
19881         
19882         var dir;
19883         
19884         switch(e.keyCode){
19885             case 27: // escape
19886                 this.hide();
19887                 e.preventDefault();
19888                 break;
19889             case 37: // left
19890             case 39: // right
19891                 dir = e.keyCode == 37 ? -1 : 1;
19892                 
19893                 this.vIndex = this.vIndex + dir;
19894                 
19895                 if(this.vIndex < 0){
19896                     this.vIndex = 0;
19897                 }
19898                 
19899                 if(this.vIndex > 11){
19900                     this.vIndex = 11;
19901                 }
19902                 
19903                 if(isNaN(this.vIndex)){
19904                     this.vIndex = 0;
19905                 }
19906                 
19907                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19908                 
19909                 break;
19910             case 38: // up
19911             case 40: // down
19912                 
19913                 dir = e.keyCode == 38 ? -1 : 1;
19914                 
19915                 this.vIndex = this.vIndex + dir * 4;
19916                 
19917                 if(this.vIndex < 0){
19918                     this.vIndex = 0;
19919                 }
19920                 
19921                 if(this.vIndex > 11){
19922                     this.vIndex = 11;
19923                 }
19924                 
19925                 if(isNaN(this.vIndex)){
19926                     this.vIndex = 0;
19927                 }
19928                 
19929                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19930                 break;
19931                 
19932             case 13: // enter
19933                 
19934                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19935                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19936                 }
19937                 
19938                 this.hide();
19939                 e.preventDefault();
19940                 break;
19941             case 9: // tab
19942                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19943                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19944                 }
19945                 this.hide();
19946                 break;
19947             case 16: // shift
19948             case 17: // ctrl
19949             case 18: // alt
19950                 break;
19951             default :
19952                 this.hide();
19953                 
19954         }
19955     },
19956     
19957     remove: function() 
19958     {
19959         this.picker().remove();
19960     }
19961    
19962 });
19963
19964 Roo.apply(Roo.bootstrap.MonthField,  {
19965     
19966     content : {
19967         tag: 'tbody',
19968         cn: [
19969         {
19970             tag: 'tr',
19971             cn: [
19972             {
19973                 tag: 'td',
19974                 colspan: '7'
19975             }
19976             ]
19977         }
19978         ]
19979     },
19980     
19981     dates:{
19982         en: {
19983             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19984             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
19985         }
19986     }
19987 });
19988
19989 Roo.apply(Roo.bootstrap.MonthField,  {
19990   
19991     template : {
19992         tag: 'div',
19993         cls: 'datepicker dropdown-menu roo-dynamic',
19994         cn: [
19995             {
19996                 tag: 'div',
19997                 cls: 'datepicker-months',
19998                 cn: [
19999                 {
20000                     tag: 'table',
20001                     cls: 'table-condensed',
20002                     cn:[
20003                         Roo.bootstrap.DateField.content
20004                     ]
20005                 }
20006                 ]
20007             }
20008         ]
20009     }
20010 });
20011
20012  
20013
20014  
20015  /*
20016  * - LGPL
20017  *
20018  * CheckBox
20019  * 
20020  */
20021
20022 /**
20023  * @class Roo.bootstrap.CheckBox
20024  * @extends Roo.bootstrap.Input
20025  * Bootstrap CheckBox class
20026  * 
20027  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20028  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20029  * @cfg {String} boxLabel The text that appears beside the checkbox
20030  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20031  * @cfg {Boolean} checked initnal the element
20032  * @cfg {Boolean} inline inline the element (default false)
20033  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20034  * 
20035  * @constructor
20036  * Create a new CheckBox
20037  * @param {Object} config The config object
20038  */
20039
20040 Roo.bootstrap.CheckBox = function(config){
20041     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20042    
20043     this.addEvents({
20044         /**
20045         * @event check
20046         * Fires when the element is checked or unchecked.
20047         * @param {Roo.bootstrap.CheckBox} this This input
20048         * @param {Boolean} checked The new checked value
20049         */
20050        check : true
20051     });
20052     
20053 };
20054
20055 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20056   
20057     inputType: 'checkbox',
20058     inputValue: 1,
20059     valueOff: 0,
20060     boxLabel: false,
20061     checked: false,
20062     weight : false,
20063     inline: false,
20064     
20065     getAutoCreate : function()
20066     {
20067         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20068         
20069         var id = Roo.id();
20070         
20071         var cfg = {};
20072         
20073         cfg.cls = 'form-group ' + this.inputType; //input-group
20074         
20075         if(this.inline){
20076             cfg.cls += ' ' + this.inputType + '-inline';
20077         }
20078         
20079         var input =  {
20080             tag: 'input',
20081             id : id,
20082             type : this.inputType,
20083             value : this.inputValue,
20084             cls : 'roo-' + this.inputType, //'form-box',
20085             placeholder : this.placeholder || ''
20086             
20087         };
20088         
20089         if(this.inputType != 'radio'){
20090             var hidden =  {
20091                 tag: 'input',
20092                 type : 'hidden',
20093                 cls : 'roo-hidden-value',
20094                 value : this.checked ? this.valueOff : this.inputValue
20095             };
20096         }
20097         
20098             
20099         if (this.weight) { // Validity check?
20100             cfg.cls += " " + this.inputType + "-" + this.weight;
20101         }
20102         
20103         if (this.disabled) {
20104             input.disabled=true;
20105         }
20106         
20107         if(this.checked){
20108             input.checked = this.checked;
20109             
20110         }
20111         
20112         
20113         if (this.name) {
20114             
20115             input.name = this.name;
20116             
20117             if(this.inputType != 'radio'){
20118                 hidden.name = this.name;
20119                 input.name = '_hidden_' + this.name;
20120             }
20121         }
20122         
20123         if (this.size) {
20124             input.cls += ' input-' + this.size;
20125         }
20126         
20127         var settings=this;
20128         
20129         ['xs','sm','md','lg'].map(function(size){
20130             if (settings[size]) {
20131                 cfg.cls += ' col-' + size + '-' + settings[size];
20132             }
20133         });
20134         
20135         var inputblock = input;
20136          
20137         if (this.before || this.after) {
20138             
20139             inputblock = {
20140                 cls : 'input-group',
20141                 cn :  [] 
20142             };
20143             
20144             if (this.before) {
20145                 inputblock.cn.push({
20146                     tag :'span',
20147                     cls : 'input-group-addon',
20148                     html : this.before
20149                 });
20150             }
20151             
20152             inputblock.cn.push(input);
20153             
20154             if(this.inputType != 'radio'){
20155                 inputblock.cn.push(hidden);
20156             }
20157             
20158             if (this.after) {
20159                 inputblock.cn.push({
20160                     tag :'span',
20161                     cls : 'input-group-addon',
20162                     html : this.after
20163                 });
20164             }
20165             
20166         }
20167         
20168         if (align ==='left' && this.fieldLabel.length) {
20169 //                Roo.log("left and has label");
20170             cfg.cn = [
20171                 {
20172                     tag: 'label',
20173                     'for' :  id,
20174                     cls : 'control-label',
20175                     html : this.fieldLabel
20176
20177                 },
20178                 {
20179                     cls : "", 
20180                     cn: [
20181                         inputblock
20182                     ]
20183                 }
20184             ];
20185             
20186             if(this.labelWidth > 12){
20187                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20188             }
20189             
20190             if(this.labelWidth < 13 && this.labelmd == 0){
20191                 this.labelmd = this.labelWidth;
20192             }
20193             
20194             if(this.labellg > 0){
20195                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20196                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20197             }
20198             
20199             if(this.labelmd > 0){
20200                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20201                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20202             }
20203             
20204             if(this.labelsm > 0){
20205                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20206                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20207             }
20208             
20209             if(this.labelxs > 0){
20210                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20211                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20212             }
20213             
20214         } else if ( this.fieldLabel.length) {
20215 //                Roo.log(" label");
20216                 cfg.cn = [
20217                    
20218                     {
20219                         tag: this.boxLabel ? 'span' : 'label',
20220                         'for': id,
20221                         cls: 'control-label box-input-label',
20222                         //cls : 'input-group-addon',
20223                         html : this.fieldLabel
20224                         
20225                     },
20226                     
20227                     inputblock
20228                     
20229                 ];
20230
20231         } else {
20232             
20233 //                Roo.log(" no label && no align");
20234                 cfg.cn = [  inputblock ] ;
20235                 
20236                 
20237         }
20238         
20239         if(this.boxLabel){
20240              var boxLabelCfg = {
20241                 tag: 'label',
20242                 //'for': id, // box label is handled by onclick - so no for...
20243                 cls: 'box-label',
20244                 html: this.boxLabel
20245             };
20246             
20247             if(this.tooltip){
20248                 boxLabelCfg.tooltip = this.tooltip;
20249             }
20250              
20251             cfg.cn.push(boxLabelCfg);
20252         }
20253         
20254         if(this.inputType != 'radio'){
20255             cfg.cn.push(hidden);
20256         }
20257         
20258         return cfg;
20259         
20260     },
20261     
20262     /**
20263      * return the real input element.
20264      */
20265     inputEl: function ()
20266     {
20267         return this.el.select('input.roo-' + this.inputType,true).first();
20268     },
20269     hiddenEl: function ()
20270     {
20271         return this.el.select('input.roo-hidden-value',true).first();
20272     },
20273     
20274     labelEl: function()
20275     {
20276         return this.el.select('label.control-label',true).first();
20277     },
20278     /* depricated... */
20279     
20280     label: function()
20281     {
20282         return this.labelEl();
20283     },
20284     
20285     boxLabelEl: function()
20286     {
20287         return this.el.select('label.box-label',true).first();
20288     },
20289     
20290     initEvents : function()
20291     {
20292 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20293         
20294         this.inputEl().on('click', this.onClick,  this);
20295         
20296         if (this.boxLabel) { 
20297             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20298         }
20299         
20300         this.startValue = this.getValue();
20301         
20302         if(this.groupId){
20303             Roo.bootstrap.CheckBox.register(this);
20304         }
20305     },
20306     
20307     onClick : function()
20308     {   
20309         this.setChecked(!this.checked);
20310     },
20311     
20312     setChecked : function(state,suppressEvent)
20313     {
20314         this.startValue = this.getValue();
20315
20316         if(this.inputType == 'radio'){
20317             
20318             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20319                 e.dom.checked = false;
20320             });
20321             
20322             this.inputEl().dom.checked = true;
20323             
20324             this.inputEl().dom.value = this.inputValue;
20325             
20326             if(suppressEvent !== true){
20327                 this.fireEvent('check', this, true);
20328             }
20329             
20330             this.validate();
20331             
20332             return;
20333         }
20334         
20335         this.checked = state;
20336         
20337         this.inputEl().dom.checked = state;
20338         
20339         
20340         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20341         
20342         if(suppressEvent !== true){
20343             this.fireEvent('check', this, state);
20344         }
20345         
20346         this.validate();
20347     },
20348     
20349     getValue : function()
20350     {
20351         if(this.inputType == 'radio'){
20352             return this.getGroupValue();
20353         }
20354         
20355         return this.hiddenEl().dom.value;
20356         
20357     },
20358     
20359     getGroupValue : function()
20360     {
20361         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20362             return '';
20363         }
20364         
20365         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20366     },
20367     
20368     setValue : function(v,suppressEvent)
20369     {
20370         if(this.inputType == 'radio'){
20371             this.setGroupValue(v, suppressEvent);
20372             return;
20373         }
20374         
20375         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20376         
20377         this.validate();
20378     },
20379     
20380     setGroupValue : function(v, suppressEvent)
20381     {
20382         this.startValue = this.getValue();
20383         
20384         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20385             e.dom.checked = false;
20386             
20387             if(e.dom.value == v){
20388                 e.dom.checked = true;
20389             }
20390         });
20391         
20392         if(suppressEvent !== true){
20393             this.fireEvent('check', this, true);
20394         }
20395
20396         this.validate();
20397         
20398         return;
20399     },
20400     
20401     validate : function()
20402     {
20403         if(
20404                 this.disabled || 
20405                 (this.inputType == 'radio' && this.validateRadio()) ||
20406                 (this.inputType == 'checkbox' && this.validateCheckbox())
20407         ){
20408             this.markValid();
20409             return true;
20410         }
20411         
20412         this.markInvalid();
20413         return false;
20414     },
20415     
20416     validateRadio : function()
20417     {
20418         if(this.allowBlank){
20419             return true;
20420         }
20421         
20422         var valid = false;
20423         
20424         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20425             if(!e.dom.checked){
20426                 return;
20427             }
20428             
20429             valid = true;
20430             
20431             return false;
20432         });
20433         
20434         return valid;
20435     },
20436     
20437     validateCheckbox : function()
20438     {
20439         if(!this.groupId){
20440             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20441             //return (this.getValue() == this.inputValue) ? true : false;
20442         }
20443         
20444         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20445         
20446         if(!group){
20447             return false;
20448         }
20449         
20450         var r = false;
20451         
20452         for(var i in group){
20453             if(r){
20454                 break;
20455             }
20456             
20457             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20458         }
20459         
20460         return r;
20461     },
20462     
20463     /**
20464      * Mark this field as valid
20465      */
20466     markValid : function()
20467     {
20468         var _this = this;
20469         
20470         this.fireEvent('valid', this);
20471         
20472         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20473         
20474         if(this.groupId){
20475             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20476         }
20477         
20478         if(label){
20479             label.markValid();
20480         }
20481
20482         if(this.inputType == 'radio'){
20483             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20484                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20485                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20486             });
20487             
20488             return;
20489         }
20490
20491         if(!this.groupId){
20492             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20493             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20494             return;
20495         }
20496         
20497         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20498         
20499         if(!group){
20500             return;
20501         }
20502         
20503         for(var i in group){
20504             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20505             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20506         }
20507     },
20508     
20509      /**
20510      * Mark this field as invalid
20511      * @param {String} msg The validation message
20512      */
20513     markInvalid : function(msg)
20514     {
20515         if(this.allowBlank){
20516             return;
20517         }
20518         
20519         var _this = this;
20520         
20521         this.fireEvent('invalid', this, msg);
20522         
20523         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20524         
20525         if(this.groupId){
20526             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20527         }
20528         
20529         if(label){
20530             label.markInvalid();
20531         }
20532             
20533         if(this.inputType == 'radio'){
20534             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20535                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20536                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20537             });
20538             
20539             return;
20540         }
20541         
20542         if(!this.groupId){
20543             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20544             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20545             return;
20546         }
20547         
20548         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20549         
20550         if(!group){
20551             return;
20552         }
20553         
20554         for(var i in group){
20555             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20556             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20557         }
20558         
20559     },
20560     
20561     clearInvalid : function()
20562     {
20563         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20564         
20565         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20566         
20567         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20568         
20569         if (label) {
20570             label.iconEl.removeClass(label.validClass);
20571             label.iconEl.removeClass(label.invalidClass);
20572         }
20573     },
20574     
20575     disable : function()
20576     {
20577         if(this.inputType != 'radio'){
20578             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20579             return;
20580         }
20581         
20582         var _this = this;
20583         
20584         if(this.rendered){
20585             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20586                 _this.getActionEl().addClass(this.disabledClass);
20587                 e.dom.disabled = true;
20588             });
20589         }
20590         
20591         this.disabled = true;
20592         this.fireEvent("disable", this);
20593         return this;
20594     },
20595
20596     enable : function()
20597     {
20598         if(this.inputType != 'radio'){
20599             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20600             return;
20601         }
20602         
20603         var _this = this;
20604         
20605         if(this.rendered){
20606             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20607                 _this.getActionEl().removeClass(this.disabledClass);
20608                 e.dom.disabled = false;
20609             });
20610         }
20611         
20612         this.disabled = false;
20613         this.fireEvent("enable", this);
20614         return this;
20615     }
20616
20617 });
20618
20619 Roo.apply(Roo.bootstrap.CheckBox, {
20620     
20621     groups: {},
20622     
20623      /**
20624     * register a CheckBox Group
20625     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20626     */
20627     register : function(checkbox)
20628     {
20629         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20630             this.groups[checkbox.groupId] = {};
20631         }
20632         
20633         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20634             return;
20635         }
20636         
20637         this.groups[checkbox.groupId][checkbox.name] = checkbox;
20638         
20639     },
20640     /**
20641     * fetch a CheckBox Group based on the group ID
20642     * @param {string} the group ID
20643     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20644     */
20645     get: function(groupId) {
20646         if (typeof(this.groups[groupId]) == 'undefined') {
20647             return false;
20648         }
20649         
20650         return this.groups[groupId] ;
20651     }
20652     
20653     
20654 });
20655 /*
20656  * - LGPL
20657  *
20658  * RadioItem
20659  * 
20660  */
20661
20662 /**
20663  * @class Roo.bootstrap.Radio
20664  * @extends Roo.bootstrap.Component
20665  * Bootstrap Radio class
20666  * @cfg {String} boxLabel - the label associated
20667  * @cfg {String} value - the value of radio
20668  * 
20669  * @constructor
20670  * Create a new Radio
20671  * @param {Object} config The config object
20672  */
20673 Roo.bootstrap.Radio = function(config){
20674     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20675     
20676 };
20677
20678 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
20679     
20680     boxLabel : '',
20681     
20682     value : '',
20683     
20684     getAutoCreate : function()
20685     {
20686         var cfg = {
20687             tag : 'div',
20688             cls : 'form-group radio',
20689             cn : [
20690                 {
20691                     tag : 'label',
20692                     cls : 'box-label',
20693                     html : this.boxLabel
20694                 }
20695             ]
20696         };
20697         
20698         return cfg;
20699     },
20700     
20701     initEvents : function() 
20702     {
20703         this.parent().register(this);
20704         
20705         this.el.on('click', this.onClick, this);
20706         
20707     },
20708     
20709     onClick : function()
20710     {
20711         this.setChecked(true);
20712     },
20713     
20714     setChecked : function(state, suppressEvent)
20715     {
20716         this.parent().setValue(this.value, suppressEvent);
20717         
20718     }
20719     
20720 });
20721  
20722
20723  /*
20724  * - LGPL
20725  *
20726  * Input
20727  * 
20728  */
20729
20730 /**
20731  * @class Roo.bootstrap.SecurePass
20732  * @extends Roo.bootstrap.Input
20733  * Bootstrap SecurePass class
20734  *
20735  * 
20736  * @constructor
20737  * Create a new SecurePass
20738  * @param {Object} config The config object
20739  */
20740  
20741 Roo.bootstrap.SecurePass = function (config) {
20742     // these go here, so the translation tool can replace them..
20743     this.errors = {
20744         PwdEmpty: "Please type a password, and then retype it to confirm.",
20745         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20746         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20747         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20748         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20749         FNInPwd: "Your password can't contain your first name. Please type a different password.",
20750         LNInPwd: "Your password can't contain your last name. Please type a different password.",
20751         TooWeak: "Your password is Too Weak."
20752     },
20753     this.meterLabel = "Password strength:";
20754     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
20755     this.meterClass = [
20756         "roo-password-meter-tooweak", 
20757         "roo-password-meter-weak", 
20758         "roo-password-meter-medium", 
20759         "roo-password-meter-strong", 
20760         "roo-password-meter-grey"
20761     ];
20762     
20763     this.errors = {};
20764     
20765     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
20766 }
20767
20768 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
20769     /**
20770      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
20771      * {
20772      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
20773      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20774      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20775      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20776      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20777      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
20778      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
20779      * })
20780      */
20781     // private
20782     
20783     meterWidth: 300,
20784     errorMsg :'',    
20785     errors: false,
20786     imageRoot: '/',
20787     /**
20788      * @cfg {String/Object} Label for the strength meter (defaults to
20789      * 'Password strength:')
20790      */
20791     // private
20792     meterLabel: '',
20793     /**
20794      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
20795      * ['Weak', 'Medium', 'Strong'])
20796      */
20797     // private    
20798     pwdStrengths: false,    
20799     // private
20800     strength: 0,
20801     // private
20802     _lastPwd: null,
20803     // private
20804     kCapitalLetter: 0,
20805     kSmallLetter: 1,
20806     kDigit: 2,
20807     kPunctuation: 3,
20808     
20809     insecure: false,
20810     // private
20811     initEvents: function ()
20812     {
20813         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
20814
20815         if (this.el.is('input[type=password]') && Roo.isSafari) {
20816             this.el.on('keydown', this.SafariOnKeyDown, this);
20817         }
20818
20819         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
20820     },
20821     // private
20822     onRender: function (ct, position)
20823     {
20824         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
20825         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
20826         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
20827
20828         this.trigger.createChild({
20829                    cn: [
20830                     {
20831                     //id: 'PwdMeter',
20832                     tag: 'div',
20833                     cls: 'roo-password-meter-grey col-xs-12',
20834                     style: {
20835                         //width: 0,
20836                         //width: this.meterWidth + 'px'                                                
20837                         }
20838                     },
20839                     {                            
20840                          cls: 'roo-password-meter-text'                          
20841                     }
20842                 ]            
20843         });
20844
20845          
20846         if (this.hideTrigger) {
20847             this.trigger.setDisplayed(false);
20848         }
20849         this.setSize(this.width || '', this.height || '');
20850     },
20851     // private
20852     onDestroy: function ()
20853     {
20854         if (this.trigger) {
20855             this.trigger.removeAllListeners();
20856             this.trigger.remove();
20857         }
20858         if (this.wrap) {
20859             this.wrap.remove();
20860         }
20861         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
20862     },
20863     // private
20864     checkStrength: function ()
20865     {
20866         var pwd = this.inputEl().getValue();
20867         if (pwd == this._lastPwd) {
20868             return;
20869         }
20870
20871         var strength;
20872         if (this.ClientSideStrongPassword(pwd)) {
20873             strength = 3;
20874         } else if (this.ClientSideMediumPassword(pwd)) {
20875             strength = 2;
20876         } else if (this.ClientSideWeakPassword(pwd)) {
20877             strength = 1;
20878         } else {
20879             strength = 0;
20880         }
20881         
20882         Roo.log('strength1: ' + strength);
20883         
20884         //var pm = this.trigger.child('div/div/div').dom;
20885         var pm = this.trigger.child('div/div');
20886         pm.removeClass(this.meterClass);
20887         pm.addClass(this.meterClass[strength]);
20888                 
20889         
20890         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
20891                 
20892         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
20893         
20894         this._lastPwd = pwd;
20895     },
20896     reset: function ()
20897     {
20898         Roo.bootstrap.SecurePass.superclass.reset.call(this);
20899         
20900         this._lastPwd = '';
20901         
20902         var pm = this.trigger.child('div/div');
20903         pm.removeClass(this.meterClass);
20904         pm.addClass('roo-password-meter-grey');        
20905         
20906         
20907         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
20908         
20909         pt.innerHTML = '';
20910         this.inputEl().dom.type='password';
20911     },
20912     // private
20913     validateValue: function (value)
20914     {
20915         
20916         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
20917             return false;
20918         }
20919         if (value.length == 0) {
20920             if (this.allowBlank) {
20921                 this.clearInvalid();
20922                 return true;
20923             }
20924
20925             this.markInvalid(this.errors.PwdEmpty);
20926             this.errorMsg = this.errors.PwdEmpty;
20927             return false;
20928         }
20929         
20930         if(this.insecure){
20931             return true;
20932         }
20933         
20934         if ('[\x21-\x7e]*'.match(value)) {
20935             this.markInvalid(this.errors.PwdBadChar);
20936             this.errorMsg = this.errors.PwdBadChar;
20937             return false;
20938         }
20939         if (value.length < 6) {
20940             this.markInvalid(this.errors.PwdShort);
20941             this.errorMsg = this.errors.PwdShort;
20942             return false;
20943         }
20944         if (value.length > 16) {
20945             this.markInvalid(this.errors.PwdLong);
20946             this.errorMsg = this.errors.PwdLong;
20947             return false;
20948         }
20949         var strength;
20950         if (this.ClientSideStrongPassword(value)) {
20951             strength = 3;
20952         } else if (this.ClientSideMediumPassword(value)) {
20953             strength = 2;
20954         } else if (this.ClientSideWeakPassword(value)) {
20955             strength = 1;
20956         } else {
20957             strength = 0;
20958         }
20959
20960         
20961         if (strength < 2) {
20962             //this.markInvalid(this.errors.TooWeak);
20963             this.errorMsg = this.errors.TooWeak;
20964             //return false;
20965         }
20966         
20967         
20968         console.log('strength2: ' + strength);
20969         
20970         //var pm = this.trigger.child('div/div/div').dom;
20971         
20972         var pm = this.trigger.child('div/div');
20973         pm.removeClass(this.meterClass);
20974         pm.addClass(this.meterClass[strength]);
20975                 
20976         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
20977                 
20978         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
20979         
20980         this.errorMsg = ''; 
20981         return true;
20982     },
20983     // private
20984     CharacterSetChecks: function (type)
20985     {
20986         this.type = type;
20987         this.fResult = false;
20988     },
20989     // private
20990     isctype: function (character, type)
20991     {
20992         switch (type) {  
20993             case this.kCapitalLetter:
20994                 if (character >= 'A' && character <= 'Z') {
20995                     return true;
20996                 }
20997                 break;
20998             
20999             case this.kSmallLetter:
21000                 if (character >= 'a' && character <= 'z') {
21001                     return true;
21002                 }
21003                 break;
21004             
21005             case this.kDigit:
21006                 if (character >= '0' && character <= '9') {
21007                     return true;
21008                 }
21009                 break;
21010             
21011             case this.kPunctuation:
21012                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21013                     return true;
21014                 }
21015                 break;
21016             
21017             default:
21018                 return false;
21019         }
21020
21021     },
21022     // private
21023     IsLongEnough: function (pwd, size)
21024     {
21025         return !(pwd == null || isNaN(size) || pwd.length < size);
21026     },
21027     // private
21028     SpansEnoughCharacterSets: function (word, nb)
21029     {
21030         if (!this.IsLongEnough(word, nb))
21031         {
21032             return false;
21033         }
21034
21035         var characterSetChecks = new Array(
21036             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21037             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21038         );
21039         
21040         for (var index = 0; index < word.length; ++index) {
21041             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21042                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21043                     characterSetChecks[nCharSet].fResult = true;
21044                     break;
21045                 }
21046             }
21047         }
21048
21049         var nCharSets = 0;
21050         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21051             if (characterSetChecks[nCharSet].fResult) {
21052                 ++nCharSets;
21053             }
21054         }
21055
21056         if (nCharSets < nb) {
21057             return false;
21058         }
21059         return true;
21060     },
21061     // private
21062     ClientSideStrongPassword: function (pwd)
21063     {
21064         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21065     },
21066     // private
21067     ClientSideMediumPassword: function (pwd)
21068     {
21069         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21070     },
21071     // private
21072     ClientSideWeakPassword: function (pwd)
21073     {
21074         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21075     }
21076           
21077 })//<script type="text/javascript">
21078
21079 /*
21080  * Based  Ext JS Library 1.1.1
21081  * Copyright(c) 2006-2007, Ext JS, LLC.
21082  * LGPL
21083  *
21084  */
21085  
21086 /**
21087  * @class Roo.HtmlEditorCore
21088  * @extends Roo.Component
21089  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21090  *
21091  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21092  */
21093
21094 Roo.HtmlEditorCore = function(config){
21095     
21096     
21097     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21098     
21099     
21100     this.addEvents({
21101         /**
21102          * @event initialize
21103          * Fires when the editor is fully initialized (including the iframe)
21104          * @param {Roo.HtmlEditorCore} this
21105          */
21106         initialize: true,
21107         /**
21108          * @event activate
21109          * Fires when the editor is first receives the focus. Any insertion must wait
21110          * until after this event.
21111          * @param {Roo.HtmlEditorCore} this
21112          */
21113         activate: true,
21114          /**
21115          * @event beforesync
21116          * Fires before the textarea is updated with content from the editor iframe. Return false
21117          * to cancel the sync.
21118          * @param {Roo.HtmlEditorCore} this
21119          * @param {String} html
21120          */
21121         beforesync: true,
21122          /**
21123          * @event beforepush
21124          * Fires before the iframe editor is updated with content from the textarea. Return false
21125          * to cancel the push.
21126          * @param {Roo.HtmlEditorCore} this
21127          * @param {String} html
21128          */
21129         beforepush: true,
21130          /**
21131          * @event sync
21132          * Fires when the textarea is updated with content from the editor iframe.
21133          * @param {Roo.HtmlEditorCore} this
21134          * @param {String} html
21135          */
21136         sync: true,
21137          /**
21138          * @event push
21139          * Fires when the iframe editor is updated with content from the textarea.
21140          * @param {Roo.HtmlEditorCore} this
21141          * @param {String} html
21142          */
21143         push: true,
21144         
21145         /**
21146          * @event editorevent
21147          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21148          * @param {Roo.HtmlEditorCore} this
21149          */
21150         editorevent: true
21151         
21152     });
21153     
21154     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21155     
21156     // defaults : white / black...
21157     this.applyBlacklists();
21158     
21159     
21160     
21161 };
21162
21163
21164 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21165
21166
21167      /**
21168      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21169      */
21170     
21171     owner : false,
21172     
21173      /**
21174      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21175      *                        Roo.resizable.
21176      */
21177     resizable : false,
21178      /**
21179      * @cfg {Number} height (in pixels)
21180      */   
21181     height: 300,
21182    /**
21183      * @cfg {Number} width (in pixels)
21184      */   
21185     width: 500,
21186     
21187     /**
21188      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21189      * 
21190      */
21191     stylesheets: false,
21192     
21193     // id of frame..
21194     frameId: false,
21195     
21196     // private properties
21197     validationEvent : false,
21198     deferHeight: true,
21199     initialized : false,
21200     activated : false,
21201     sourceEditMode : false,
21202     onFocus : Roo.emptyFn,
21203     iframePad:3,
21204     hideMode:'offsets',
21205     
21206     clearUp: true,
21207     
21208     // blacklist + whitelisted elements..
21209     black: false,
21210     white: false,
21211      
21212     
21213
21214     /**
21215      * Protected method that will not generally be called directly. It
21216      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21217      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21218      */
21219     getDocMarkup : function(){
21220         // body styles..
21221         var st = '';
21222         
21223         // inherit styels from page...?? 
21224         if (this.stylesheets === false) {
21225             
21226             Roo.get(document.head).select('style').each(function(node) {
21227                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21228             });
21229             
21230             Roo.get(document.head).select('link').each(function(node) { 
21231                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21232             });
21233             
21234         } else if (!this.stylesheets.length) {
21235                 // simple..
21236                 st = '<style type="text/css">' +
21237                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21238                    '</style>';
21239         } else { 
21240             
21241         }
21242         
21243         st +=  '<style type="text/css">' +
21244             'IMG { cursor: pointer } ' +
21245         '</style>';
21246
21247         
21248         return '<html><head>' + st  +
21249             //<style type="text/css">' +
21250             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21251             //'</style>' +
21252             ' </head><body class="roo-htmleditor-body"></body></html>';
21253     },
21254
21255     // private
21256     onRender : function(ct, position)
21257     {
21258         var _t = this;
21259         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21260         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21261         
21262         
21263         this.el.dom.style.border = '0 none';
21264         this.el.dom.setAttribute('tabIndex', -1);
21265         this.el.addClass('x-hidden hide');
21266         
21267         
21268         
21269         if(Roo.isIE){ // fix IE 1px bogus margin
21270             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21271         }
21272        
21273         
21274         this.frameId = Roo.id();
21275         
21276          
21277         
21278         var iframe = this.owner.wrap.createChild({
21279             tag: 'iframe',
21280             cls: 'form-control', // bootstrap..
21281             id: this.frameId,
21282             name: this.frameId,
21283             frameBorder : 'no',
21284             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21285         }, this.el
21286         );
21287         
21288         
21289         this.iframe = iframe.dom;
21290
21291          this.assignDocWin();
21292         
21293         this.doc.designMode = 'on';
21294        
21295         this.doc.open();
21296         this.doc.write(this.getDocMarkup());
21297         this.doc.close();
21298
21299         
21300         var task = { // must defer to wait for browser to be ready
21301             run : function(){
21302                 //console.log("run task?" + this.doc.readyState);
21303                 this.assignDocWin();
21304                 if(this.doc.body || this.doc.readyState == 'complete'){
21305                     try {
21306                         this.doc.designMode="on";
21307                     } catch (e) {
21308                         return;
21309                     }
21310                     Roo.TaskMgr.stop(task);
21311                     this.initEditor.defer(10, this);
21312                 }
21313             },
21314             interval : 10,
21315             duration: 10000,
21316             scope: this
21317         };
21318         Roo.TaskMgr.start(task);
21319
21320     },
21321
21322     // private
21323     onResize : function(w, h)
21324     {
21325          Roo.log('resize: ' +w + ',' + h );
21326         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21327         if(!this.iframe){
21328             return;
21329         }
21330         if(typeof w == 'number'){
21331             
21332             this.iframe.style.width = w + 'px';
21333         }
21334         if(typeof h == 'number'){
21335             
21336             this.iframe.style.height = h + 'px';
21337             if(this.doc){
21338                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21339             }
21340         }
21341         
21342     },
21343
21344     /**
21345      * Toggles the editor between standard and source edit mode.
21346      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21347      */
21348     toggleSourceEdit : function(sourceEditMode){
21349         
21350         this.sourceEditMode = sourceEditMode === true;
21351         
21352         if(this.sourceEditMode){
21353  
21354             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21355             
21356         }else{
21357             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21358             //this.iframe.className = '';
21359             this.deferFocus();
21360         }
21361         //this.setSize(this.owner.wrap.getSize());
21362         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21363     },
21364
21365     
21366   
21367
21368     /**
21369      * Protected method that will not generally be called directly. If you need/want
21370      * custom HTML cleanup, this is the method you should override.
21371      * @param {String} html The HTML to be cleaned
21372      * return {String} The cleaned HTML
21373      */
21374     cleanHtml : function(html){
21375         html = String(html);
21376         if(html.length > 5){
21377             if(Roo.isSafari){ // strip safari nonsense
21378                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21379             }
21380         }
21381         if(html == '&nbsp;'){
21382             html = '';
21383         }
21384         return html;
21385     },
21386
21387     /**
21388      * HTML Editor -> Textarea
21389      * Protected method that will not generally be called directly. Syncs the contents
21390      * of the editor iframe with the textarea.
21391      */
21392     syncValue : function(){
21393         if(this.initialized){
21394             var bd = (this.doc.body || this.doc.documentElement);
21395             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21396             var html = bd.innerHTML;
21397             if(Roo.isSafari){
21398                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21399                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21400                 if(m && m[1]){
21401                     html = '<div style="'+m[0]+'">' + html + '</div>';
21402                 }
21403             }
21404             html = this.cleanHtml(html);
21405             // fix up the special chars.. normaly like back quotes in word...
21406             // however we do not want to do this with chinese..
21407             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21408                 var cc = b.charCodeAt();
21409                 if (
21410                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21411                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21412                     (cc >= 0xf900 && cc < 0xfb00 )
21413                 ) {
21414                         return b;
21415                 }
21416                 return "&#"+cc+";" 
21417             });
21418             if(this.owner.fireEvent('beforesync', this, html) !== false){
21419                 this.el.dom.value = html;
21420                 this.owner.fireEvent('sync', this, html);
21421             }
21422         }
21423     },
21424
21425     /**
21426      * Protected method that will not generally be called directly. Pushes the value of the textarea
21427      * into the iframe editor.
21428      */
21429     pushValue : function(){
21430         if(this.initialized){
21431             var v = this.el.dom.value.trim();
21432             
21433 //            if(v.length < 1){
21434 //                v = '&#160;';
21435 //            }
21436             
21437             if(this.owner.fireEvent('beforepush', this, v) !== false){
21438                 var d = (this.doc.body || this.doc.documentElement);
21439                 d.innerHTML = v;
21440                 this.cleanUpPaste();
21441                 this.el.dom.value = d.innerHTML;
21442                 this.owner.fireEvent('push', this, v);
21443             }
21444         }
21445     },
21446
21447     // private
21448     deferFocus : function(){
21449         this.focus.defer(10, this);
21450     },
21451
21452     // doc'ed in Field
21453     focus : function(){
21454         if(this.win && !this.sourceEditMode){
21455             this.win.focus();
21456         }else{
21457             this.el.focus();
21458         }
21459     },
21460     
21461     assignDocWin: function()
21462     {
21463         var iframe = this.iframe;
21464         
21465          if(Roo.isIE){
21466             this.doc = iframe.contentWindow.document;
21467             this.win = iframe.contentWindow;
21468         } else {
21469 //            if (!Roo.get(this.frameId)) {
21470 //                return;
21471 //            }
21472 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21473 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21474             
21475             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21476                 return;
21477             }
21478             
21479             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21480             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21481         }
21482     },
21483     
21484     // private
21485     initEditor : function(){
21486         //console.log("INIT EDITOR");
21487         this.assignDocWin();
21488         
21489         
21490         
21491         this.doc.designMode="on";
21492         this.doc.open();
21493         this.doc.write(this.getDocMarkup());
21494         this.doc.close();
21495         
21496         var dbody = (this.doc.body || this.doc.documentElement);
21497         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21498         // this copies styles from the containing element into thsi one..
21499         // not sure why we need all of this..
21500         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21501         
21502         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21503         //ss['background-attachment'] = 'fixed'; // w3c
21504         dbody.bgProperties = 'fixed'; // ie
21505         //Roo.DomHelper.applyStyles(dbody, ss);
21506         Roo.EventManager.on(this.doc, {
21507             //'mousedown': this.onEditorEvent,
21508             'mouseup': this.onEditorEvent,
21509             'dblclick': this.onEditorEvent,
21510             'click': this.onEditorEvent,
21511             'keyup': this.onEditorEvent,
21512             buffer:100,
21513             scope: this
21514         });
21515         if(Roo.isGecko){
21516             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21517         }
21518         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21519             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21520         }
21521         this.initialized = true;
21522
21523         this.owner.fireEvent('initialize', this);
21524         this.pushValue();
21525     },
21526
21527     // private
21528     onDestroy : function(){
21529         
21530         
21531         
21532         if(this.rendered){
21533             
21534             //for (var i =0; i < this.toolbars.length;i++) {
21535             //    // fixme - ask toolbars for heights?
21536             //    this.toolbars[i].onDestroy();
21537            // }
21538             
21539             //this.wrap.dom.innerHTML = '';
21540             //this.wrap.remove();
21541         }
21542     },
21543
21544     // private
21545     onFirstFocus : function(){
21546         
21547         this.assignDocWin();
21548         
21549         
21550         this.activated = true;
21551          
21552     
21553         if(Roo.isGecko){ // prevent silly gecko errors
21554             this.win.focus();
21555             var s = this.win.getSelection();
21556             if(!s.focusNode || s.focusNode.nodeType != 3){
21557                 var r = s.getRangeAt(0);
21558                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21559                 r.collapse(true);
21560                 this.deferFocus();
21561             }
21562             try{
21563                 this.execCmd('useCSS', true);
21564                 this.execCmd('styleWithCSS', false);
21565             }catch(e){}
21566         }
21567         this.owner.fireEvent('activate', this);
21568     },
21569
21570     // private
21571     adjustFont: function(btn){
21572         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21573         //if(Roo.isSafari){ // safari
21574         //    adjust *= 2;
21575        // }
21576         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21577         if(Roo.isSafari){ // safari
21578             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21579             v =  (v < 10) ? 10 : v;
21580             v =  (v > 48) ? 48 : v;
21581             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21582             
21583         }
21584         
21585         
21586         v = Math.max(1, v+adjust);
21587         
21588         this.execCmd('FontSize', v  );
21589     },
21590
21591     onEditorEvent : function(e)
21592     {
21593         this.owner.fireEvent('editorevent', this, e);
21594       //  this.updateToolbar();
21595         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21596     },
21597
21598     insertTag : function(tg)
21599     {
21600         // could be a bit smarter... -> wrap the current selected tRoo..
21601         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21602             
21603             range = this.createRange(this.getSelection());
21604             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21605             wrappingNode.appendChild(range.extractContents());
21606             range.insertNode(wrappingNode);
21607
21608             return;
21609             
21610             
21611             
21612         }
21613         this.execCmd("formatblock",   tg);
21614         
21615     },
21616     
21617     insertText : function(txt)
21618     {
21619         
21620         
21621         var range = this.createRange();
21622         range.deleteContents();
21623                //alert(Sender.getAttribute('label'));
21624                
21625         range.insertNode(this.doc.createTextNode(txt));
21626     } ,
21627     
21628      
21629
21630     /**
21631      * Executes a Midas editor command on the editor document and performs necessary focus and
21632      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21633      * @param {String} cmd The Midas command
21634      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21635      */
21636     relayCmd : function(cmd, value){
21637         this.win.focus();
21638         this.execCmd(cmd, value);
21639         this.owner.fireEvent('editorevent', this);
21640         //this.updateToolbar();
21641         this.owner.deferFocus();
21642     },
21643
21644     /**
21645      * Executes a Midas editor command directly on the editor document.
21646      * For visual commands, you should use {@link #relayCmd} instead.
21647      * <b>This should only be called after the editor is initialized.</b>
21648      * @param {String} cmd The Midas command
21649      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21650      */
21651     execCmd : function(cmd, value){
21652         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21653         this.syncValue();
21654     },
21655  
21656  
21657    
21658     /**
21659      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21660      * to insert tRoo.
21661      * @param {String} text | dom node.. 
21662      */
21663     insertAtCursor : function(text)
21664     {
21665         
21666         if(!this.activated){
21667             return;
21668         }
21669         /*
21670         if(Roo.isIE){
21671             this.win.focus();
21672             var r = this.doc.selection.createRange();
21673             if(r){
21674                 r.collapse(true);
21675                 r.pasteHTML(text);
21676                 this.syncValue();
21677                 this.deferFocus();
21678             
21679             }
21680             return;
21681         }
21682         */
21683         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21684             this.win.focus();
21685             
21686             
21687             // from jquery ui (MIT licenced)
21688             var range, node;
21689             var win = this.win;
21690             
21691             if (win.getSelection && win.getSelection().getRangeAt) {
21692                 range = win.getSelection().getRangeAt(0);
21693                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21694                 range.insertNode(node);
21695             } else if (win.document.selection && win.document.selection.createRange) {
21696                 // no firefox support
21697                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21698                 win.document.selection.createRange().pasteHTML(txt);
21699             } else {
21700                 // no firefox support
21701                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21702                 this.execCmd('InsertHTML', txt);
21703             } 
21704             
21705             this.syncValue();
21706             
21707             this.deferFocus();
21708         }
21709     },
21710  // private
21711     mozKeyPress : function(e){
21712         if(e.ctrlKey){
21713             var c = e.getCharCode(), cmd;
21714           
21715             if(c > 0){
21716                 c = String.fromCharCode(c).toLowerCase();
21717                 switch(c){
21718                     case 'b':
21719                         cmd = 'bold';
21720                         break;
21721                     case 'i':
21722                         cmd = 'italic';
21723                         break;
21724                     
21725                     case 'u':
21726                         cmd = 'underline';
21727                         break;
21728                     
21729                     case 'v':
21730                         this.cleanUpPaste.defer(100, this);
21731                         return;
21732                         
21733                 }
21734                 if(cmd){
21735                     this.win.focus();
21736                     this.execCmd(cmd);
21737                     this.deferFocus();
21738                     e.preventDefault();
21739                 }
21740                 
21741             }
21742         }
21743     },
21744
21745     // private
21746     fixKeys : function(){ // load time branching for fastest keydown performance
21747         if(Roo.isIE){
21748             return function(e){
21749                 var k = e.getKey(), r;
21750                 if(k == e.TAB){
21751                     e.stopEvent();
21752                     r = this.doc.selection.createRange();
21753                     if(r){
21754                         r.collapse(true);
21755                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21756                         this.deferFocus();
21757                     }
21758                     return;
21759                 }
21760                 
21761                 if(k == e.ENTER){
21762                     r = this.doc.selection.createRange();
21763                     if(r){
21764                         var target = r.parentElement();
21765                         if(!target || target.tagName.toLowerCase() != 'li'){
21766                             e.stopEvent();
21767                             r.pasteHTML('<br />');
21768                             r.collapse(false);
21769                             r.select();
21770                         }
21771                     }
21772                 }
21773                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21774                     this.cleanUpPaste.defer(100, this);
21775                     return;
21776                 }
21777                 
21778                 
21779             };
21780         }else if(Roo.isOpera){
21781             return function(e){
21782                 var k = e.getKey();
21783                 if(k == e.TAB){
21784                     e.stopEvent();
21785                     this.win.focus();
21786                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21787                     this.deferFocus();
21788                 }
21789                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21790                     this.cleanUpPaste.defer(100, this);
21791                     return;
21792                 }
21793                 
21794             };
21795         }else if(Roo.isSafari){
21796             return function(e){
21797                 var k = e.getKey();
21798                 
21799                 if(k == e.TAB){
21800                     e.stopEvent();
21801                     this.execCmd('InsertText','\t');
21802                     this.deferFocus();
21803                     return;
21804                 }
21805                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21806                     this.cleanUpPaste.defer(100, this);
21807                     return;
21808                 }
21809                 
21810              };
21811         }
21812     }(),
21813     
21814     getAllAncestors: function()
21815     {
21816         var p = this.getSelectedNode();
21817         var a = [];
21818         if (!p) {
21819             a.push(p); // push blank onto stack..
21820             p = this.getParentElement();
21821         }
21822         
21823         
21824         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21825             a.push(p);
21826             p = p.parentNode;
21827         }
21828         a.push(this.doc.body);
21829         return a;
21830     },
21831     lastSel : false,
21832     lastSelNode : false,
21833     
21834     
21835     getSelection : function() 
21836     {
21837         this.assignDocWin();
21838         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21839     },
21840     
21841     getSelectedNode: function() 
21842     {
21843         // this may only work on Gecko!!!
21844         
21845         // should we cache this!!!!
21846         
21847         
21848         
21849          
21850         var range = this.createRange(this.getSelection()).cloneRange();
21851         
21852         if (Roo.isIE) {
21853             var parent = range.parentElement();
21854             while (true) {
21855                 var testRange = range.duplicate();
21856                 testRange.moveToElementText(parent);
21857                 if (testRange.inRange(range)) {
21858                     break;
21859                 }
21860                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21861                     break;
21862                 }
21863                 parent = parent.parentElement;
21864             }
21865             return parent;
21866         }
21867         
21868         // is ancestor a text element.
21869         var ac =  range.commonAncestorContainer;
21870         if (ac.nodeType == 3) {
21871             ac = ac.parentNode;
21872         }
21873         
21874         var ar = ac.childNodes;
21875          
21876         var nodes = [];
21877         var other_nodes = [];
21878         var has_other_nodes = false;
21879         for (var i=0;i<ar.length;i++) {
21880             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21881                 continue;
21882             }
21883             // fullly contained node.
21884             
21885             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21886                 nodes.push(ar[i]);
21887                 continue;
21888             }
21889             
21890             // probably selected..
21891             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21892                 other_nodes.push(ar[i]);
21893                 continue;
21894             }
21895             // outer..
21896             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21897                 continue;
21898             }
21899             
21900             
21901             has_other_nodes = true;
21902         }
21903         if (!nodes.length && other_nodes.length) {
21904             nodes= other_nodes;
21905         }
21906         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21907             return false;
21908         }
21909         
21910         return nodes[0];
21911     },
21912     createRange: function(sel)
21913     {
21914         // this has strange effects when using with 
21915         // top toolbar - not sure if it's a great idea.
21916         //this.editor.contentWindow.focus();
21917         if (typeof sel != "undefined") {
21918             try {
21919                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21920             } catch(e) {
21921                 return this.doc.createRange();
21922             }
21923         } else {
21924             return this.doc.createRange();
21925         }
21926     },
21927     getParentElement: function()
21928     {
21929         
21930         this.assignDocWin();
21931         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21932         
21933         var range = this.createRange(sel);
21934          
21935         try {
21936             var p = range.commonAncestorContainer;
21937             while (p.nodeType == 3) { // text node
21938                 p = p.parentNode;
21939             }
21940             return p;
21941         } catch (e) {
21942             return null;
21943         }
21944     
21945     },
21946     /***
21947      *
21948      * Range intersection.. the hard stuff...
21949      *  '-1' = before
21950      *  '0' = hits..
21951      *  '1' = after.
21952      *         [ -- selected range --- ]
21953      *   [fail]                        [fail]
21954      *
21955      *    basically..
21956      *      if end is before start or  hits it. fail.
21957      *      if start is after end or hits it fail.
21958      *
21959      *   if either hits (but other is outside. - then it's not 
21960      *   
21961      *    
21962      **/
21963     
21964     
21965     // @see http://www.thismuchiknow.co.uk/?p=64.
21966     rangeIntersectsNode : function(range, node)
21967     {
21968         var nodeRange = node.ownerDocument.createRange();
21969         try {
21970             nodeRange.selectNode(node);
21971         } catch (e) {
21972             nodeRange.selectNodeContents(node);
21973         }
21974     
21975         var rangeStartRange = range.cloneRange();
21976         rangeStartRange.collapse(true);
21977     
21978         var rangeEndRange = range.cloneRange();
21979         rangeEndRange.collapse(false);
21980     
21981         var nodeStartRange = nodeRange.cloneRange();
21982         nodeStartRange.collapse(true);
21983     
21984         var nodeEndRange = nodeRange.cloneRange();
21985         nodeEndRange.collapse(false);
21986     
21987         return rangeStartRange.compareBoundaryPoints(
21988                  Range.START_TO_START, nodeEndRange) == -1 &&
21989                rangeEndRange.compareBoundaryPoints(
21990                  Range.START_TO_START, nodeStartRange) == 1;
21991         
21992          
21993     },
21994     rangeCompareNode : function(range, node)
21995     {
21996         var nodeRange = node.ownerDocument.createRange();
21997         try {
21998             nodeRange.selectNode(node);
21999         } catch (e) {
22000             nodeRange.selectNodeContents(node);
22001         }
22002         
22003         
22004         range.collapse(true);
22005     
22006         nodeRange.collapse(true);
22007      
22008         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22009         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22010          
22011         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22012         
22013         var nodeIsBefore   =  ss == 1;
22014         var nodeIsAfter    = ee == -1;
22015         
22016         if (nodeIsBefore && nodeIsAfter) {
22017             return 0; // outer
22018         }
22019         if (!nodeIsBefore && nodeIsAfter) {
22020             return 1; //right trailed.
22021         }
22022         
22023         if (nodeIsBefore && !nodeIsAfter) {
22024             return 2;  // left trailed.
22025         }
22026         // fully contined.
22027         return 3;
22028     },
22029
22030     // private? - in a new class?
22031     cleanUpPaste :  function()
22032     {
22033         // cleans up the whole document..
22034         Roo.log('cleanuppaste');
22035         
22036         this.cleanUpChildren(this.doc.body);
22037         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22038         if (clean != this.doc.body.innerHTML) {
22039             this.doc.body.innerHTML = clean;
22040         }
22041         
22042     },
22043     
22044     cleanWordChars : function(input) {// change the chars to hex code
22045         var he = Roo.HtmlEditorCore;
22046         
22047         var output = input;
22048         Roo.each(he.swapCodes, function(sw) { 
22049             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22050             
22051             output = output.replace(swapper, sw[1]);
22052         });
22053         
22054         return output;
22055     },
22056     
22057     
22058     cleanUpChildren : function (n)
22059     {
22060         if (!n.childNodes.length) {
22061             return;
22062         }
22063         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22064            this.cleanUpChild(n.childNodes[i]);
22065         }
22066     },
22067     
22068     
22069         
22070     
22071     cleanUpChild : function (node)
22072     {
22073         var ed = this;
22074         //console.log(node);
22075         if (node.nodeName == "#text") {
22076             // clean up silly Windows -- stuff?
22077             return; 
22078         }
22079         if (node.nodeName == "#comment") {
22080             node.parentNode.removeChild(node);
22081             // clean up silly Windows -- stuff?
22082             return; 
22083         }
22084         var lcname = node.tagName.toLowerCase();
22085         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22086         // whitelist of tags..
22087         
22088         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22089             // remove node.
22090             node.parentNode.removeChild(node);
22091             return;
22092             
22093         }
22094         
22095         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22096         
22097         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22098         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22099         
22100         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22101         //    remove_keep_children = true;
22102         //}
22103         
22104         if (remove_keep_children) {
22105             this.cleanUpChildren(node);
22106             // inserts everything just before this node...
22107             while (node.childNodes.length) {
22108                 var cn = node.childNodes[0];
22109                 node.removeChild(cn);
22110                 node.parentNode.insertBefore(cn, node);
22111             }
22112             node.parentNode.removeChild(node);
22113             return;
22114         }
22115         
22116         if (!node.attributes || !node.attributes.length) {
22117             this.cleanUpChildren(node);
22118             return;
22119         }
22120         
22121         function cleanAttr(n,v)
22122         {
22123             
22124             if (v.match(/^\./) || v.match(/^\//)) {
22125                 return;
22126             }
22127             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
22128                 return;
22129             }
22130             if (v.match(/^#/)) {
22131                 return;
22132             }
22133 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22134             node.removeAttribute(n);
22135             
22136         }
22137         
22138         var cwhite = this.cwhite;
22139         var cblack = this.cblack;
22140             
22141         function cleanStyle(n,v)
22142         {
22143             if (v.match(/expression/)) { //XSS?? should we even bother..
22144                 node.removeAttribute(n);
22145                 return;
22146             }
22147             
22148             var parts = v.split(/;/);
22149             var clean = [];
22150             
22151             Roo.each(parts, function(p) {
22152                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22153                 if (!p.length) {
22154                     return true;
22155                 }
22156                 var l = p.split(':').shift().replace(/\s+/g,'');
22157                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22158                 
22159                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22160 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22161                     //node.removeAttribute(n);
22162                     return true;
22163                 }
22164                 //Roo.log()
22165                 // only allow 'c whitelisted system attributes'
22166                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22167 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22168                     //node.removeAttribute(n);
22169                     return true;
22170                 }
22171                 
22172                 
22173                  
22174                 
22175                 clean.push(p);
22176                 return true;
22177             });
22178             if (clean.length) { 
22179                 node.setAttribute(n, clean.join(';'));
22180             } else {
22181                 node.removeAttribute(n);
22182             }
22183             
22184         }
22185         
22186         
22187         for (var i = node.attributes.length-1; i > -1 ; i--) {
22188             var a = node.attributes[i];
22189             //console.log(a);
22190             
22191             if (a.name.toLowerCase().substr(0,2)=='on')  {
22192                 node.removeAttribute(a.name);
22193                 continue;
22194             }
22195             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22196                 node.removeAttribute(a.name);
22197                 continue;
22198             }
22199             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22200                 cleanAttr(a.name,a.value); // fixme..
22201                 continue;
22202             }
22203             if (a.name == 'style') {
22204                 cleanStyle(a.name,a.value);
22205                 continue;
22206             }
22207             /// clean up MS crap..
22208             // tecnically this should be a list of valid class'es..
22209             
22210             
22211             if (a.name == 'class') {
22212                 if (a.value.match(/^Mso/)) {
22213                     node.className = '';
22214                 }
22215                 
22216                 if (a.value.match(/^body$/)) {
22217                     node.className = '';
22218                 }
22219                 continue;
22220             }
22221             
22222             // style cleanup!?
22223             // class cleanup?
22224             
22225         }
22226         
22227         
22228         this.cleanUpChildren(node);
22229         
22230         
22231     },
22232     
22233     /**
22234      * Clean up MS wordisms...
22235      */
22236     cleanWord : function(node)
22237     {
22238         
22239         
22240         if (!node) {
22241             this.cleanWord(this.doc.body);
22242             return;
22243         }
22244         if (node.nodeName == "#text") {
22245             // clean up silly Windows -- stuff?
22246             return; 
22247         }
22248         if (node.nodeName == "#comment") {
22249             node.parentNode.removeChild(node);
22250             // clean up silly Windows -- stuff?
22251             return; 
22252         }
22253         
22254         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22255             node.parentNode.removeChild(node);
22256             return;
22257         }
22258         
22259         // remove - but keep children..
22260         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22261             while (node.childNodes.length) {
22262                 var cn = node.childNodes[0];
22263                 node.removeChild(cn);
22264                 node.parentNode.insertBefore(cn, node);
22265             }
22266             node.parentNode.removeChild(node);
22267             this.iterateChildren(node, this.cleanWord);
22268             return;
22269         }
22270         // clean styles
22271         if (node.className.length) {
22272             
22273             var cn = node.className.split(/\W+/);
22274             var cna = [];
22275             Roo.each(cn, function(cls) {
22276                 if (cls.match(/Mso[a-zA-Z]+/)) {
22277                     return;
22278                 }
22279                 cna.push(cls);
22280             });
22281             node.className = cna.length ? cna.join(' ') : '';
22282             if (!cna.length) {
22283                 node.removeAttribute("class");
22284             }
22285         }
22286         
22287         if (node.hasAttribute("lang")) {
22288             node.removeAttribute("lang");
22289         }
22290         
22291         if (node.hasAttribute("style")) {
22292             
22293             var styles = node.getAttribute("style").split(";");
22294             var nstyle = [];
22295             Roo.each(styles, function(s) {
22296                 if (!s.match(/:/)) {
22297                     return;
22298                 }
22299                 var kv = s.split(":");
22300                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22301                     return;
22302                 }
22303                 // what ever is left... we allow.
22304                 nstyle.push(s);
22305             });
22306             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22307             if (!nstyle.length) {
22308                 node.removeAttribute('style');
22309             }
22310         }
22311         this.iterateChildren(node, this.cleanWord);
22312         
22313         
22314         
22315     },
22316     /**
22317      * iterateChildren of a Node, calling fn each time, using this as the scole..
22318      * @param {DomNode} node node to iterate children of.
22319      * @param {Function} fn method of this class to call on each item.
22320      */
22321     iterateChildren : function(node, fn)
22322     {
22323         if (!node.childNodes.length) {
22324                 return;
22325         }
22326         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22327            fn.call(this, node.childNodes[i])
22328         }
22329     },
22330     
22331     
22332     /**
22333      * cleanTableWidths.
22334      *
22335      * Quite often pasting from word etc.. results in tables with column and widths.
22336      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22337      *
22338      */
22339     cleanTableWidths : function(node)
22340     {
22341          
22342          
22343         if (!node) {
22344             this.cleanTableWidths(this.doc.body);
22345             return;
22346         }
22347         
22348         // ignore list...
22349         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22350             return; 
22351         }
22352         Roo.log(node.tagName);
22353         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22354             this.iterateChildren(node, this.cleanTableWidths);
22355             return;
22356         }
22357         if (node.hasAttribute('width')) {
22358             node.removeAttribute('width');
22359         }
22360         
22361          
22362         if (node.hasAttribute("style")) {
22363             // pretty basic...
22364             
22365             var styles = node.getAttribute("style").split(";");
22366             var nstyle = [];
22367             Roo.each(styles, function(s) {
22368                 if (!s.match(/:/)) {
22369                     return;
22370                 }
22371                 var kv = s.split(":");
22372                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22373                     return;
22374                 }
22375                 // what ever is left... we allow.
22376                 nstyle.push(s);
22377             });
22378             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22379             if (!nstyle.length) {
22380                 node.removeAttribute('style');
22381             }
22382         }
22383         
22384         this.iterateChildren(node, this.cleanTableWidths);
22385         
22386         
22387     },
22388     
22389     
22390     
22391     
22392     domToHTML : function(currentElement, depth, nopadtext) {
22393         
22394         depth = depth || 0;
22395         nopadtext = nopadtext || false;
22396     
22397         if (!currentElement) {
22398             return this.domToHTML(this.doc.body);
22399         }
22400         
22401         //Roo.log(currentElement);
22402         var j;
22403         var allText = false;
22404         var nodeName = currentElement.nodeName;
22405         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22406         
22407         if  (nodeName == '#text') {
22408             
22409             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22410         }
22411         
22412         
22413         var ret = '';
22414         if (nodeName != 'BODY') {
22415              
22416             var i = 0;
22417             // Prints the node tagName, such as <A>, <IMG>, etc
22418             if (tagName) {
22419                 var attr = [];
22420                 for(i = 0; i < currentElement.attributes.length;i++) {
22421                     // quoting?
22422                     var aname = currentElement.attributes.item(i).name;
22423                     if (!currentElement.attributes.item(i).value.length) {
22424                         continue;
22425                     }
22426                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22427                 }
22428                 
22429                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22430             } 
22431             else {
22432                 
22433                 // eack
22434             }
22435         } else {
22436             tagName = false;
22437         }
22438         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22439             return ret;
22440         }
22441         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22442             nopadtext = true;
22443         }
22444         
22445         
22446         // Traverse the tree
22447         i = 0;
22448         var currentElementChild = currentElement.childNodes.item(i);
22449         var allText = true;
22450         var innerHTML  = '';
22451         lastnode = '';
22452         while (currentElementChild) {
22453             // Formatting code (indent the tree so it looks nice on the screen)
22454             var nopad = nopadtext;
22455             if (lastnode == 'SPAN') {
22456                 nopad  = true;
22457             }
22458             // text
22459             if  (currentElementChild.nodeName == '#text') {
22460                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22461                 toadd = nopadtext ? toadd : toadd.trim();
22462                 if (!nopad && toadd.length > 80) {
22463                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22464                 }
22465                 innerHTML  += toadd;
22466                 
22467                 i++;
22468                 currentElementChild = currentElement.childNodes.item(i);
22469                 lastNode = '';
22470                 continue;
22471             }
22472             allText = false;
22473             
22474             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22475                 
22476             // Recursively traverse the tree structure of the child node
22477             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22478             lastnode = currentElementChild.nodeName;
22479             i++;
22480             currentElementChild=currentElement.childNodes.item(i);
22481         }
22482         
22483         ret += innerHTML;
22484         
22485         if (!allText) {
22486                 // The remaining code is mostly for formatting the tree
22487             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22488         }
22489         
22490         
22491         if (tagName) {
22492             ret+= "</"+tagName+">";
22493         }
22494         return ret;
22495         
22496     },
22497         
22498     applyBlacklists : function()
22499     {
22500         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22501         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22502         
22503         this.white = [];
22504         this.black = [];
22505         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22506             if (b.indexOf(tag) > -1) {
22507                 return;
22508             }
22509             this.white.push(tag);
22510             
22511         }, this);
22512         
22513         Roo.each(w, function(tag) {
22514             if (b.indexOf(tag) > -1) {
22515                 return;
22516             }
22517             if (this.white.indexOf(tag) > -1) {
22518                 return;
22519             }
22520             this.white.push(tag);
22521             
22522         }, this);
22523         
22524         
22525         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22526             if (w.indexOf(tag) > -1) {
22527                 return;
22528             }
22529             this.black.push(tag);
22530             
22531         }, this);
22532         
22533         Roo.each(b, function(tag) {
22534             if (w.indexOf(tag) > -1) {
22535                 return;
22536             }
22537             if (this.black.indexOf(tag) > -1) {
22538                 return;
22539             }
22540             this.black.push(tag);
22541             
22542         }, this);
22543         
22544         
22545         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22546         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22547         
22548         this.cwhite = [];
22549         this.cblack = [];
22550         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22551             if (b.indexOf(tag) > -1) {
22552                 return;
22553             }
22554             this.cwhite.push(tag);
22555             
22556         }, this);
22557         
22558         Roo.each(w, function(tag) {
22559             if (b.indexOf(tag) > -1) {
22560                 return;
22561             }
22562             if (this.cwhite.indexOf(tag) > -1) {
22563                 return;
22564             }
22565             this.cwhite.push(tag);
22566             
22567         }, this);
22568         
22569         
22570         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22571             if (w.indexOf(tag) > -1) {
22572                 return;
22573             }
22574             this.cblack.push(tag);
22575             
22576         }, this);
22577         
22578         Roo.each(b, function(tag) {
22579             if (w.indexOf(tag) > -1) {
22580                 return;
22581             }
22582             if (this.cblack.indexOf(tag) > -1) {
22583                 return;
22584             }
22585             this.cblack.push(tag);
22586             
22587         }, this);
22588     },
22589     
22590     setStylesheets : function(stylesheets)
22591     {
22592         if(typeof(stylesheets) == 'string'){
22593             Roo.get(this.iframe.contentDocument.head).createChild({
22594                 tag : 'link',
22595                 rel : 'stylesheet',
22596                 type : 'text/css',
22597                 href : stylesheets
22598             });
22599             
22600             return;
22601         }
22602         var _this = this;
22603      
22604         Roo.each(stylesheets, function(s) {
22605             if(!s.length){
22606                 return;
22607             }
22608             
22609             Roo.get(_this.iframe.contentDocument.head).createChild({
22610                 tag : 'link',
22611                 rel : 'stylesheet',
22612                 type : 'text/css',
22613                 href : s
22614             });
22615         });
22616
22617         
22618     },
22619     
22620     removeStylesheets : function()
22621     {
22622         var _this = this;
22623         
22624         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22625             s.remove();
22626         });
22627     }
22628     
22629     // hide stuff that is not compatible
22630     /**
22631      * @event blur
22632      * @hide
22633      */
22634     /**
22635      * @event change
22636      * @hide
22637      */
22638     /**
22639      * @event focus
22640      * @hide
22641      */
22642     /**
22643      * @event specialkey
22644      * @hide
22645      */
22646     /**
22647      * @cfg {String} fieldClass @hide
22648      */
22649     /**
22650      * @cfg {String} focusClass @hide
22651      */
22652     /**
22653      * @cfg {String} autoCreate @hide
22654      */
22655     /**
22656      * @cfg {String} inputType @hide
22657      */
22658     /**
22659      * @cfg {String} invalidClass @hide
22660      */
22661     /**
22662      * @cfg {String} invalidText @hide
22663      */
22664     /**
22665      * @cfg {String} msgFx @hide
22666      */
22667     /**
22668      * @cfg {String} validateOnBlur @hide
22669      */
22670 });
22671
22672 Roo.HtmlEditorCore.white = [
22673         'area', 'br', 'img', 'input', 'hr', 'wbr',
22674         
22675        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22676        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22677        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22678        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22679        'table',   'ul',         'xmp', 
22680        
22681        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22682       'thead',   'tr', 
22683      
22684       'dir', 'menu', 'ol', 'ul', 'dl',
22685        
22686       'embed',  'object'
22687 ];
22688
22689
22690 Roo.HtmlEditorCore.black = [
22691     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22692         'applet', // 
22693         'base',   'basefont', 'bgsound', 'blink',  'body', 
22694         'frame',  'frameset', 'head',    'html',   'ilayer', 
22695         'iframe', 'layer',  'link',     'meta',    'object',   
22696         'script', 'style' ,'title',  'xml' // clean later..
22697 ];
22698 Roo.HtmlEditorCore.clean = [
22699     'script', 'style', 'title', 'xml'
22700 ];
22701 Roo.HtmlEditorCore.remove = [
22702     'font'
22703 ];
22704 // attributes..
22705
22706 Roo.HtmlEditorCore.ablack = [
22707     'on'
22708 ];
22709     
22710 Roo.HtmlEditorCore.aclean = [ 
22711     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22712 ];
22713
22714 // protocols..
22715 Roo.HtmlEditorCore.pwhite= [
22716         'http',  'https',  'mailto'
22717 ];
22718
22719 // white listed style attributes.
22720 Roo.HtmlEditorCore.cwhite= [
22721       //  'text-align', /// default is to allow most things..
22722       
22723          
22724 //        'font-size'//??
22725 ];
22726
22727 // black listed style attributes.
22728 Roo.HtmlEditorCore.cblack= [
22729       //  'font-size' -- this can be set by the project 
22730 ];
22731
22732
22733 Roo.HtmlEditorCore.swapCodes   =[ 
22734     [    8211, "--" ], 
22735     [    8212, "--" ], 
22736     [    8216,  "'" ],  
22737     [    8217, "'" ],  
22738     [    8220, '"' ],  
22739     [    8221, '"' ],  
22740     [    8226, "*" ],  
22741     [    8230, "..." ]
22742 ]; 
22743
22744     /*
22745  * - LGPL
22746  *
22747  * HtmlEditor
22748  * 
22749  */
22750
22751 /**
22752  * @class Roo.bootstrap.HtmlEditor
22753  * @extends Roo.bootstrap.TextArea
22754  * Bootstrap HtmlEditor class
22755
22756  * @constructor
22757  * Create a new HtmlEditor
22758  * @param {Object} config The config object
22759  */
22760
22761 Roo.bootstrap.HtmlEditor = function(config){
22762     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
22763     if (!this.toolbars) {
22764         this.toolbars = [];
22765     }
22766     
22767     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22768     this.addEvents({
22769             /**
22770              * @event initialize
22771              * Fires when the editor is fully initialized (including the iframe)
22772              * @param {HtmlEditor} this
22773              */
22774             initialize: true,
22775             /**
22776              * @event activate
22777              * Fires when the editor is first receives the focus. Any insertion must wait
22778              * until after this event.
22779              * @param {HtmlEditor} this
22780              */
22781             activate: true,
22782              /**
22783              * @event beforesync
22784              * Fires before the textarea is updated with content from the editor iframe. Return false
22785              * to cancel the sync.
22786              * @param {HtmlEditor} this
22787              * @param {String} html
22788              */
22789             beforesync: true,
22790              /**
22791              * @event beforepush
22792              * Fires before the iframe editor is updated with content from the textarea. Return false
22793              * to cancel the push.
22794              * @param {HtmlEditor} this
22795              * @param {String} html
22796              */
22797             beforepush: true,
22798              /**
22799              * @event sync
22800              * Fires when the textarea is updated with content from the editor iframe.
22801              * @param {HtmlEditor} this
22802              * @param {String} html
22803              */
22804             sync: true,
22805              /**
22806              * @event push
22807              * Fires when the iframe editor is updated with content from the textarea.
22808              * @param {HtmlEditor} this
22809              * @param {String} html
22810              */
22811             push: true,
22812              /**
22813              * @event editmodechange
22814              * Fires when the editor switches edit modes
22815              * @param {HtmlEditor} this
22816              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22817              */
22818             editmodechange: true,
22819             /**
22820              * @event editorevent
22821              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22822              * @param {HtmlEditor} this
22823              */
22824             editorevent: true,
22825             /**
22826              * @event firstfocus
22827              * Fires when on first focus - needed by toolbars..
22828              * @param {HtmlEditor} this
22829              */
22830             firstfocus: true,
22831             /**
22832              * @event autosave
22833              * Auto save the htmlEditor value as a file into Events
22834              * @param {HtmlEditor} this
22835              */
22836             autosave: true,
22837             /**
22838              * @event savedpreview
22839              * preview the saved version of htmlEditor
22840              * @param {HtmlEditor} this
22841              */
22842             savedpreview: true
22843         });
22844 };
22845
22846
22847 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
22848     
22849     
22850       /**
22851      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22852      */
22853     toolbars : false,
22854     
22855      /**
22856     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
22857     */
22858     btns : [],
22859    
22860      /**
22861      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22862      *                        Roo.resizable.
22863      */
22864     resizable : false,
22865      /**
22866      * @cfg {Number} height (in pixels)
22867      */   
22868     height: 300,
22869    /**
22870      * @cfg {Number} width (in pixels)
22871      */   
22872     width: false,
22873     
22874     /**
22875      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22876      * 
22877      */
22878     stylesheets: false,
22879     
22880     // id of frame..
22881     frameId: false,
22882     
22883     // private properties
22884     validationEvent : false,
22885     deferHeight: true,
22886     initialized : false,
22887     activated : false,
22888     
22889     onFocus : Roo.emptyFn,
22890     iframePad:3,
22891     hideMode:'offsets',
22892     
22893     tbContainer : false,
22894     
22895     toolbarContainer :function() {
22896         return this.wrap.select('.x-html-editor-tb',true).first();
22897     },
22898
22899     /**
22900      * Protected method that will not generally be called directly. It
22901      * is called when the editor creates its toolbar. Override this method if you need to
22902      * add custom toolbar buttons.
22903      * @param {HtmlEditor} editor
22904      */
22905     createToolbar : function(){
22906         Roo.log('renewing');
22907         Roo.log("create toolbars");
22908         
22909         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
22910         this.toolbars[0].render(this.toolbarContainer());
22911         
22912         return;
22913         
22914 //        if (!editor.toolbars || !editor.toolbars.length) {
22915 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
22916 //        }
22917 //        
22918 //        for (var i =0 ; i < editor.toolbars.length;i++) {
22919 //            editor.toolbars[i] = Roo.factory(
22920 //                    typeof(editor.toolbars[i]) == 'string' ?
22921 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
22922 //                Roo.bootstrap.HtmlEditor);
22923 //            editor.toolbars[i].init(editor);
22924 //        }
22925     },
22926
22927      
22928     // private
22929     onRender : function(ct, position)
22930     {
22931        // Roo.log("Call onRender: " + this.xtype);
22932         var _t = this;
22933         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
22934       
22935         this.wrap = this.inputEl().wrap({
22936             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22937         });
22938         
22939         this.editorcore.onRender(ct, position);
22940          
22941         if (this.resizable) {
22942             this.resizeEl = new Roo.Resizable(this.wrap, {
22943                 pinned : true,
22944                 wrap: true,
22945                 dynamic : true,
22946                 minHeight : this.height,
22947                 height: this.height,
22948                 handles : this.resizable,
22949                 width: this.width,
22950                 listeners : {
22951                     resize : function(r, w, h) {
22952                         _t.onResize(w,h); // -something
22953                     }
22954                 }
22955             });
22956             
22957         }
22958         this.createToolbar(this);
22959        
22960         
22961         if(!this.width && this.resizable){
22962             this.setSize(this.wrap.getSize());
22963         }
22964         if (this.resizeEl) {
22965             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22966             // should trigger onReize..
22967         }
22968         
22969     },
22970
22971     // private
22972     onResize : function(w, h)
22973     {
22974         Roo.log('resize: ' +w + ',' + h );
22975         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
22976         var ew = false;
22977         var eh = false;
22978         
22979         if(this.inputEl() ){
22980             if(typeof w == 'number'){
22981                 var aw = w - this.wrap.getFrameWidth('lr');
22982                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
22983                 ew = aw;
22984             }
22985             if(typeof h == 'number'){
22986                  var tbh = -11;  // fixme it needs to tool bar size!
22987                 for (var i =0; i < this.toolbars.length;i++) {
22988                     // fixme - ask toolbars for heights?
22989                     tbh += this.toolbars[i].el.getHeight();
22990                     //if (this.toolbars[i].footer) {
22991                     //    tbh += this.toolbars[i].footer.el.getHeight();
22992                     //}
22993                 }
22994               
22995                 
22996                 
22997                 
22998                 
22999                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23000                 ah -= 5; // knock a few pixes off for look..
23001                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23002                 var eh = ah;
23003             }
23004         }
23005         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23006         this.editorcore.onResize(ew,eh);
23007         
23008     },
23009
23010     /**
23011      * Toggles the editor between standard and source edit mode.
23012      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23013      */
23014     toggleSourceEdit : function(sourceEditMode)
23015     {
23016         this.editorcore.toggleSourceEdit(sourceEditMode);
23017         
23018         if(this.editorcore.sourceEditMode){
23019             Roo.log('editor - showing textarea');
23020             
23021 //            Roo.log('in');
23022 //            Roo.log(this.syncValue());
23023             this.syncValue();
23024             this.inputEl().removeClass(['hide', 'x-hidden']);
23025             this.inputEl().dom.removeAttribute('tabIndex');
23026             this.inputEl().focus();
23027         }else{
23028             Roo.log('editor - hiding textarea');
23029 //            Roo.log('out')
23030 //            Roo.log(this.pushValue()); 
23031             this.pushValue();
23032             
23033             this.inputEl().addClass(['hide', 'x-hidden']);
23034             this.inputEl().dom.setAttribute('tabIndex', -1);
23035             //this.deferFocus();
23036         }
23037          
23038         if(this.resizable){
23039             this.setSize(this.wrap.getSize());
23040         }
23041         
23042         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23043     },
23044  
23045     // private (for BoxComponent)
23046     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23047
23048     // private (for BoxComponent)
23049     getResizeEl : function(){
23050         return this.wrap;
23051     },
23052
23053     // private (for BoxComponent)
23054     getPositionEl : function(){
23055         return this.wrap;
23056     },
23057
23058     // private
23059     initEvents : function(){
23060         this.originalValue = this.getValue();
23061     },
23062
23063 //    /**
23064 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23065 //     * @method
23066 //     */
23067 //    markInvalid : Roo.emptyFn,
23068 //    /**
23069 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23070 //     * @method
23071 //     */
23072 //    clearInvalid : Roo.emptyFn,
23073
23074     setValue : function(v){
23075         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23076         this.editorcore.pushValue();
23077     },
23078
23079      
23080     // private
23081     deferFocus : function(){
23082         this.focus.defer(10, this);
23083     },
23084
23085     // doc'ed in Field
23086     focus : function(){
23087         this.editorcore.focus();
23088         
23089     },
23090       
23091
23092     // private
23093     onDestroy : function(){
23094         
23095         
23096         
23097         if(this.rendered){
23098             
23099             for (var i =0; i < this.toolbars.length;i++) {
23100                 // fixme - ask toolbars for heights?
23101                 this.toolbars[i].onDestroy();
23102             }
23103             
23104             this.wrap.dom.innerHTML = '';
23105             this.wrap.remove();
23106         }
23107     },
23108
23109     // private
23110     onFirstFocus : function(){
23111         //Roo.log("onFirstFocus");
23112         this.editorcore.onFirstFocus();
23113          for (var i =0; i < this.toolbars.length;i++) {
23114             this.toolbars[i].onFirstFocus();
23115         }
23116         
23117     },
23118     
23119     // private
23120     syncValue : function()
23121     {   
23122         this.editorcore.syncValue();
23123     },
23124     
23125     pushValue : function()
23126     {   
23127         this.editorcore.pushValue();
23128     }
23129      
23130     
23131     // hide stuff that is not compatible
23132     /**
23133      * @event blur
23134      * @hide
23135      */
23136     /**
23137      * @event change
23138      * @hide
23139      */
23140     /**
23141      * @event focus
23142      * @hide
23143      */
23144     /**
23145      * @event specialkey
23146      * @hide
23147      */
23148     /**
23149      * @cfg {String} fieldClass @hide
23150      */
23151     /**
23152      * @cfg {String} focusClass @hide
23153      */
23154     /**
23155      * @cfg {String} autoCreate @hide
23156      */
23157     /**
23158      * @cfg {String} inputType @hide
23159      */
23160     /**
23161      * @cfg {String} invalidClass @hide
23162      */
23163     /**
23164      * @cfg {String} invalidText @hide
23165      */
23166     /**
23167      * @cfg {String} msgFx @hide
23168      */
23169     /**
23170      * @cfg {String} validateOnBlur @hide
23171      */
23172 });
23173  
23174     
23175    
23176    
23177    
23178       
23179 Roo.namespace('Roo.bootstrap.htmleditor');
23180 /**
23181  * @class Roo.bootstrap.HtmlEditorToolbar1
23182  * Basic Toolbar
23183  * 
23184  * Usage:
23185  *
23186  new Roo.bootstrap.HtmlEditor({
23187     ....
23188     toolbars : [
23189         new Roo.bootstrap.HtmlEditorToolbar1({
23190             disable : { fonts: 1 , format: 1, ..., ... , ...],
23191             btns : [ .... ]
23192         })
23193     }
23194      
23195  * 
23196  * @cfg {Object} disable List of elements to disable..
23197  * @cfg {Array} btns List of additional buttons.
23198  * 
23199  * 
23200  * NEEDS Extra CSS? 
23201  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23202  */
23203  
23204 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23205 {
23206     
23207     Roo.apply(this, config);
23208     
23209     // default disabled, based on 'good practice'..
23210     this.disable = this.disable || {};
23211     Roo.applyIf(this.disable, {
23212         fontSize : true,
23213         colors : true,
23214         specialElements : true
23215     });
23216     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23217     
23218     this.editor = config.editor;
23219     this.editorcore = config.editor.editorcore;
23220     
23221     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23222     
23223     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23224     // dont call parent... till later.
23225 }
23226 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23227      
23228     bar : true,
23229     
23230     editor : false,
23231     editorcore : false,
23232     
23233     
23234     formats : [
23235         "p" ,  
23236         "h1","h2","h3","h4","h5","h6", 
23237         "pre", "code", 
23238         "abbr", "acronym", "address", "cite", "samp", "var",
23239         'div','span'
23240     ],
23241     
23242     onRender : function(ct, position)
23243     {
23244        // Roo.log("Call onRender: " + this.xtype);
23245         
23246        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23247        Roo.log(this.el);
23248        this.el.dom.style.marginBottom = '0';
23249        var _this = this;
23250        var editorcore = this.editorcore;
23251        var editor= this.editor;
23252        
23253        var children = [];
23254        var btn = function(id,cmd , toggle, handler, html){
23255        
23256             var  event = toggle ? 'toggle' : 'click';
23257        
23258             var a = {
23259                 size : 'sm',
23260                 xtype: 'Button',
23261                 xns: Roo.bootstrap,
23262                 glyphicon : id,
23263                 cmd : id || cmd,
23264                 enableToggle:toggle !== false,
23265                 html : html || '',
23266                 pressed : toggle ? false : null,
23267                 listeners : {}
23268             };
23269             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23270                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23271             };
23272             children.push(a);
23273             return a;
23274        }
23275        
23276     //    var cb_box = function...
23277         
23278         var style = {
23279                 xtype: 'Button',
23280                 size : 'sm',
23281                 xns: Roo.bootstrap,
23282                 glyphicon : 'font',
23283                 //html : 'submit'
23284                 menu : {
23285                     xtype: 'Menu',
23286                     xns: Roo.bootstrap,
23287                     items:  []
23288                 }
23289         };
23290         Roo.each(this.formats, function(f) {
23291             style.menu.items.push({
23292                 xtype :'MenuItem',
23293                 xns: Roo.bootstrap,
23294                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23295                 tagname : f,
23296                 listeners : {
23297                     click : function()
23298                     {
23299                         editorcore.insertTag(this.tagname);
23300                         editor.focus();
23301                     }
23302                 }
23303                 
23304             });
23305         });
23306         children.push(style);   
23307         
23308         btn('bold',false,true);
23309         btn('italic',false,true);
23310         btn('align-left', 'justifyleft',true);
23311         btn('align-center', 'justifycenter',true);
23312         btn('align-right' , 'justifyright',true);
23313         btn('link', false, false, function(btn) {
23314             //Roo.log("create link?");
23315             var url = prompt(this.createLinkText, this.defaultLinkValue);
23316             if(url && url != 'http:/'+'/'){
23317                 this.editorcore.relayCmd('createlink', url);
23318             }
23319         }),
23320         btn('list','insertunorderedlist',true);
23321         btn('pencil', false,true, function(btn){
23322                 Roo.log(this);
23323                 this.toggleSourceEdit(btn.pressed);
23324         });
23325         
23326         if (this.editor.btns.length > 0) {
23327             for (var i = 0; i<this.editor.btns.length; i++) {
23328                 children.push(this.editor.btns[i]);
23329             }
23330         }
23331         
23332         /*
23333         var cog = {
23334                 xtype: 'Button',
23335                 size : 'sm',
23336                 xns: Roo.bootstrap,
23337                 glyphicon : 'cog',
23338                 //html : 'submit'
23339                 menu : {
23340                     xtype: 'Menu',
23341                     xns: Roo.bootstrap,
23342                     items:  []
23343                 }
23344         };
23345         
23346         cog.menu.items.push({
23347             xtype :'MenuItem',
23348             xns: Roo.bootstrap,
23349             html : Clean styles,
23350             tagname : f,
23351             listeners : {
23352                 click : function()
23353                 {
23354                     editorcore.insertTag(this.tagname);
23355                     editor.focus();
23356                 }
23357             }
23358             
23359         });
23360        */
23361         
23362          
23363        this.xtype = 'NavSimplebar';
23364         
23365         for(var i=0;i< children.length;i++) {
23366             
23367             this.buttons.add(this.addxtypeChild(children[i]));
23368             
23369         }
23370         
23371         editor.on('editorevent', this.updateToolbar, this);
23372     },
23373     onBtnClick : function(id)
23374     {
23375        this.editorcore.relayCmd(id);
23376        this.editorcore.focus();
23377     },
23378     
23379     /**
23380      * Protected method that will not generally be called directly. It triggers
23381      * a toolbar update by reading the markup state of the current selection in the editor.
23382      */
23383     updateToolbar: function(){
23384
23385         if(!this.editorcore.activated){
23386             this.editor.onFirstFocus(); // is this neeed?
23387             return;
23388         }
23389
23390         var btns = this.buttons; 
23391         var doc = this.editorcore.doc;
23392         btns.get('bold').setActive(doc.queryCommandState('bold'));
23393         btns.get('italic').setActive(doc.queryCommandState('italic'));
23394         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23395         
23396         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23397         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23398         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23399         
23400         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23401         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23402          /*
23403         
23404         var ans = this.editorcore.getAllAncestors();
23405         if (this.formatCombo) {
23406             
23407             
23408             var store = this.formatCombo.store;
23409             this.formatCombo.setValue("");
23410             for (var i =0; i < ans.length;i++) {
23411                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23412                     // select it..
23413                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23414                     break;
23415                 }
23416             }
23417         }
23418         
23419         
23420         
23421         // hides menus... - so this cant be on a menu...
23422         Roo.bootstrap.MenuMgr.hideAll();
23423         */
23424         Roo.bootstrap.MenuMgr.hideAll();
23425         //this.editorsyncValue();
23426     },
23427     onFirstFocus: function() {
23428         this.buttons.each(function(item){
23429            item.enable();
23430         });
23431     },
23432     toggleSourceEdit : function(sourceEditMode){
23433         
23434           
23435         if(sourceEditMode){
23436             Roo.log("disabling buttons");
23437            this.buttons.each( function(item){
23438                 if(item.cmd != 'pencil'){
23439                     item.disable();
23440                 }
23441             });
23442           
23443         }else{
23444             Roo.log("enabling buttons");
23445             if(this.editorcore.initialized){
23446                 this.buttons.each( function(item){
23447                     item.enable();
23448                 });
23449             }
23450             
23451         }
23452         Roo.log("calling toggole on editor");
23453         // tell the editor that it's been pressed..
23454         this.editor.toggleSourceEdit(sourceEditMode);
23455        
23456     }
23457 });
23458
23459
23460
23461
23462
23463 /**
23464  * @class Roo.bootstrap.Table.AbstractSelectionModel
23465  * @extends Roo.util.Observable
23466  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23467  * implemented by descendant classes.  This class should not be directly instantiated.
23468  * @constructor
23469  */
23470 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23471     this.locked = false;
23472     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23473 };
23474
23475
23476 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23477     /** @ignore Called by the grid automatically. Do not call directly. */
23478     init : function(grid){
23479         this.grid = grid;
23480         this.initEvents();
23481     },
23482
23483     /**
23484      * Locks the selections.
23485      */
23486     lock : function(){
23487         this.locked = true;
23488     },
23489
23490     /**
23491      * Unlocks the selections.
23492      */
23493     unlock : function(){
23494         this.locked = false;
23495     },
23496
23497     /**
23498      * Returns true if the selections are locked.
23499      * @return {Boolean}
23500      */
23501     isLocked : function(){
23502         return this.locked;
23503     }
23504 });
23505 /**
23506  * @extends Roo.bootstrap.Table.AbstractSelectionModel
23507  * @class Roo.bootstrap.Table.RowSelectionModel
23508  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23509  * It supports multiple selections and keyboard selection/navigation. 
23510  * @constructor
23511  * @param {Object} config
23512  */
23513
23514 Roo.bootstrap.Table.RowSelectionModel = function(config){
23515     Roo.apply(this, config);
23516     this.selections = new Roo.util.MixedCollection(false, function(o){
23517         return o.id;
23518     });
23519
23520     this.last = false;
23521     this.lastActive = false;
23522
23523     this.addEvents({
23524         /**
23525              * @event selectionchange
23526              * Fires when the selection changes
23527              * @param {SelectionModel} this
23528              */
23529             "selectionchange" : true,
23530         /**
23531              * @event afterselectionchange
23532              * Fires after the selection changes (eg. by key press or clicking)
23533              * @param {SelectionModel} this
23534              */
23535             "afterselectionchange" : true,
23536         /**
23537              * @event beforerowselect
23538              * Fires when a row is selected being selected, return false to cancel.
23539              * @param {SelectionModel} this
23540              * @param {Number} rowIndex The selected index
23541              * @param {Boolean} keepExisting False if other selections will be cleared
23542              */
23543             "beforerowselect" : true,
23544         /**
23545              * @event rowselect
23546              * Fires when a row is selected.
23547              * @param {SelectionModel} this
23548              * @param {Number} rowIndex The selected index
23549              * @param {Roo.data.Record} r The record
23550              */
23551             "rowselect" : true,
23552         /**
23553              * @event rowdeselect
23554              * Fires when a row is deselected.
23555              * @param {SelectionModel} this
23556              * @param {Number} rowIndex The selected index
23557              */
23558         "rowdeselect" : true
23559     });
23560     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23561     this.locked = false;
23562  };
23563
23564 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
23565     /**
23566      * @cfg {Boolean} singleSelect
23567      * True to allow selection of only one row at a time (defaults to false)
23568      */
23569     singleSelect : false,
23570
23571     // private
23572     initEvents : function()
23573     {
23574
23575         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23576         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
23577         //}else{ // allow click to work like normal
23578          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
23579         //}
23580         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23581         this.grid.on("rowclick", this.handleMouseDown, this);
23582         
23583         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23584             "up" : function(e){
23585                 if(!e.shiftKey){
23586                     this.selectPrevious(e.shiftKey);
23587                 }else if(this.last !== false && this.lastActive !== false){
23588                     var last = this.last;
23589                     this.selectRange(this.last,  this.lastActive-1);
23590                     this.grid.getView().focusRow(this.lastActive);
23591                     if(last !== false){
23592                         this.last = last;
23593                     }
23594                 }else{
23595                     this.selectFirstRow();
23596                 }
23597                 this.fireEvent("afterselectionchange", this);
23598             },
23599             "down" : function(e){
23600                 if(!e.shiftKey){
23601                     this.selectNext(e.shiftKey);
23602                 }else if(this.last !== false && this.lastActive !== false){
23603                     var last = this.last;
23604                     this.selectRange(this.last,  this.lastActive+1);
23605                     this.grid.getView().focusRow(this.lastActive);
23606                     if(last !== false){
23607                         this.last = last;
23608                     }
23609                 }else{
23610                     this.selectFirstRow();
23611                 }
23612                 this.fireEvent("afterselectionchange", this);
23613             },
23614             scope: this
23615         });
23616         this.grid.store.on('load', function(){
23617             this.selections.clear();
23618         },this);
23619         /*
23620         var view = this.grid.view;
23621         view.on("refresh", this.onRefresh, this);
23622         view.on("rowupdated", this.onRowUpdated, this);
23623         view.on("rowremoved", this.onRemove, this);
23624         */
23625     },
23626
23627     // private
23628     onRefresh : function()
23629     {
23630         var ds = this.grid.store, i, v = this.grid.view;
23631         var s = this.selections;
23632         s.each(function(r){
23633             if((i = ds.indexOfId(r.id)) != -1){
23634                 v.onRowSelect(i);
23635             }else{
23636                 s.remove(r);
23637             }
23638         });
23639     },
23640
23641     // private
23642     onRemove : function(v, index, r){
23643         this.selections.remove(r);
23644     },
23645
23646     // private
23647     onRowUpdated : function(v, index, r){
23648         if(this.isSelected(r)){
23649             v.onRowSelect(index);
23650         }
23651     },
23652
23653     /**
23654      * Select records.
23655      * @param {Array} records The records to select
23656      * @param {Boolean} keepExisting (optional) True to keep existing selections
23657      */
23658     selectRecords : function(records, keepExisting)
23659     {
23660         if(!keepExisting){
23661             this.clearSelections();
23662         }
23663             var ds = this.grid.store;
23664         for(var i = 0, len = records.length; i < len; i++){
23665             this.selectRow(ds.indexOf(records[i]), true);
23666         }
23667     },
23668
23669     /**
23670      * Gets the number of selected rows.
23671      * @return {Number}
23672      */
23673     getCount : function(){
23674         return this.selections.length;
23675     },
23676
23677     /**
23678      * Selects the first row in the grid.
23679      */
23680     selectFirstRow : function(){
23681         this.selectRow(0);
23682     },
23683
23684     /**
23685      * Select the last row.
23686      * @param {Boolean} keepExisting (optional) True to keep existing selections
23687      */
23688     selectLastRow : function(keepExisting){
23689         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
23690         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
23691     },
23692
23693     /**
23694      * Selects the row immediately following the last selected row.
23695      * @param {Boolean} keepExisting (optional) True to keep existing selections
23696      */
23697     selectNext : function(keepExisting)
23698     {
23699             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
23700             this.selectRow(this.last+1, keepExisting);
23701             this.grid.getView().focusRow(this.last);
23702         }
23703     },
23704
23705     /**
23706      * Selects the row that precedes the last selected row.
23707      * @param {Boolean} keepExisting (optional) True to keep existing selections
23708      */
23709     selectPrevious : function(keepExisting){
23710         if(this.last){
23711             this.selectRow(this.last-1, keepExisting);
23712             this.grid.getView().focusRow(this.last);
23713         }
23714     },
23715
23716     /**
23717      * Returns the selected records
23718      * @return {Array} Array of selected records
23719      */
23720     getSelections : function(){
23721         return [].concat(this.selections.items);
23722     },
23723
23724     /**
23725      * Returns the first selected record.
23726      * @return {Record}
23727      */
23728     getSelected : function(){
23729         return this.selections.itemAt(0);
23730     },
23731
23732
23733     /**
23734      * Clears all selections.
23735      */
23736     clearSelections : function(fast)
23737     {
23738         if(this.locked) {
23739             return;
23740         }
23741         if(fast !== true){
23742                 var ds = this.grid.store;
23743             var s = this.selections;
23744             s.each(function(r){
23745                 this.deselectRow(ds.indexOfId(r.id));
23746             }, this);
23747             s.clear();
23748         }else{
23749             this.selections.clear();
23750         }
23751         this.last = false;
23752     },
23753
23754
23755     /**
23756      * Selects all rows.
23757      */
23758     selectAll : function(){
23759         if(this.locked) {
23760             return;
23761         }
23762         this.selections.clear();
23763         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
23764             this.selectRow(i, true);
23765         }
23766     },
23767
23768     /**
23769      * Returns True if there is a selection.
23770      * @return {Boolean}
23771      */
23772     hasSelection : function(){
23773         return this.selections.length > 0;
23774     },
23775
23776     /**
23777      * Returns True if the specified row is selected.
23778      * @param {Number/Record} record The record or index of the record to check
23779      * @return {Boolean}
23780      */
23781     isSelected : function(index){
23782             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
23783         return (r && this.selections.key(r.id) ? true : false);
23784     },
23785
23786     /**
23787      * Returns True if the specified record id is selected.
23788      * @param {String} id The id of record to check
23789      * @return {Boolean}
23790      */
23791     isIdSelected : function(id){
23792         return (this.selections.key(id) ? true : false);
23793     },
23794
23795
23796     // private
23797     handleMouseDBClick : function(e, t){
23798         
23799     },
23800     // private
23801     handleMouseDown : function(e, t)
23802     {
23803             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
23804         if(this.isLocked() || rowIndex < 0 ){
23805             return;
23806         };
23807         if(e.shiftKey && this.last !== false){
23808             var last = this.last;
23809             this.selectRange(last, rowIndex, e.ctrlKey);
23810             this.last = last; // reset the last
23811             t.focus();
23812     
23813         }else{
23814             var isSelected = this.isSelected(rowIndex);
23815             //Roo.log("select row:" + rowIndex);
23816             if(isSelected){
23817                 this.deselectRow(rowIndex);
23818             } else {
23819                         this.selectRow(rowIndex, true);
23820             }
23821     
23822             /*
23823                 if(e.button !== 0 && isSelected){
23824                 alert('rowIndex 2: ' + rowIndex);
23825                     view.focusRow(rowIndex);
23826                 }else if(e.ctrlKey && isSelected){
23827                     this.deselectRow(rowIndex);
23828                 }else if(!isSelected){
23829                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
23830                     view.focusRow(rowIndex);
23831                 }
23832             */
23833         }
23834         this.fireEvent("afterselectionchange", this);
23835     },
23836     // private
23837     handleDragableRowClick :  function(grid, rowIndex, e) 
23838     {
23839         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
23840             this.selectRow(rowIndex, false);
23841             grid.view.focusRow(rowIndex);
23842              this.fireEvent("afterselectionchange", this);
23843         }
23844     },
23845     
23846     /**
23847      * Selects multiple rows.
23848      * @param {Array} rows Array of the indexes of the row to select
23849      * @param {Boolean} keepExisting (optional) True to keep existing selections
23850      */
23851     selectRows : function(rows, keepExisting){
23852         if(!keepExisting){
23853             this.clearSelections();
23854         }
23855         for(var i = 0, len = rows.length; i < len; i++){
23856             this.selectRow(rows[i], true);
23857         }
23858     },
23859
23860     /**
23861      * Selects a range of rows. All rows in between startRow and endRow are also selected.
23862      * @param {Number} startRow The index of the first row in the range
23863      * @param {Number} endRow The index of the last row in the range
23864      * @param {Boolean} keepExisting (optional) True to retain existing selections
23865      */
23866     selectRange : function(startRow, endRow, keepExisting){
23867         if(this.locked) {
23868             return;
23869         }
23870         if(!keepExisting){
23871             this.clearSelections();
23872         }
23873         if(startRow <= endRow){
23874             for(var i = startRow; i <= endRow; i++){
23875                 this.selectRow(i, true);
23876             }
23877         }else{
23878             for(var i = startRow; i >= endRow; i--){
23879                 this.selectRow(i, true);
23880             }
23881         }
23882     },
23883
23884     /**
23885      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
23886      * @param {Number} startRow The index of the first row in the range
23887      * @param {Number} endRow The index of the last row in the range
23888      */
23889     deselectRange : function(startRow, endRow, preventViewNotify){
23890         if(this.locked) {
23891             return;
23892         }
23893         for(var i = startRow; i <= endRow; i++){
23894             this.deselectRow(i, preventViewNotify);
23895         }
23896     },
23897
23898     /**
23899      * Selects a row.
23900      * @param {Number} row The index of the row to select
23901      * @param {Boolean} keepExisting (optional) True to keep existing selections
23902      */
23903     selectRow : function(index, keepExisting, preventViewNotify)
23904     {
23905             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
23906             return;
23907         }
23908         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
23909             if(!keepExisting || this.singleSelect){
23910                 this.clearSelections();
23911             }
23912             
23913             var r = this.grid.store.getAt(index);
23914             //console.log('selectRow - record id :' + r.id);
23915             
23916             this.selections.add(r);
23917             this.last = this.lastActive = index;
23918             if(!preventViewNotify){
23919                 var proxy = new Roo.Element(
23920                                 this.grid.getRowDom(index)
23921                 );
23922                 proxy.addClass('bg-info info');
23923             }
23924             this.fireEvent("rowselect", this, index, r);
23925             this.fireEvent("selectionchange", this);
23926         }
23927     },
23928
23929     /**
23930      * Deselects a row.
23931      * @param {Number} row The index of the row to deselect
23932      */
23933     deselectRow : function(index, preventViewNotify)
23934     {
23935         if(this.locked) {
23936             return;
23937         }
23938         if(this.last == index){
23939             this.last = false;
23940         }
23941         if(this.lastActive == index){
23942             this.lastActive = false;
23943         }
23944         
23945         var r = this.grid.store.getAt(index);
23946         if (!r) {
23947             return;
23948         }
23949         
23950         this.selections.remove(r);
23951         //.console.log('deselectRow - record id :' + r.id);
23952         if(!preventViewNotify){
23953         
23954             var proxy = new Roo.Element(
23955                 this.grid.getRowDom(index)
23956             );
23957             proxy.removeClass('bg-info info');
23958         }
23959         this.fireEvent("rowdeselect", this, index);
23960         this.fireEvent("selectionchange", this);
23961     },
23962
23963     // private
23964     restoreLast : function(){
23965         if(this._last){
23966             this.last = this._last;
23967         }
23968     },
23969
23970     // private
23971     acceptsNav : function(row, col, cm){
23972         return !cm.isHidden(col) && cm.isCellEditable(col, row);
23973     },
23974
23975     // private
23976     onEditorKey : function(field, e){
23977         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
23978         if(k == e.TAB){
23979             e.stopEvent();
23980             ed.completeEdit();
23981             if(e.shiftKey){
23982                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
23983             }else{
23984                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
23985             }
23986         }else if(k == e.ENTER && !e.ctrlKey){
23987             e.stopEvent();
23988             ed.completeEdit();
23989             if(e.shiftKey){
23990                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
23991             }else{
23992                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
23993             }
23994         }else if(k == e.ESC){
23995             ed.cancelEdit();
23996         }
23997         if(newCell){
23998             g.startEditing(newCell[0], newCell[1]);
23999         }
24000     }
24001 });
24002 /*
24003  * Based on:
24004  * Ext JS Library 1.1.1
24005  * Copyright(c) 2006-2007, Ext JS, LLC.
24006  *
24007  * Originally Released Under LGPL - original licence link has changed is not relivant.
24008  *
24009  * Fork - LGPL
24010  * <script type="text/javascript">
24011  */
24012  
24013 /**
24014  * @class Roo.bootstrap.PagingToolbar
24015  * @extends Roo.bootstrap.NavSimplebar
24016  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24017  * @constructor
24018  * Create a new PagingToolbar
24019  * @param {Object} config The config object
24020  * @param {Roo.data.Store} store
24021  */
24022 Roo.bootstrap.PagingToolbar = function(config)
24023 {
24024     // old args format still supported... - xtype is prefered..
24025         // created from xtype...
24026     
24027     this.ds = config.dataSource;
24028     
24029     if (config.store && !this.ds) {
24030         this.store= Roo.factory(config.store, Roo.data);
24031         this.ds = this.store;
24032         this.ds.xmodule = this.xmodule || false;
24033     }
24034     
24035     this.toolbarItems = [];
24036     if (config.items) {
24037         this.toolbarItems = config.items;
24038     }
24039     
24040     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24041     
24042     this.cursor = 0;
24043     
24044     if (this.ds) { 
24045         this.bind(this.ds);
24046     }
24047     
24048     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24049     
24050 };
24051
24052 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24053     /**
24054      * @cfg {Roo.data.Store} dataSource
24055      * The underlying data store providing the paged data
24056      */
24057     /**
24058      * @cfg {String/HTMLElement/Element} container
24059      * container The id or element that will contain the toolbar
24060      */
24061     /**
24062      * @cfg {Boolean} displayInfo
24063      * True to display the displayMsg (defaults to false)
24064      */
24065     /**
24066      * @cfg {Number} pageSize
24067      * The number of records to display per page (defaults to 20)
24068      */
24069     pageSize: 20,
24070     /**
24071      * @cfg {String} displayMsg
24072      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24073      */
24074     displayMsg : 'Displaying {0} - {1} of {2}',
24075     /**
24076      * @cfg {String} emptyMsg
24077      * The message to display when no records are found (defaults to "No data to display")
24078      */
24079     emptyMsg : 'No data to display',
24080     /**
24081      * Customizable piece of the default paging text (defaults to "Page")
24082      * @type String
24083      */
24084     beforePageText : "Page",
24085     /**
24086      * Customizable piece of the default paging text (defaults to "of %0")
24087      * @type String
24088      */
24089     afterPageText : "of {0}",
24090     /**
24091      * Customizable piece of the default paging text (defaults to "First Page")
24092      * @type String
24093      */
24094     firstText : "First Page",
24095     /**
24096      * Customizable piece of the default paging text (defaults to "Previous Page")
24097      * @type String
24098      */
24099     prevText : "Previous Page",
24100     /**
24101      * Customizable piece of the default paging text (defaults to "Next Page")
24102      * @type String
24103      */
24104     nextText : "Next Page",
24105     /**
24106      * Customizable piece of the default paging text (defaults to "Last Page")
24107      * @type String
24108      */
24109     lastText : "Last Page",
24110     /**
24111      * Customizable piece of the default paging text (defaults to "Refresh")
24112      * @type String
24113      */
24114     refreshText : "Refresh",
24115
24116     buttons : false,
24117     // private
24118     onRender : function(ct, position) 
24119     {
24120         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24121         this.navgroup.parentId = this.id;
24122         this.navgroup.onRender(this.el, null);
24123         // add the buttons to the navgroup
24124         
24125         if(this.displayInfo){
24126             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24127             this.displayEl = this.el.select('.x-paging-info', true).first();
24128 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24129 //            this.displayEl = navel.el.select('span',true).first();
24130         }
24131         
24132         var _this = this;
24133         
24134         if(this.buttons){
24135             Roo.each(_this.buttons, function(e){ // this might need to use render????
24136                Roo.factory(e).onRender(_this.el, null);
24137             });
24138         }
24139             
24140         Roo.each(_this.toolbarItems, function(e) {
24141             _this.navgroup.addItem(e);
24142         });
24143         
24144         
24145         this.first = this.navgroup.addItem({
24146             tooltip: this.firstText,
24147             cls: "prev",
24148             icon : 'fa fa-backward',
24149             disabled: true,
24150             preventDefault: true,
24151             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24152         });
24153         
24154         this.prev =  this.navgroup.addItem({
24155             tooltip: this.prevText,
24156             cls: "prev",
24157             icon : 'fa fa-step-backward',
24158             disabled: true,
24159             preventDefault: true,
24160             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24161         });
24162     //this.addSeparator();
24163         
24164         
24165         var field = this.navgroup.addItem( {
24166             tagtype : 'span',
24167             cls : 'x-paging-position',
24168             
24169             html : this.beforePageText  +
24170                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24171                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24172          } ); //?? escaped?
24173         
24174         this.field = field.el.select('input', true).first();
24175         this.field.on("keydown", this.onPagingKeydown, this);
24176         this.field.on("focus", function(){this.dom.select();});
24177     
24178     
24179         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24180         //this.field.setHeight(18);
24181         //this.addSeparator();
24182         this.next = this.navgroup.addItem({
24183             tooltip: this.nextText,
24184             cls: "next",
24185             html : ' <i class="fa fa-step-forward">',
24186             disabled: true,
24187             preventDefault: true,
24188             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24189         });
24190         this.last = this.navgroup.addItem({
24191             tooltip: this.lastText,
24192             icon : 'fa fa-forward',
24193             cls: "next",
24194             disabled: true,
24195             preventDefault: true,
24196             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24197         });
24198     //this.addSeparator();
24199         this.loading = this.navgroup.addItem({
24200             tooltip: this.refreshText,
24201             icon: 'fa fa-refresh',
24202             preventDefault: true,
24203             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24204         });
24205         
24206     },
24207
24208     // private
24209     updateInfo : function(){
24210         if(this.displayEl){
24211             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24212             var msg = count == 0 ?
24213                 this.emptyMsg :
24214                 String.format(
24215                     this.displayMsg,
24216                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24217                 );
24218             this.displayEl.update(msg);
24219         }
24220     },
24221
24222     // private
24223     onLoad : function(ds, r, o)
24224     {
24225         this.cursor = o.params ? o.params.start : 0;
24226         var d = this.getPageData(),
24227             ap = d.activePage,
24228             ps = d.pages;
24229         
24230         
24231         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24232         this.field.dom.value = ap;
24233         this.first.setDisabled(ap == 1);
24234         this.prev.setDisabled(ap == 1);
24235         this.next.setDisabled(ap == ps);
24236         this.last.setDisabled(ap == ps);
24237         this.loading.enable();
24238         this.updateInfo();
24239     },
24240
24241     // private
24242     getPageData : function(){
24243         var total = this.ds.getTotalCount();
24244         return {
24245             total : total,
24246             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24247             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24248         };
24249     },
24250
24251     // private
24252     onLoadError : function(){
24253         this.loading.enable();
24254     },
24255
24256     // private
24257     onPagingKeydown : function(e){
24258         var k = e.getKey();
24259         var d = this.getPageData();
24260         if(k == e.RETURN){
24261             var v = this.field.dom.value, pageNum;
24262             if(!v || isNaN(pageNum = parseInt(v, 10))){
24263                 this.field.dom.value = d.activePage;
24264                 return;
24265             }
24266             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24267             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24268             e.stopEvent();
24269         }
24270         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))
24271         {
24272           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24273           this.field.dom.value = pageNum;
24274           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24275           e.stopEvent();
24276         }
24277         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24278         {
24279           var v = this.field.dom.value, pageNum; 
24280           var increment = (e.shiftKey) ? 10 : 1;
24281           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24282                 increment *= -1;
24283           }
24284           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24285             this.field.dom.value = d.activePage;
24286             return;
24287           }
24288           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24289           {
24290             this.field.dom.value = parseInt(v, 10) + increment;
24291             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24292             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24293           }
24294           e.stopEvent();
24295         }
24296     },
24297
24298     // private
24299     beforeLoad : function(){
24300         if(this.loading){
24301             this.loading.disable();
24302         }
24303     },
24304
24305     // private
24306     onClick : function(which){
24307         
24308         var ds = this.ds;
24309         if (!ds) {
24310             return;
24311         }
24312         
24313         switch(which){
24314             case "first":
24315                 ds.load({params:{start: 0, limit: this.pageSize}});
24316             break;
24317             case "prev":
24318                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24319             break;
24320             case "next":
24321                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24322             break;
24323             case "last":
24324                 var total = ds.getTotalCount();
24325                 var extra = total % this.pageSize;
24326                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24327                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24328             break;
24329             case "refresh":
24330                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24331             break;
24332         }
24333     },
24334
24335     /**
24336      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24337      * @param {Roo.data.Store} store The data store to unbind
24338      */
24339     unbind : function(ds){
24340         ds.un("beforeload", this.beforeLoad, this);
24341         ds.un("load", this.onLoad, this);
24342         ds.un("loadexception", this.onLoadError, this);
24343         ds.un("remove", this.updateInfo, this);
24344         ds.un("add", this.updateInfo, this);
24345         this.ds = undefined;
24346     },
24347
24348     /**
24349      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24350      * @param {Roo.data.Store} store The data store to bind
24351      */
24352     bind : function(ds){
24353         ds.on("beforeload", this.beforeLoad, this);
24354         ds.on("load", this.onLoad, this);
24355         ds.on("loadexception", this.onLoadError, this);
24356         ds.on("remove", this.updateInfo, this);
24357         ds.on("add", this.updateInfo, this);
24358         this.ds = ds;
24359     }
24360 });/*
24361  * - LGPL
24362  *
24363  * element
24364  * 
24365  */
24366
24367 /**
24368  * @class Roo.bootstrap.MessageBar
24369  * @extends Roo.bootstrap.Component
24370  * Bootstrap MessageBar class
24371  * @cfg {String} html contents of the MessageBar
24372  * @cfg {String} weight (info | success | warning | danger) default info
24373  * @cfg {String} beforeClass insert the bar before the given class
24374  * @cfg {Boolean} closable (true | false) default false
24375  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24376  * 
24377  * @constructor
24378  * Create a new Element
24379  * @param {Object} config The config object
24380  */
24381
24382 Roo.bootstrap.MessageBar = function(config){
24383     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24384 };
24385
24386 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24387     
24388     html: '',
24389     weight: 'info',
24390     closable: false,
24391     fixed: false,
24392     beforeClass: 'bootstrap-sticky-wrap',
24393     
24394     getAutoCreate : function(){
24395         
24396         var cfg = {
24397             tag: 'div',
24398             cls: 'alert alert-dismissable alert-' + this.weight,
24399             cn: [
24400                 {
24401                     tag: 'span',
24402                     cls: 'message',
24403                     html: this.html || ''
24404                 }
24405             ]
24406         };
24407         
24408         if(this.fixed){
24409             cfg.cls += ' alert-messages-fixed';
24410         }
24411         
24412         if(this.closable){
24413             cfg.cn.push({
24414                 tag: 'button',
24415                 cls: 'close',
24416                 html: 'x'
24417             });
24418         }
24419         
24420         return cfg;
24421     },
24422     
24423     onRender : function(ct, position)
24424     {
24425         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24426         
24427         if(!this.el){
24428             var cfg = Roo.apply({},  this.getAutoCreate());
24429             cfg.id = Roo.id();
24430             
24431             if (this.cls) {
24432                 cfg.cls += ' ' + this.cls;
24433             }
24434             if (this.style) {
24435                 cfg.style = this.style;
24436             }
24437             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24438             
24439             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24440         }
24441         
24442         this.el.select('>button.close').on('click', this.hide, this);
24443         
24444     },
24445     
24446     show : function()
24447     {
24448         if (!this.rendered) {
24449             this.render();
24450         }
24451         
24452         this.el.show();
24453         
24454         this.fireEvent('show', this);
24455         
24456     },
24457     
24458     hide : function()
24459     {
24460         if (!this.rendered) {
24461             this.render();
24462         }
24463         
24464         this.el.hide();
24465         
24466         this.fireEvent('hide', this);
24467     },
24468     
24469     update : function()
24470     {
24471 //        var e = this.el.dom.firstChild;
24472 //        
24473 //        if(this.closable){
24474 //            e = e.nextSibling;
24475 //        }
24476 //        
24477 //        e.data = this.html || '';
24478
24479         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24480     }
24481    
24482 });
24483
24484  
24485
24486      /*
24487  * - LGPL
24488  *
24489  * Graph
24490  * 
24491  */
24492
24493
24494 /**
24495  * @class Roo.bootstrap.Graph
24496  * @extends Roo.bootstrap.Component
24497  * Bootstrap Graph class
24498 > Prameters
24499  -sm {number} sm 4
24500  -md {number} md 5
24501  @cfg {String} graphtype  bar | vbar | pie
24502  @cfg {number} g_x coodinator | centre x (pie)
24503  @cfg {number} g_y coodinator | centre y (pie)
24504  @cfg {number} g_r radius (pie)
24505  @cfg {number} g_height height of the chart (respected by all elements in the set)
24506  @cfg {number} g_width width of the chart (respected by all elements in the set)
24507  @cfg {Object} title The title of the chart
24508     
24509  -{Array}  values
24510  -opts (object) options for the chart 
24511      o {
24512      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24513      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24514      o vgutter (number)
24515      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.
24516      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24517      o to
24518      o stretch (boolean)
24519      o }
24520  -opts (object) options for the pie
24521      o{
24522      o cut
24523      o startAngle (number)
24524      o endAngle (number)
24525      } 
24526  *
24527  * @constructor
24528  * Create a new Input
24529  * @param {Object} config The config object
24530  */
24531
24532 Roo.bootstrap.Graph = function(config){
24533     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24534     
24535     this.addEvents({
24536         // img events
24537         /**
24538          * @event click
24539          * The img click event for the img.
24540          * @param {Roo.EventObject} e
24541          */
24542         "click" : true
24543     });
24544 };
24545
24546 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
24547     
24548     sm: 4,
24549     md: 5,
24550     graphtype: 'bar',
24551     g_height: 250,
24552     g_width: 400,
24553     g_x: 50,
24554     g_y: 50,
24555     g_r: 30,
24556     opts:{
24557         //g_colors: this.colors,
24558         g_type: 'soft',
24559         g_gutter: '20%'
24560
24561     },
24562     title : false,
24563
24564     getAutoCreate : function(){
24565         
24566         var cfg = {
24567             tag: 'div',
24568             html : null
24569         };
24570         
24571         
24572         return  cfg;
24573     },
24574
24575     onRender : function(ct,position){
24576         
24577         
24578         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24579         
24580         if (typeof(Raphael) == 'undefined') {
24581             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24582             return;
24583         }
24584         
24585         this.raphael = Raphael(this.el.dom);
24586         
24587                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24588                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24589                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24590                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24591                 /*
24592                 r.text(160, 10, "Single Series Chart").attr(txtattr);
24593                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24594                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24595                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
24596                 
24597                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
24598                 r.barchart(330, 10, 300, 220, data1);
24599                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
24600                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
24601                 */
24602                 
24603                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24604                 // r.barchart(30, 30, 560, 250,  xdata, {
24605                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
24606                 //     axis : "0 0 1 1",
24607                 //     axisxlabels :  xdata
24608                 //     //yvalues : cols,
24609                    
24610                 // });
24611 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24612 //        
24613 //        this.load(null,xdata,{
24614 //                axis : "0 0 1 1",
24615 //                axisxlabels :  xdata
24616 //                });
24617
24618     },
24619
24620     load : function(graphtype,xdata,opts)
24621     {
24622         this.raphael.clear();
24623         if(!graphtype) {
24624             graphtype = this.graphtype;
24625         }
24626         if(!opts){
24627             opts = this.opts;
24628         }
24629         var r = this.raphael,
24630             fin = function () {
24631                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
24632             },
24633             fout = function () {
24634                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
24635             },
24636             pfin = function() {
24637                 this.sector.stop();
24638                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
24639
24640                 if (this.label) {
24641                     this.label[0].stop();
24642                     this.label[0].attr({ r: 7.5 });
24643                     this.label[1].attr({ "font-weight": 800 });
24644                 }
24645             },
24646             pfout = function() {
24647                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
24648
24649                 if (this.label) {
24650                     this.label[0].animate({ r: 5 }, 500, "bounce");
24651                     this.label[1].attr({ "font-weight": 400 });
24652                 }
24653             };
24654
24655         switch(graphtype){
24656             case 'bar':
24657                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24658                 break;
24659             case 'hbar':
24660                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24661                 break;
24662             case 'pie':
24663 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
24664 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
24665 //            
24666                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
24667                 
24668                 break;
24669
24670         }
24671         
24672         if(this.title){
24673             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
24674         }
24675         
24676     },
24677     
24678     setTitle: function(o)
24679     {
24680         this.title = o;
24681     },
24682     
24683     initEvents: function() {
24684         
24685         if(!this.href){
24686             this.el.on('click', this.onClick, this);
24687         }
24688     },
24689     
24690     onClick : function(e)
24691     {
24692         Roo.log('img onclick');
24693         this.fireEvent('click', this, e);
24694     }
24695    
24696 });
24697
24698  
24699 /*
24700  * - LGPL
24701  *
24702  * numberBox
24703  * 
24704  */
24705 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24706
24707 /**
24708  * @class Roo.bootstrap.dash.NumberBox
24709  * @extends Roo.bootstrap.Component
24710  * Bootstrap NumberBox class
24711  * @cfg {String} headline Box headline
24712  * @cfg {String} content Box content
24713  * @cfg {String} icon Box icon
24714  * @cfg {String} footer Footer text
24715  * @cfg {String} fhref Footer href
24716  * 
24717  * @constructor
24718  * Create a new NumberBox
24719  * @param {Object} config The config object
24720  */
24721
24722
24723 Roo.bootstrap.dash.NumberBox = function(config){
24724     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
24725     
24726 };
24727
24728 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
24729     
24730     headline : '',
24731     content : '',
24732     icon : '',
24733     footer : '',
24734     fhref : '',
24735     ficon : '',
24736     
24737     getAutoCreate : function(){
24738         
24739         var cfg = {
24740             tag : 'div',
24741             cls : 'small-box ',
24742             cn : [
24743                 {
24744                     tag : 'div',
24745                     cls : 'inner',
24746                     cn :[
24747                         {
24748                             tag : 'h3',
24749                             cls : 'roo-headline',
24750                             html : this.headline
24751                         },
24752                         {
24753                             tag : 'p',
24754                             cls : 'roo-content',
24755                             html : this.content
24756                         }
24757                     ]
24758                 }
24759             ]
24760         };
24761         
24762         if(this.icon){
24763             cfg.cn.push({
24764                 tag : 'div',
24765                 cls : 'icon',
24766                 cn :[
24767                     {
24768                         tag : 'i',
24769                         cls : 'ion ' + this.icon
24770                     }
24771                 ]
24772             });
24773         }
24774         
24775         if(this.footer){
24776             var footer = {
24777                 tag : 'a',
24778                 cls : 'small-box-footer',
24779                 href : this.fhref || '#',
24780                 html : this.footer
24781             };
24782             
24783             cfg.cn.push(footer);
24784             
24785         }
24786         
24787         return  cfg;
24788     },
24789
24790     onRender : function(ct,position){
24791         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
24792
24793
24794        
24795                 
24796     },
24797
24798     setHeadline: function (value)
24799     {
24800         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
24801     },
24802     
24803     setFooter: function (value, href)
24804     {
24805         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
24806         
24807         if(href){
24808             this.el.select('a.small-box-footer',true).first().attr('href', href);
24809         }
24810         
24811     },
24812
24813     setContent: function (value)
24814     {
24815         this.el.select('.roo-content',true).first().dom.innerHTML = value;
24816     },
24817
24818     initEvents: function() 
24819     {   
24820         
24821     }
24822     
24823 });
24824
24825  
24826 /*
24827  * - LGPL
24828  *
24829  * TabBox
24830  * 
24831  */
24832 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24833
24834 /**
24835  * @class Roo.bootstrap.dash.TabBox
24836  * @extends Roo.bootstrap.Component
24837  * Bootstrap TabBox class
24838  * @cfg {String} title Title of the TabBox
24839  * @cfg {String} icon Icon of the TabBox
24840  * @cfg {Boolean} showtabs (true|false) show the tabs default true
24841  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
24842  * 
24843  * @constructor
24844  * Create a new TabBox
24845  * @param {Object} config The config object
24846  */
24847
24848
24849 Roo.bootstrap.dash.TabBox = function(config){
24850     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
24851     this.addEvents({
24852         // raw events
24853         /**
24854          * @event addpane
24855          * When a pane is added
24856          * @param {Roo.bootstrap.dash.TabPane} pane
24857          */
24858         "addpane" : true,
24859         /**
24860          * @event activatepane
24861          * When a pane is activated
24862          * @param {Roo.bootstrap.dash.TabPane} pane
24863          */
24864         "activatepane" : true
24865         
24866          
24867     });
24868     
24869     this.panes = [];
24870 };
24871
24872 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
24873
24874     title : '',
24875     icon : false,
24876     showtabs : true,
24877     tabScrollable : false,
24878     
24879     getChildContainer : function()
24880     {
24881         return this.el.select('.tab-content', true).first();
24882     },
24883     
24884     getAutoCreate : function(){
24885         
24886         var header = {
24887             tag: 'li',
24888             cls: 'pull-left header',
24889             html: this.title,
24890             cn : []
24891         };
24892         
24893         if(this.icon){
24894             header.cn.push({
24895                 tag: 'i',
24896                 cls: 'fa ' + this.icon
24897             });
24898         }
24899         
24900         var h = {
24901             tag: 'ul',
24902             cls: 'nav nav-tabs pull-right',
24903             cn: [
24904                 header
24905             ]
24906         };
24907         
24908         if(this.tabScrollable){
24909             h = {
24910                 tag: 'div',
24911                 cls: 'tab-header',
24912                 cn: [
24913                     {
24914                         tag: 'ul',
24915                         cls: 'nav nav-tabs pull-right',
24916                         cn: [
24917                             header
24918                         ]
24919                     }
24920                 ]
24921             };
24922         }
24923         
24924         var cfg = {
24925             tag: 'div',
24926             cls: 'nav-tabs-custom',
24927             cn: [
24928                 h,
24929                 {
24930                     tag: 'div',
24931                     cls: 'tab-content no-padding',
24932                     cn: []
24933                 }
24934             ]
24935         };
24936
24937         return  cfg;
24938     },
24939     initEvents : function()
24940     {
24941         //Roo.log('add add pane handler');
24942         this.on('addpane', this.onAddPane, this);
24943     },
24944      /**
24945      * Updates the box title
24946      * @param {String} html to set the title to.
24947      */
24948     setTitle : function(value)
24949     {
24950         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
24951     },
24952     onAddPane : function(pane)
24953     {
24954         this.panes.push(pane);
24955         //Roo.log('addpane');
24956         //Roo.log(pane);
24957         // tabs are rendere left to right..
24958         if(!this.showtabs){
24959             return;
24960         }
24961         
24962         var ctr = this.el.select('.nav-tabs', true).first();
24963          
24964          
24965         var existing = ctr.select('.nav-tab',true);
24966         var qty = existing.getCount();;
24967         
24968         
24969         var tab = ctr.createChild({
24970             tag : 'li',
24971             cls : 'nav-tab' + (qty ? '' : ' active'),
24972             cn : [
24973                 {
24974                     tag : 'a',
24975                     href:'#',
24976                     html : pane.title
24977                 }
24978             ]
24979         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
24980         pane.tab = tab;
24981         
24982         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
24983         if (!qty) {
24984             pane.el.addClass('active');
24985         }
24986         
24987                 
24988     },
24989     onTabClick : function(ev,un,ob,pane)
24990     {
24991         //Roo.log('tab - prev default');
24992         ev.preventDefault();
24993         
24994         
24995         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
24996         pane.tab.addClass('active');
24997         //Roo.log(pane.title);
24998         this.getChildContainer().select('.tab-pane',true).removeClass('active');
24999         // technically we should have a deactivate event.. but maybe add later.
25000         // and it should not de-activate the selected tab...
25001         this.fireEvent('activatepane', pane);
25002         pane.el.addClass('active');
25003         pane.fireEvent('activate');
25004         
25005         
25006     },
25007     
25008     getActivePane : function()
25009     {
25010         var r = false;
25011         Roo.each(this.panes, function(p) {
25012             if(p.el.hasClass('active')){
25013                 r = p;
25014                 return false;
25015             }
25016             
25017             return;
25018         });
25019         
25020         return r;
25021     }
25022     
25023     
25024 });
25025
25026  
25027 /*
25028  * - LGPL
25029  *
25030  * Tab pane
25031  * 
25032  */
25033 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25034 /**
25035  * @class Roo.bootstrap.TabPane
25036  * @extends Roo.bootstrap.Component
25037  * Bootstrap TabPane class
25038  * @cfg {Boolean} active (false | true) Default false
25039  * @cfg {String} title title of panel
25040
25041  * 
25042  * @constructor
25043  * Create a new TabPane
25044  * @param {Object} config The config object
25045  */
25046
25047 Roo.bootstrap.dash.TabPane = function(config){
25048     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25049     
25050     this.addEvents({
25051         // raw events
25052         /**
25053          * @event activate
25054          * When a pane is activated
25055          * @param {Roo.bootstrap.dash.TabPane} pane
25056          */
25057         "activate" : true
25058          
25059     });
25060 };
25061
25062 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25063     
25064     active : false,
25065     title : '',
25066     
25067     // the tabBox that this is attached to.
25068     tab : false,
25069      
25070     getAutoCreate : function() 
25071     {
25072         var cfg = {
25073             tag: 'div',
25074             cls: 'tab-pane'
25075         };
25076         
25077         if(this.active){
25078             cfg.cls += ' active';
25079         }
25080         
25081         return cfg;
25082     },
25083     initEvents  : function()
25084     {
25085         //Roo.log('trigger add pane handler');
25086         this.parent().fireEvent('addpane', this)
25087     },
25088     
25089      /**
25090      * Updates the tab title 
25091      * @param {String} html to set the title to.
25092      */
25093     setTitle: function(str)
25094     {
25095         if (!this.tab) {
25096             return;
25097         }
25098         this.title = str;
25099         this.tab.select('a', true).first().dom.innerHTML = str;
25100         
25101     }
25102     
25103     
25104     
25105 });
25106
25107  
25108
25109
25110  /*
25111  * - LGPL
25112  *
25113  * menu
25114  * 
25115  */
25116 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25117
25118 /**
25119  * @class Roo.bootstrap.menu.Menu
25120  * @extends Roo.bootstrap.Component
25121  * Bootstrap Menu class - container for Menu
25122  * @cfg {String} html Text of the menu
25123  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25124  * @cfg {String} icon Font awesome icon
25125  * @cfg {String} pos Menu align to (top | bottom) default bottom
25126  * 
25127  * 
25128  * @constructor
25129  * Create a new Menu
25130  * @param {Object} config The config object
25131  */
25132
25133
25134 Roo.bootstrap.menu.Menu = function(config){
25135     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25136     
25137     this.addEvents({
25138         /**
25139          * @event beforeshow
25140          * Fires before this menu is displayed
25141          * @param {Roo.bootstrap.menu.Menu} this
25142          */
25143         beforeshow : true,
25144         /**
25145          * @event beforehide
25146          * Fires before this menu is hidden
25147          * @param {Roo.bootstrap.menu.Menu} this
25148          */
25149         beforehide : true,
25150         /**
25151          * @event show
25152          * Fires after this menu is displayed
25153          * @param {Roo.bootstrap.menu.Menu} this
25154          */
25155         show : true,
25156         /**
25157          * @event hide
25158          * Fires after this menu is hidden
25159          * @param {Roo.bootstrap.menu.Menu} this
25160          */
25161         hide : true,
25162         /**
25163          * @event click
25164          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25165          * @param {Roo.bootstrap.menu.Menu} this
25166          * @param {Roo.EventObject} e
25167          */
25168         click : true
25169     });
25170     
25171 };
25172
25173 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25174     
25175     submenu : false,
25176     html : '',
25177     weight : 'default',
25178     icon : false,
25179     pos : 'bottom',
25180     
25181     
25182     getChildContainer : function() {
25183         if(this.isSubMenu){
25184             return this.el;
25185         }
25186         
25187         return this.el.select('ul.dropdown-menu', true).first();  
25188     },
25189     
25190     getAutoCreate : function()
25191     {
25192         var text = [
25193             {
25194                 tag : 'span',
25195                 cls : 'roo-menu-text',
25196                 html : this.html
25197             }
25198         ];
25199         
25200         if(this.icon){
25201             text.unshift({
25202                 tag : 'i',
25203                 cls : 'fa ' + this.icon
25204             })
25205         }
25206         
25207         
25208         var cfg = {
25209             tag : 'div',
25210             cls : 'btn-group',
25211             cn : [
25212                 {
25213                     tag : 'button',
25214                     cls : 'dropdown-button btn btn-' + this.weight,
25215                     cn : text
25216                 },
25217                 {
25218                     tag : 'button',
25219                     cls : 'dropdown-toggle btn btn-' + this.weight,
25220                     cn : [
25221                         {
25222                             tag : 'span',
25223                             cls : 'caret'
25224                         }
25225                     ]
25226                 },
25227                 {
25228                     tag : 'ul',
25229                     cls : 'dropdown-menu'
25230                 }
25231             ]
25232             
25233         };
25234         
25235         if(this.pos == 'top'){
25236             cfg.cls += ' dropup';
25237         }
25238         
25239         if(this.isSubMenu){
25240             cfg = {
25241                 tag : 'ul',
25242                 cls : 'dropdown-menu'
25243             }
25244         }
25245         
25246         return cfg;
25247     },
25248     
25249     onRender : function(ct, position)
25250     {
25251         this.isSubMenu = ct.hasClass('dropdown-submenu');
25252         
25253         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25254     },
25255     
25256     initEvents : function() 
25257     {
25258         if(this.isSubMenu){
25259             return;
25260         }
25261         
25262         this.hidden = true;
25263         
25264         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25265         this.triggerEl.on('click', this.onTriggerPress, this);
25266         
25267         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25268         this.buttonEl.on('click', this.onClick, this);
25269         
25270     },
25271     
25272     list : function()
25273     {
25274         if(this.isSubMenu){
25275             return this.el;
25276         }
25277         
25278         return this.el.select('ul.dropdown-menu', true).first();
25279     },
25280     
25281     onClick : function(e)
25282     {
25283         this.fireEvent("click", this, e);
25284     },
25285     
25286     onTriggerPress  : function(e)
25287     {   
25288         if (this.isVisible()) {
25289             this.hide();
25290         } else {
25291             this.show();
25292         }
25293     },
25294     
25295     isVisible : function(){
25296         return !this.hidden;
25297     },
25298     
25299     show : function()
25300     {
25301         this.fireEvent("beforeshow", this);
25302         
25303         this.hidden = false;
25304         this.el.addClass('open');
25305         
25306         Roo.get(document).on("mouseup", this.onMouseUp, this);
25307         
25308         this.fireEvent("show", this);
25309         
25310         
25311     },
25312     
25313     hide : function()
25314     {
25315         this.fireEvent("beforehide", this);
25316         
25317         this.hidden = true;
25318         this.el.removeClass('open');
25319         
25320         Roo.get(document).un("mouseup", this.onMouseUp);
25321         
25322         this.fireEvent("hide", this);
25323     },
25324     
25325     onMouseUp : function()
25326     {
25327         this.hide();
25328     }
25329     
25330 });
25331
25332  
25333  /*
25334  * - LGPL
25335  *
25336  * menu item
25337  * 
25338  */
25339 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25340
25341 /**
25342  * @class Roo.bootstrap.menu.Item
25343  * @extends Roo.bootstrap.Component
25344  * Bootstrap MenuItem class
25345  * @cfg {Boolean} submenu (true | false) default false
25346  * @cfg {String} html text of the item
25347  * @cfg {String} href the link
25348  * @cfg {Boolean} disable (true | false) default false
25349  * @cfg {Boolean} preventDefault (true | false) default true
25350  * @cfg {String} icon Font awesome icon
25351  * @cfg {String} pos Submenu align to (left | right) default right 
25352  * 
25353  * 
25354  * @constructor
25355  * Create a new Item
25356  * @param {Object} config The config object
25357  */
25358
25359
25360 Roo.bootstrap.menu.Item = function(config){
25361     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25362     this.addEvents({
25363         /**
25364          * @event mouseover
25365          * Fires when the mouse is hovering over this menu
25366          * @param {Roo.bootstrap.menu.Item} this
25367          * @param {Roo.EventObject} e
25368          */
25369         mouseover : true,
25370         /**
25371          * @event mouseout
25372          * Fires when the mouse exits this menu
25373          * @param {Roo.bootstrap.menu.Item} this
25374          * @param {Roo.EventObject} e
25375          */
25376         mouseout : true,
25377         // raw events
25378         /**
25379          * @event click
25380          * The raw click event for the entire grid.
25381          * @param {Roo.EventObject} e
25382          */
25383         click : true
25384     });
25385 };
25386
25387 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25388     
25389     submenu : false,
25390     href : '',
25391     html : '',
25392     preventDefault: true,
25393     disable : false,
25394     icon : false,
25395     pos : 'right',
25396     
25397     getAutoCreate : function()
25398     {
25399         var text = [
25400             {
25401                 tag : 'span',
25402                 cls : 'roo-menu-item-text',
25403                 html : this.html
25404             }
25405         ];
25406         
25407         if(this.icon){
25408             text.unshift({
25409                 tag : 'i',
25410                 cls : 'fa ' + this.icon
25411             })
25412         }
25413         
25414         var cfg = {
25415             tag : 'li',
25416             cn : [
25417                 {
25418                     tag : 'a',
25419                     href : this.href || '#',
25420                     cn : text
25421                 }
25422             ]
25423         };
25424         
25425         if(this.disable){
25426             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25427         }
25428         
25429         if(this.submenu){
25430             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25431             
25432             if(this.pos == 'left'){
25433                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25434             }
25435         }
25436         
25437         return cfg;
25438     },
25439     
25440     initEvents : function() 
25441     {
25442         this.el.on('mouseover', this.onMouseOver, this);
25443         this.el.on('mouseout', this.onMouseOut, this);
25444         
25445         this.el.select('a', true).first().on('click', this.onClick, this);
25446         
25447     },
25448     
25449     onClick : function(e)
25450     {
25451         if(this.preventDefault){
25452             e.preventDefault();
25453         }
25454         
25455         this.fireEvent("click", this, e);
25456     },
25457     
25458     onMouseOver : function(e)
25459     {
25460         if(this.submenu && this.pos == 'left'){
25461             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25462         }
25463         
25464         this.fireEvent("mouseover", this, e);
25465     },
25466     
25467     onMouseOut : function(e)
25468     {
25469         this.fireEvent("mouseout", this, e);
25470     }
25471 });
25472
25473  
25474
25475  /*
25476  * - LGPL
25477  *
25478  * menu separator
25479  * 
25480  */
25481 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25482
25483 /**
25484  * @class Roo.bootstrap.menu.Separator
25485  * @extends Roo.bootstrap.Component
25486  * Bootstrap Separator class
25487  * 
25488  * @constructor
25489  * Create a new Separator
25490  * @param {Object} config The config object
25491  */
25492
25493
25494 Roo.bootstrap.menu.Separator = function(config){
25495     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25496 };
25497
25498 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
25499     
25500     getAutoCreate : function(){
25501         var cfg = {
25502             tag : 'li',
25503             cls: 'divider'
25504         };
25505         
25506         return cfg;
25507     }
25508    
25509 });
25510
25511  
25512
25513  /*
25514  * - LGPL
25515  *
25516  * Tooltip
25517  * 
25518  */
25519
25520 /**
25521  * @class Roo.bootstrap.Tooltip
25522  * Bootstrap Tooltip class
25523  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25524  * to determine which dom element triggers the tooltip.
25525  * 
25526  * It needs to add support for additional attributes like tooltip-position
25527  * 
25528  * @constructor
25529  * Create a new Toolti
25530  * @param {Object} config The config object
25531  */
25532
25533 Roo.bootstrap.Tooltip = function(config){
25534     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25535     
25536     this.alignment = Roo.bootstrap.Tooltip.alignment;
25537     
25538     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25539         this.alignment = config.alignment;
25540     }
25541     
25542 };
25543
25544 Roo.apply(Roo.bootstrap.Tooltip, {
25545     /**
25546      * @function init initialize tooltip monitoring.
25547      * @static
25548      */
25549     currentEl : false,
25550     currentTip : false,
25551     currentRegion : false,
25552     
25553     //  init : delay?
25554     
25555     init : function()
25556     {
25557         Roo.get(document).on('mouseover', this.enter ,this);
25558         Roo.get(document).on('mouseout', this.leave, this);
25559          
25560         
25561         this.currentTip = new Roo.bootstrap.Tooltip();
25562     },
25563     
25564     enter : function(ev)
25565     {
25566         var dom = ev.getTarget();
25567         
25568         //Roo.log(['enter',dom]);
25569         var el = Roo.fly(dom);
25570         if (this.currentEl) {
25571             //Roo.log(dom);
25572             //Roo.log(this.currentEl);
25573             //Roo.log(this.currentEl.contains(dom));
25574             if (this.currentEl == el) {
25575                 return;
25576             }
25577             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25578                 return;
25579             }
25580
25581         }
25582         
25583         if (this.currentTip.el) {
25584             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25585         }    
25586         //Roo.log(ev);
25587         
25588         if(!el || el.dom == document){
25589             return;
25590         }
25591         
25592         var bindEl = el;
25593         
25594         // you can not look for children, as if el is the body.. then everythign is the child..
25595         if (!el.attr('tooltip')) { //
25596             if (!el.select("[tooltip]").elements.length) {
25597                 return;
25598             }
25599             // is the mouse over this child...?
25600             bindEl = el.select("[tooltip]").first();
25601             var xy = ev.getXY();
25602             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
25603                 //Roo.log("not in region.");
25604                 return;
25605             }
25606             //Roo.log("child element over..");
25607             
25608         }
25609         this.currentEl = bindEl;
25610         this.currentTip.bind(bindEl);
25611         this.currentRegion = Roo.lib.Region.getRegion(dom);
25612         this.currentTip.enter();
25613         
25614     },
25615     leave : function(ev)
25616     {
25617         var dom = ev.getTarget();
25618         //Roo.log(['leave',dom]);
25619         if (!this.currentEl) {
25620             return;
25621         }
25622         
25623         
25624         if (dom != this.currentEl.dom) {
25625             return;
25626         }
25627         var xy = ev.getXY();
25628         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
25629             return;
25630         }
25631         // only activate leave if mouse cursor is outside... bounding box..
25632         
25633         
25634         
25635         
25636         if (this.currentTip) {
25637             this.currentTip.leave();
25638         }
25639         //Roo.log('clear currentEl');
25640         this.currentEl = false;
25641         
25642         
25643     },
25644     alignment : {
25645         'left' : ['r-l', [-2,0], 'right'],
25646         'right' : ['l-r', [2,0], 'left'],
25647         'bottom' : ['t-b', [0,2], 'top'],
25648         'top' : [ 'b-t', [0,-2], 'bottom']
25649     }
25650     
25651 });
25652
25653
25654 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
25655     
25656     
25657     bindEl : false,
25658     
25659     delay : null, // can be { show : 300 , hide: 500}
25660     
25661     timeout : null,
25662     
25663     hoverState : null, //???
25664     
25665     placement : 'bottom', 
25666     
25667     alignment : false,
25668     
25669     getAutoCreate : function(){
25670     
25671         var cfg = {
25672            cls : 'tooltip',
25673            role : 'tooltip',
25674            cn : [
25675                 {
25676                     cls : 'tooltip-arrow'
25677                 },
25678                 {
25679                     cls : 'tooltip-inner'
25680                 }
25681            ]
25682         };
25683         
25684         return cfg;
25685     },
25686     bind : function(el)
25687     {
25688         this.bindEl = el;
25689     },
25690       
25691     
25692     enter : function () {
25693        
25694         if (this.timeout != null) {
25695             clearTimeout(this.timeout);
25696         }
25697         
25698         this.hoverState = 'in';
25699          //Roo.log("enter - show");
25700         if (!this.delay || !this.delay.show) {
25701             this.show();
25702             return;
25703         }
25704         var _t = this;
25705         this.timeout = setTimeout(function () {
25706             if (_t.hoverState == 'in') {
25707                 _t.show();
25708             }
25709         }, this.delay.show);
25710     },
25711     leave : function()
25712     {
25713         clearTimeout(this.timeout);
25714     
25715         this.hoverState = 'out';
25716          if (!this.delay || !this.delay.hide) {
25717             this.hide();
25718             return;
25719         }
25720        
25721         var _t = this;
25722         this.timeout = setTimeout(function () {
25723             //Roo.log("leave - timeout");
25724             
25725             if (_t.hoverState == 'out') {
25726                 _t.hide();
25727                 Roo.bootstrap.Tooltip.currentEl = false;
25728             }
25729         }, delay);
25730     },
25731     
25732     show : function (msg)
25733     {
25734         if (!this.el) {
25735             this.render(document.body);
25736         }
25737         // set content.
25738         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
25739         
25740         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
25741         
25742         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
25743         
25744         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
25745         
25746         var placement = typeof this.placement == 'function' ?
25747             this.placement.call(this, this.el, on_el) :
25748             this.placement;
25749             
25750         var autoToken = /\s?auto?\s?/i;
25751         var autoPlace = autoToken.test(placement);
25752         if (autoPlace) {
25753             placement = placement.replace(autoToken, '') || 'top';
25754         }
25755         
25756         //this.el.detach()
25757         //this.el.setXY([0,0]);
25758         this.el.show();
25759         //this.el.dom.style.display='block';
25760         
25761         //this.el.appendTo(on_el);
25762         
25763         var p = this.getPosition();
25764         var box = this.el.getBox();
25765         
25766         if (autoPlace) {
25767             // fixme..
25768         }
25769         
25770         var align = this.alignment[placement];
25771         
25772         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
25773         
25774         if(placement == 'top' || placement == 'bottom'){
25775             if(xy[0] < 0){
25776                 placement = 'right';
25777             }
25778             
25779             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
25780                 placement = 'left';
25781             }
25782             
25783             var scroll = Roo.select('body', true).first().getScroll();
25784             
25785             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
25786                 placement = 'top';
25787             }
25788             
25789         }
25790         
25791         this.el.alignTo(this.bindEl, align[0],align[1]);
25792         //var arrow = this.el.select('.arrow',true).first();
25793         //arrow.set(align[2], 
25794         
25795         this.el.addClass(placement);
25796         
25797         this.el.addClass('in fade');
25798         
25799         this.hoverState = null;
25800         
25801         if (this.el.hasClass('fade')) {
25802             // fade it?
25803         }
25804         
25805     },
25806     hide : function()
25807     {
25808          
25809         if (!this.el) {
25810             return;
25811         }
25812         //this.el.setXY([0,0]);
25813         this.el.removeClass('in');
25814         //this.el.hide();
25815         
25816     }
25817     
25818 });
25819  
25820
25821  /*
25822  * - LGPL
25823  *
25824  * Location Picker
25825  * 
25826  */
25827
25828 /**
25829  * @class Roo.bootstrap.LocationPicker
25830  * @extends Roo.bootstrap.Component
25831  * Bootstrap LocationPicker class
25832  * @cfg {Number} latitude Position when init default 0
25833  * @cfg {Number} longitude Position when init default 0
25834  * @cfg {Number} zoom default 15
25835  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
25836  * @cfg {Boolean} mapTypeControl default false
25837  * @cfg {Boolean} disableDoubleClickZoom default false
25838  * @cfg {Boolean} scrollwheel default true
25839  * @cfg {Boolean} streetViewControl default false
25840  * @cfg {Number} radius default 0
25841  * @cfg {String} locationName
25842  * @cfg {Boolean} draggable default true
25843  * @cfg {Boolean} enableAutocomplete default false
25844  * @cfg {Boolean} enableReverseGeocode default true
25845  * @cfg {String} markerTitle
25846  * 
25847  * @constructor
25848  * Create a new LocationPicker
25849  * @param {Object} config The config object
25850  */
25851
25852
25853 Roo.bootstrap.LocationPicker = function(config){
25854     
25855     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
25856     
25857     this.addEvents({
25858         /**
25859          * @event initial
25860          * Fires when the picker initialized.
25861          * @param {Roo.bootstrap.LocationPicker} this
25862          * @param {Google Location} location
25863          */
25864         initial : true,
25865         /**
25866          * @event positionchanged
25867          * Fires when the picker position changed.
25868          * @param {Roo.bootstrap.LocationPicker} this
25869          * @param {Google Location} location
25870          */
25871         positionchanged : true,
25872         /**
25873          * @event resize
25874          * Fires when the map resize.
25875          * @param {Roo.bootstrap.LocationPicker} this
25876          */
25877         resize : true,
25878         /**
25879          * @event show
25880          * Fires when the map show.
25881          * @param {Roo.bootstrap.LocationPicker} this
25882          */
25883         show : true,
25884         /**
25885          * @event hide
25886          * Fires when the map hide.
25887          * @param {Roo.bootstrap.LocationPicker} this
25888          */
25889         hide : true,
25890         /**
25891          * @event mapClick
25892          * Fires when click the map.
25893          * @param {Roo.bootstrap.LocationPicker} this
25894          * @param {Map event} e
25895          */
25896         mapClick : true,
25897         /**
25898          * @event mapRightClick
25899          * Fires when right click the map.
25900          * @param {Roo.bootstrap.LocationPicker} this
25901          * @param {Map event} e
25902          */
25903         mapRightClick : true,
25904         /**
25905          * @event markerClick
25906          * Fires when click the marker.
25907          * @param {Roo.bootstrap.LocationPicker} this
25908          * @param {Map event} e
25909          */
25910         markerClick : true,
25911         /**
25912          * @event markerRightClick
25913          * Fires when right click the marker.
25914          * @param {Roo.bootstrap.LocationPicker} this
25915          * @param {Map event} e
25916          */
25917         markerRightClick : true,
25918         /**
25919          * @event OverlayViewDraw
25920          * Fires when OverlayView Draw
25921          * @param {Roo.bootstrap.LocationPicker} this
25922          */
25923         OverlayViewDraw : true,
25924         /**
25925          * @event OverlayViewOnAdd
25926          * Fires when OverlayView Draw
25927          * @param {Roo.bootstrap.LocationPicker} this
25928          */
25929         OverlayViewOnAdd : true,
25930         /**
25931          * @event OverlayViewOnRemove
25932          * Fires when OverlayView Draw
25933          * @param {Roo.bootstrap.LocationPicker} this
25934          */
25935         OverlayViewOnRemove : true,
25936         /**
25937          * @event OverlayViewShow
25938          * Fires when OverlayView Draw
25939          * @param {Roo.bootstrap.LocationPicker} this
25940          * @param {Pixel} cpx
25941          */
25942         OverlayViewShow : true,
25943         /**
25944          * @event OverlayViewHide
25945          * Fires when OverlayView Draw
25946          * @param {Roo.bootstrap.LocationPicker} this
25947          */
25948         OverlayViewHide : true,
25949         /**
25950          * @event loadexception
25951          * Fires when load google lib failed.
25952          * @param {Roo.bootstrap.LocationPicker} this
25953          */
25954         loadexception : true
25955     });
25956         
25957 };
25958
25959 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
25960     
25961     gMapContext: false,
25962     
25963     latitude: 0,
25964     longitude: 0,
25965     zoom: 15,
25966     mapTypeId: false,
25967     mapTypeControl: false,
25968     disableDoubleClickZoom: false,
25969     scrollwheel: true,
25970     streetViewControl: false,
25971     radius: 0,
25972     locationName: '',
25973     draggable: true,
25974     enableAutocomplete: false,
25975     enableReverseGeocode: true,
25976     markerTitle: '',
25977     
25978     getAutoCreate: function()
25979     {
25980
25981         var cfg = {
25982             tag: 'div',
25983             cls: 'roo-location-picker'
25984         };
25985         
25986         return cfg
25987     },
25988     
25989     initEvents: function(ct, position)
25990     {       
25991         if(!this.el.getWidth() || this.isApplied()){
25992             return;
25993         }
25994         
25995         this.el.setVisibilityMode(Roo.Element.DISPLAY);
25996         
25997         this.initial();
25998     },
25999     
26000     initial: function()
26001     {
26002         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26003             this.fireEvent('loadexception', this);
26004             return;
26005         }
26006         
26007         if(!this.mapTypeId){
26008             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26009         }
26010         
26011         this.gMapContext = this.GMapContext();
26012         
26013         this.initOverlayView();
26014         
26015         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26016         
26017         var _this = this;
26018                 
26019         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26020             _this.setPosition(_this.gMapContext.marker.position);
26021         });
26022         
26023         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26024             _this.fireEvent('mapClick', this, event);
26025             
26026         });
26027
26028         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26029             _this.fireEvent('mapRightClick', this, event);
26030             
26031         });
26032         
26033         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26034             _this.fireEvent('markerClick', this, event);
26035             
26036         });
26037
26038         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26039             _this.fireEvent('markerRightClick', this, event);
26040             
26041         });
26042         
26043         this.setPosition(this.gMapContext.location);
26044         
26045         this.fireEvent('initial', this, this.gMapContext.location);
26046     },
26047     
26048     initOverlayView: function()
26049     {
26050         var _this = this;
26051         
26052         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26053             
26054             draw: function()
26055             {
26056                 _this.fireEvent('OverlayViewDraw', _this);
26057             },
26058             
26059             onAdd: function()
26060             {
26061                 _this.fireEvent('OverlayViewOnAdd', _this);
26062             },
26063             
26064             onRemove: function()
26065             {
26066                 _this.fireEvent('OverlayViewOnRemove', _this);
26067             },
26068             
26069             show: function(cpx)
26070             {
26071                 _this.fireEvent('OverlayViewShow', _this, cpx);
26072             },
26073             
26074             hide: function()
26075             {
26076                 _this.fireEvent('OverlayViewHide', _this);
26077             }
26078             
26079         });
26080     },
26081     
26082     fromLatLngToContainerPixel: function(event)
26083     {
26084         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26085     },
26086     
26087     isApplied: function() 
26088     {
26089         return this.getGmapContext() == false ? false : true;
26090     },
26091     
26092     getGmapContext: function() 
26093     {
26094         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26095     },
26096     
26097     GMapContext: function() 
26098     {
26099         var position = new google.maps.LatLng(this.latitude, this.longitude);
26100         
26101         var _map = new google.maps.Map(this.el.dom, {
26102             center: position,
26103             zoom: this.zoom,
26104             mapTypeId: this.mapTypeId,
26105             mapTypeControl: this.mapTypeControl,
26106             disableDoubleClickZoom: this.disableDoubleClickZoom,
26107             scrollwheel: this.scrollwheel,
26108             streetViewControl: this.streetViewControl,
26109             locationName: this.locationName,
26110             draggable: this.draggable,
26111             enableAutocomplete: this.enableAutocomplete,
26112             enableReverseGeocode: this.enableReverseGeocode
26113         });
26114         
26115         var _marker = new google.maps.Marker({
26116             position: position,
26117             map: _map,
26118             title: this.markerTitle,
26119             draggable: this.draggable
26120         });
26121         
26122         return {
26123             map: _map,
26124             marker: _marker,
26125             circle: null,
26126             location: position,
26127             radius: this.radius,
26128             locationName: this.locationName,
26129             addressComponents: {
26130                 formatted_address: null,
26131                 addressLine1: null,
26132                 addressLine2: null,
26133                 streetName: null,
26134                 streetNumber: null,
26135                 city: null,
26136                 district: null,
26137                 state: null,
26138                 stateOrProvince: null
26139             },
26140             settings: this,
26141             domContainer: this.el.dom,
26142             geodecoder: new google.maps.Geocoder()
26143         };
26144     },
26145     
26146     drawCircle: function(center, radius, options) 
26147     {
26148         if (this.gMapContext.circle != null) {
26149             this.gMapContext.circle.setMap(null);
26150         }
26151         if (radius > 0) {
26152             radius *= 1;
26153             options = Roo.apply({}, options, {
26154                 strokeColor: "#0000FF",
26155                 strokeOpacity: .35,
26156                 strokeWeight: 2,
26157                 fillColor: "#0000FF",
26158                 fillOpacity: .2
26159             });
26160             
26161             options.map = this.gMapContext.map;
26162             options.radius = radius;
26163             options.center = center;
26164             this.gMapContext.circle = new google.maps.Circle(options);
26165             return this.gMapContext.circle;
26166         }
26167         
26168         return null;
26169     },
26170     
26171     setPosition: function(location) 
26172     {
26173         this.gMapContext.location = location;
26174         this.gMapContext.marker.setPosition(location);
26175         this.gMapContext.map.panTo(location);
26176         this.drawCircle(location, this.gMapContext.radius, {});
26177         
26178         var _this = this;
26179         
26180         if (this.gMapContext.settings.enableReverseGeocode) {
26181             this.gMapContext.geodecoder.geocode({
26182                 latLng: this.gMapContext.location
26183             }, function(results, status) {
26184                 
26185                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26186                     _this.gMapContext.locationName = results[0].formatted_address;
26187                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26188                     
26189                     _this.fireEvent('positionchanged', this, location);
26190                 }
26191             });
26192             
26193             return;
26194         }
26195         
26196         this.fireEvent('positionchanged', this, location);
26197     },
26198     
26199     resize: function()
26200     {
26201         google.maps.event.trigger(this.gMapContext.map, "resize");
26202         
26203         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26204         
26205         this.fireEvent('resize', this);
26206     },
26207     
26208     setPositionByLatLng: function(latitude, longitude)
26209     {
26210         this.setPosition(new google.maps.LatLng(latitude, longitude));
26211     },
26212     
26213     getCurrentPosition: function() 
26214     {
26215         return {
26216             latitude: this.gMapContext.location.lat(),
26217             longitude: this.gMapContext.location.lng()
26218         };
26219     },
26220     
26221     getAddressName: function() 
26222     {
26223         return this.gMapContext.locationName;
26224     },
26225     
26226     getAddressComponents: function() 
26227     {
26228         return this.gMapContext.addressComponents;
26229     },
26230     
26231     address_component_from_google_geocode: function(address_components) 
26232     {
26233         var result = {};
26234         
26235         for (var i = 0; i < address_components.length; i++) {
26236             var component = address_components[i];
26237             if (component.types.indexOf("postal_code") >= 0) {
26238                 result.postalCode = component.short_name;
26239             } else if (component.types.indexOf("street_number") >= 0) {
26240                 result.streetNumber = component.short_name;
26241             } else if (component.types.indexOf("route") >= 0) {
26242                 result.streetName = component.short_name;
26243             } else if (component.types.indexOf("neighborhood") >= 0) {
26244                 result.city = component.short_name;
26245             } else if (component.types.indexOf("locality") >= 0) {
26246                 result.city = component.short_name;
26247             } else if (component.types.indexOf("sublocality") >= 0) {
26248                 result.district = component.short_name;
26249             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26250                 result.stateOrProvince = component.short_name;
26251             } else if (component.types.indexOf("country") >= 0) {
26252                 result.country = component.short_name;
26253             }
26254         }
26255         
26256         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26257         result.addressLine2 = "";
26258         return result;
26259     },
26260     
26261     setZoomLevel: function(zoom)
26262     {
26263         this.gMapContext.map.setZoom(zoom);
26264     },
26265     
26266     show: function()
26267     {
26268         if(!this.el){
26269             return;
26270         }
26271         
26272         this.el.show();
26273         
26274         this.resize();
26275         
26276         this.fireEvent('show', this);
26277     },
26278     
26279     hide: function()
26280     {
26281         if(!this.el){
26282             return;
26283         }
26284         
26285         this.el.hide();
26286         
26287         this.fireEvent('hide', this);
26288     }
26289     
26290 });
26291
26292 Roo.apply(Roo.bootstrap.LocationPicker, {
26293     
26294     OverlayView : function(map, options)
26295     {
26296         options = options || {};
26297         
26298         this.setMap(map);
26299     }
26300     
26301     
26302 });/*
26303  * - LGPL
26304  *
26305  * Alert
26306  * 
26307  */
26308
26309 /**
26310  * @class Roo.bootstrap.Alert
26311  * @extends Roo.bootstrap.Component
26312  * Bootstrap Alert class
26313  * @cfg {String} title The title of alert
26314  * @cfg {String} html The content of alert
26315  * @cfg {String} weight (  success | info | warning | danger )
26316  * @cfg {String} faicon font-awesomeicon
26317  * 
26318  * @constructor
26319  * Create a new alert
26320  * @param {Object} config The config object
26321  */
26322
26323
26324 Roo.bootstrap.Alert = function(config){
26325     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26326     
26327 };
26328
26329 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26330     
26331     title: '',
26332     html: '',
26333     weight: false,
26334     faicon: false,
26335     
26336     getAutoCreate : function()
26337     {
26338         
26339         var cfg = {
26340             tag : 'div',
26341             cls : 'alert',
26342             cn : [
26343                 {
26344                     tag : 'i',
26345                     cls : 'roo-alert-icon'
26346                     
26347                 },
26348                 {
26349                     tag : 'b',
26350                     cls : 'roo-alert-title',
26351                     html : this.title
26352                 },
26353                 {
26354                     tag : 'span',
26355                     cls : 'roo-alert-text',
26356                     html : this.html
26357                 }
26358             ]
26359         };
26360         
26361         if(this.faicon){
26362             cfg.cn[0].cls += ' fa ' + this.faicon;
26363         }
26364         
26365         if(this.weight){
26366             cfg.cls += ' alert-' + this.weight;
26367         }
26368         
26369         return cfg;
26370     },
26371     
26372     initEvents: function() 
26373     {
26374         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26375     },
26376     
26377     setTitle : function(str)
26378     {
26379         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26380     },
26381     
26382     setText : function(str)
26383     {
26384         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26385     },
26386     
26387     setWeight : function(weight)
26388     {
26389         if(this.weight){
26390             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26391         }
26392         
26393         this.weight = weight;
26394         
26395         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26396     },
26397     
26398     setIcon : function(icon)
26399     {
26400         if(this.faicon){
26401             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26402         }
26403         
26404         this.faicon = icon;
26405         
26406         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26407     },
26408     
26409     hide: function() 
26410     {
26411         this.el.hide();   
26412     },
26413     
26414     show: function() 
26415     {  
26416         this.el.show();   
26417     }
26418     
26419 });
26420
26421  
26422 /*
26423 * Licence: LGPL
26424 */
26425
26426 /**
26427  * @class Roo.bootstrap.UploadCropbox
26428  * @extends Roo.bootstrap.Component
26429  * Bootstrap UploadCropbox class
26430  * @cfg {String} emptyText show when image has been loaded
26431  * @cfg {String} rotateNotify show when image too small to rotate
26432  * @cfg {Number} errorTimeout default 3000
26433  * @cfg {Number} minWidth default 300
26434  * @cfg {Number} minHeight default 300
26435  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26436  * @cfg {Boolean} isDocument (true|false) default false
26437  * @cfg {String} url action url
26438  * @cfg {String} paramName default 'imageUpload'
26439  * @cfg {String} method default POST
26440  * @cfg {Boolean} loadMask (true|false) default true
26441  * @cfg {Boolean} loadingText default 'Loading...'
26442  * 
26443  * @constructor
26444  * Create a new UploadCropbox
26445  * @param {Object} config The config object
26446  */
26447
26448 Roo.bootstrap.UploadCropbox = function(config){
26449     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26450     
26451     this.addEvents({
26452         /**
26453          * @event beforeselectfile
26454          * Fire before select file
26455          * @param {Roo.bootstrap.UploadCropbox} this
26456          */
26457         "beforeselectfile" : true,
26458         /**
26459          * @event initial
26460          * Fire after initEvent
26461          * @param {Roo.bootstrap.UploadCropbox} this
26462          */
26463         "initial" : true,
26464         /**
26465          * @event crop
26466          * Fire after initEvent
26467          * @param {Roo.bootstrap.UploadCropbox} this
26468          * @param {String} data
26469          */
26470         "crop" : true,
26471         /**
26472          * @event prepare
26473          * Fire when preparing the file data
26474          * @param {Roo.bootstrap.UploadCropbox} this
26475          * @param {Object} file
26476          */
26477         "prepare" : true,
26478         /**
26479          * @event exception
26480          * Fire when get exception
26481          * @param {Roo.bootstrap.UploadCropbox} this
26482          * @param {XMLHttpRequest} xhr
26483          */
26484         "exception" : true,
26485         /**
26486          * @event beforeloadcanvas
26487          * Fire before load the canvas
26488          * @param {Roo.bootstrap.UploadCropbox} this
26489          * @param {String} src
26490          */
26491         "beforeloadcanvas" : true,
26492         /**
26493          * @event trash
26494          * Fire when trash image
26495          * @param {Roo.bootstrap.UploadCropbox} this
26496          */
26497         "trash" : true,
26498         /**
26499          * @event download
26500          * Fire when download the image
26501          * @param {Roo.bootstrap.UploadCropbox} this
26502          */
26503         "download" : true,
26504         /**
26505          * @event footerbuttonclick
26506          * Fire when footerbuttonclick
26507          * @param {Roo.bootstrap.UploadCropbox} this
26508          * @param {String} type
26509          */
26510         "footerbuttonclick" : true,
26511         /**
26512          * @event resize
26513          * Fire when resize
26514          * @param {Roo.bootstrap.UploadCropbox} this
26515          */
26516         "resize" : true,
26517         /**
26518          * @event rotate
26519          * Fire when rotate the image
26520          * @param {Roo.bootstrap.UploadCropbox} this
26521          * @param {String} pos
26522          */
26523         "rotate" : true,
26524         /**
26525          * @event inspect
26526          * Fire when inspect the file
26527          * @param {Roo.bootstrap.UploadCropbox} this
26528          * @param {Object} file
26529          */
26530         "inspect" : true,
26531         /**
26532          * @event upload
26533          * Fire when xhr upload the file
26534          * @param {Roo.bootstrap.UploadCropbox} this
26535          * @param {Object} data
26536          */
26537         "upload" : true,
26538         /**
26539          * @event arrange
26540          * Fire when arrange the file data
26541          * @param {Roo.bootstrap.UploadCropbox} this
26542          * @param {Object} formData
26543          */
26544         "arrange" : true
26545     });
26546     
26547     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26548 };
26549
26550 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
26551     
26552     emptyText : 'Click to upload image',
26553     rotateNotify : 'Image is too small to rotate',
26554     errorTimeout : 3000,
26555     scale : 0,
26556     baseScale : 1,
26557     rotate : 0,
26558     dragable : false,
26559     pinching : false,
26560     mouseX : 0,
26561     mouseY : 0,
26562     cropData : false,
26563     minWidth : 300,
26564     minHeight : 300,
26565     file : false,
26566     exif : {},
26567     baseRotate : 1,
26568     cropType : 'image/jpeg',
26569     buttons : false,
26570     canvasLoaded : false,
26571     isDocument : false,
26572     method : 'POST',
26573     paramName : 'imageUpload',
26574     loadMask : true,
26575     loadingText : 'Loading...',
26576     maskEl : false,
26577     
26578     getAutoCreate : function()
26579     {
26580         var cfg = {
26581             tag : 'div',
26582             cls : 'roo-upload-cropbox',
26583             cn : [
26584                 {
26585                     tag : 'input',
26586                     cls : 'roo-upload-cropbox-selector',
26587                     type : 'file'
26588                 },
26589                 {
26590                     tag : 'div',
26591                     cls : 'roo-upload-cropbox-body',
26592                     style : 'cursor:pointer',
26593                     cn : [
26594                         {
26595                             tag : 'div',
26596                             cls : 'roo-upload-cropbox-preview'
26597                         },
26598                         {
26599                             tag : 'div',
26600                             cls : 'roo-upload-cropbox-thumb'
26601                         },
26602                         {
26603                             tag : 'div',
26604                             cls : 'roo-upload-cropbox-empty-notify',
26605                             html : this.emptyText
26606                         },
26607                         {
26608                             tag : 'div',
26609                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
26610                             html : this.rotateNotify
26611                         }
26612                     ]
26613                 },
26614                 {
26615                     tag : 'div',
26616                     cls : 'roo-upload-cropbox-footer',
26617                     cn : {
26618                         tag : 'div',
26619                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
26620                         cn : []
26621                     }
26622                 }
26623             ]
26624         };
26625         
26626         return cfg;
26627     },
26628     
26629     onRender : function(ct, position)
26630     {
26631         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
26632         
26633         if (this.buttons.length) {
26634             
26635             Roo.each(this.buttons, function(bb) {
26636                 
26637                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
26638                 
26639                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
26640                 
26641             }, this);
26642         }
26643         
26644         if(this.loadMask){
26645             this.maskEl = this.el;
26646         }
26647     },
26648     
26649     initEvents : function()
26650     {
26651         this.urlAPI = (window.createObjectURL && window) || 
26652                                 (window.URL && URL.revokeObjectURL && URL) || 
26653                                 (window.webkitURL && webkitURL);
26654                         
26655         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
26656         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26657         
26658         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
26659         this.selectorEl.hide();
26660         
26661         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
26662         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26663         
26664         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
26665         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26666         this.thumbEl.hide();
26667         
26668         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
26669         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26670         
26671         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
26672         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26673         this.errorEl.hide();
26674         
26675         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
26676         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26677         this.footerEl.hide();
26678         
26679         this.setThumbBoxSize();
26680         
26681         this.bind();
26682         
26683         this.resize();
26684         
26685         this.fireEvent('initial', this);
26686     },
26687
26688     bind : function()
26689     {
26690         var _this = this;
26691         
26692         window.addEventListener("resize", function() { _this.resize(); } );
26693         
26694         this.bodyEl.on('click', this.beforeSelectFile, this);
26695         
26696         if(Roo.isTouch){
26697             this.bodyEl.on('touchstart', this.onTouchStart, this);
26698             this.bodyEl.on('touchmove', this.onTouchMove, this);
26699             this.bodyEl.on('touchend', this.onTouchEnd, this);
26700         }
26701         
26702         if(!Roo.isTouch){
26703             this.bodyEl.on('mousedown', this.onMouseDown, this);
26704             this.bodyEl.on('mousemove', this.onMouseMove, this);
26705             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
26706             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
26707             Roo.get(document).on('mouseup', this.onMouseUp, this);
26708         }
26709         
26710         this.selectorEl.on('change', this.onFileSelected, this);
26711     },
26712     
26713     reset : function()
26714     {    
26715         this.scale = 0;
26716         this.baseScale = 1;
26717         this.rotate = 0;
26718         this.baseRotate = 1;
26719         this.dragable = false;
26720         this.pinching = false;
26721         this.mouseX = 0;
26722         this.mouseY = 0;
26723         this.cropData = false;
26724         this.notifyEl.dom.innerHTML = this.emptyText;
26725         
26726         this.selectorEl.dom.value = '';
26727         
26728     },
26729     
26730     resize : function()
26731     {
26732         if(this.fireEvent('resize', this) != false){
26733             this.setThumbBoxPosition();
26734             this.setCanvasPosition();
26735         }
26736     },
26737     
26738     onFooterButtonClick : function(e, el, o, type)
26739     {
26740         switch (type) {
26741             case 'rotate-left' :
26742                 this.onRotateLeft(e);
26743                 break;
26744             case 'rotate-right' :
26745                 this.onRotateRight(e);
26746                 break;
26747             case 'picture' :
26748                 this.beforeSelectFile(e);
26749                 break;
26750             case 'trash' :
26751                 this.trash(e);
26752                 break;
26753             case 'crop' :
26754                 this.crop(e);
26755                 break;
26756             case 'download' :
26757                 this.download(e);
26758                 break;
26759             default :
26760                 break;
26761         }
26762         
26763         this.fireEvent('footerbuttonclick', this, type);
26764     },
26765     
26766     beforeSelectFile : function(e)
26767     {
26768         e.preventDefault();
26769         
26770         if(this.fireEvent('beforeselectfile', this) != false){
26771             this.selectorEl.dom.click();
26772         }
26773     },
26774     
26775     onFileSelected : function(e)
26776     {
26777         e.preventDefault();
26778         
26779         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26780             return;
26781         }
26782         
26783         var file = this.selectorEl.dom.files[0];
26784         
26785         if(this.fireEvent('inspect', this, file) != false){
26786             this.prepare(file);
26787         }
26788         
26789     },
26790     
26791     trash : function(e)
26792     {
26793         this.fireEvent('trash', this);
26794     },
26795     
26796     download : function(e)
26797     {
26798         this.fireEvent('download', this);
26799     },
26800     
26801     loadCanvas : function(src)
26802     {   
26803         if(this.fireEvent('beforeloadcanvas', this, src) != false){
26804             
26805             this.reset();
26806             
26807             this.imageEl = document.createElement('img');
26808             
26809             var _this = this;
26810             
26811             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
26812             
26813             this.imageEl.src = src;
26814         }
26815     },
26816     
26817     onLoadCanvas : function()
26818     {   
26819         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
26820         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
26821         
26822         this.bodyEl.un('click', this.beforeSelectFile, this);
26823         
26824         this.notifyEl.hide();
26825         this.thumbEl.show();
26826         this.footerEl.show();
26827         
26828         this.baseRotateLevel();
26829         
26830         if(this.isDocument){
26831             this.setThumbBoxSize();
26832         }
26833         
26834         this.setThumbBoxPosition();
26835         
26836         this.baseScaleLevel();
26837         
26838         this.draw();
26839         
26840         this.resize();
26841         
26842         this.canvasLoaded = true;
26843         
26844         if(this.loadMask){
26845             this.maskEl.unmask();
26846         }
26847         
26848     },
26849     
26850     setCanvasPosition : function()
26851     {   
26852         if(!this.canvasEl){
26853             return;
26854         }
26855         
26856         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
26857         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
26858         
26859         this.previewEl.setLeft(pw);
26860         this.previewEl.setTop(ph);
26861         
26862     },
26863     
26864     onMouseDown : function(e)
26865     {   
26866         e.stopEvent();
26867         
26868         this.dragable = true;
26869         this.pinching = false;
26870         
26871         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
26872             this.dragable = false;
26873             return;
26874         }
26875         
26876         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26877         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26878         
26879     },
26880     
26881     onMouseMove : function(e)
26882     {   
26883         e.stopEvent();
26884         
26885         if(!this.canvasLoaded){
26886             return;
26887         }
26888         
26889         if (!this.dragable){
26890             return;
26891         }
26892         
26893         var minX = Math.ceil(this.thumbEl.getLeft(true));
26894         var minY = Math.ceil(this.thumbEl.getTop(true));
26895         
26896         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
26897         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
26898         
26899         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26900         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26901         
26902         x = x - this.mouseX;
26903         y = y - this.mouseY;
26904         
26905         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
26906         var bgY = Math.ceil(y + this.previewEl.getTop(true));
26907         
26908         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
26909         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
26910         
26911         this.previewEl.setLeft(bgX);
26912         this.previewEl.setTop(bgY);
26913         
26914         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26915         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26916     },
26917     
26918     onMouseUp : function(e)
26919     {   
26920         e.stopEvent();
26921         
26922         this.dragable = false;
26923     },
26924     
26925     onMouseWheel : function(e)
26926     {   
26927         e.stopEvent();
26928         
26929         this.startScale = this.scale;
26930         
26931         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
26932         
26933         if(!this.zoomable()){
26934             this.scale = this.startScale;
26935             return;
26936         }
26937         
26938         this.draw();
26939         
26940         return;
26941     },
26942     
26943     zoomable : function()
26944     {
26945         var minScale = this.thumbEl.getWidth() / this.minWidth;
26946         
26947         if(this.minWidth < this.minHeight){
26948             minScale = this.thumbEl.getHeight() / this.minHeight;
26949         }
26950         
26951         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
26952         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
26953         
26954         if(
26955                 this.isDocument &&
26956                 (this.rotate == 0 || this.rotate == 180) && 
26957                 (
26958                     width > this.imageEl.OriginWidth || 
26959                     height > this.imageEl.OriginHeight ||
26960                     (width < this.minWidth && height < this.minHeight)
26961                 )
26962         ){
26963             return false;
26964         }
26965         
26966         if(
26967                 this.isDocument &&
26968                 (this.rotate == 90 || this.rotate == 270) && 
26969                 (
26970                     width > this.imageEl.OriginWidth || 
26971                     height > this.imageEl.OriginHeight ||
26972                     (width < this.minHeight && height < this.minWidth)
26973                 )
26974         ){
26975             return false;
26976         }
26977         
26978         if(
26979                 !this.isDocument &&
26980                 (this.rotate == 0 || this.rotate == 180) && 
26981                 (
26982                     width < this.minWidth || 
26983                     width > this.imageEl.OriginWidth || 
26984                     height < this.minHeight || 
26985                     height > this.imageEl.OriginHeight
26986                 )
26987         ){
26988             return false;
26989         }
26990         
26991         if(
26992                 !this.isDocument &&
26993                 (this.rotate == 90 || this.rotate == 270) && 
26994                 (
26995                     width < this.minHeight || 
26996                     width > this.imageEl.OriginWidth || 
26997                     height < this.minWidth || 
26998                     height > this.imageEl.OriginHeight
26999                 )
27000         ){
27001             return false;
27002         }
27003         
27004         return true;
27005         
27006     },
27007     
27008     onRotateLeft : function(e)
27009     {   
27010         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27011             
27012             var minScale = this.thumbEl.getWidth() / this.minWidth;
27013             
27014             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27015             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27016             
27017             this.startScale = this.scale;
27018             
27019             while (this.getScaleLevel() < minScale){
27020             
27021                 this.scale = this.scale + 1;
27022                 
27023                 if(!this.zoomable()){
27024                     break;
27025                 }
27026                 
27027                 if(
27028                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27029                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27030                 ){
27031                     continue;
27032                 }
27033                 
27034                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27035
27036                 this.draw();
27037                 
27038                 return;
27039             }
27040             
27041             this.scale = this.startScale;
27042             
27043             this.onRotateFail();
27044             
27045             return false;
27046         }
27047         
27048         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27049
27050         if(this.isDocument){
27051             this.setThumbBoxSize();
27052             this.setThumbBoxPosition();
27053             this.setCanvasPosition();
27054         }
27055         
27056         this.draw();
27057         
27058         this.fireEvent('rotate', this, 'left');
27059         
27060     },
27061     
27062     onRotateRight : function(e)
27063     {
27064         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27065             
27066             var minScale = this.thumbEl.getWidth() / this.minWidth;
27067         
27068             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27069             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27070             
27071             this.startScale = this.scale;
27072             
27073             while (this.getScaleLevel() < minScale){
27074             
27075                 this.scale = this.scale + 1;
27076                 
27077                 if(!this.zoomable()){
27078                     break;
27079                 }
27080                 
27081                 if(
27082                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27083                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27084                 ){
27085                     continue;
27086                 }
27087                 
27088                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27089
27090                 this.draw();
27091                 
27092                 return;
27093             }
27094             
27095             this.scale = this.startScale;
27096             
27097             this.onRotateFail();
27098             
27099             return false;
27100         }
27101         
27102         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27103
27104         if(this.isDocument){
27105             this.setThumbBoxSize();
27106             this.setThumbBoxPosition();
27107             this.setCanvasPosition();
27108         }
27109         
27110         this.draw();
27111         
27112         this.fireEvent('rotate', this, 'right');
27113     },
27114     
27115     onRotateFail : function()
27116     {
27117         this.errorEl.show(true);
27118         
27119         var _this = this;
27120         
27121         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27122     },
27123     
27124     draw : function()
27125     {
27126         this.previewEl.dom.innerHTML = '';
27127         
27128         var canvasEl = document.createElement("canvas");
27129         
27130         var contextEl = canvasEl.getContext("2d");
27131         
27132         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27133         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27134         var center = this.imageEl.OriginWidth / 2;
27135         
27136         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27137             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27138             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27139             center = this.imageEl.OriginHeight / 2;
27140         }
27141         
27142         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27143         
27144         contextEl.translate(center, center);
27145         contextEl.rotate(this.rotate * Math.PI / 180);
27146
27147         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27148         
27149         this.canvasEl = document.createElement("canvas");
27150         
27151         this.contextEl = this.canvasEl.getContext("2d");
27152         
27153         switch (this.rotate) {
27154             case 0 :
27155                 
27156                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27157                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27158                 
27159                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27160                 
27161                 break;
27162             case 90 : 
27163                 
27164                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27165                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27166                 
27167                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27168                     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);
27169                     break;
27170                 }
27171                 
27172                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27173                 
27174                 break;
27175             case 180 :
27176                 
27177                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27178                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27179                 
27180                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27181                     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);
27182                     break;
27183                 }
27184                 
27185                 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);
27186                 
27187                 break;
27188             case 270 :
27189                 
27190                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27191                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27192         
27193                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27194                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27195                     break;
27196                 }
27197                 
27198                 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);
27199                 
27200                 break;
27201             default : 
27202                 break;
27203         }
27204         
27205         this.previewEl.appendChild(this.canvasEl);
27206         
27207         this.setCanvasPosition();
27208     },
27209     
27210     crop : function()
27211     {
27212         if(!this.canvasLoaded){
27213             return;
27214         }
27215         
27216         var imageCanvas = document.createElement("canvas");
27217         
27218         var imageContext = imageCanvas.getContext("2d");
27219         
27220         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27221         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27222         
27223         var center = imageCanvas.width / 2;
27224         
27225         imageContext.translate(center, center);
27226         
27227         imageContext.rotate(this.rotate * Math.PI / 180);
27228         
27229         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27230         
27231         var canvas = document.createElement("canvas");
27232         
27233         var context = canvas.getContext("2d");
27234                 
27235         canvas.width = this.minWidth;
27236         canvas.height = this.minHeight;
27237
27238         switch (this.rotate) {
27239             case 0 :
27240                 
27241                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27242                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27243                 
27244                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27245                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27246                 
27247                 var targetWidth = this.minWidth - 2 * x;
27248                 var targetHeight = this.minHeight - 2 * y;
27249                 
27250                 var scale = 1;
27251                 
27252                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27253                     scale = targetWidth / width;
27254                 }
27255                 
27256                 if(x > 0 && y == 0){
27257                     scale = targetHeight / height;
27258                 }
27259                 
27260                 if(x > 0 && y > 0){
27261                     scale = targetWidth / width;
27262                     
27263                     if(width < height){
27264                         scale = targetHeight / height;
27265                     }
27266                 }
27267                 
27268                 context.scale(scale, scale);
27269                 
27270                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27271                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27272
27273                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27274                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27275
27276                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27277                 
27278                 break;
27279             case 90 : 
27280                 
27281                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27282                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27283                 
27284                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27285                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27286                 
27287                 var targetWidth = this.minWidth - 2 * x;
27288                 var targetHeight = this.minHeight - 2 * y;
27289                 
27290                 var scale = 1;
27291                 
27292                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27293                     scale = targetWidth / width;
27294                 }
27295                 
27296                 if(x > 0 && y == 0){
27297                     scale = targetHeight / height;
27298                 }
27299                 
27300                 if(x > 0 && y > 0){
27301                     scale = targetWidth / width;
27302                     
27303                     if(width < height){
27304                         scale = targetHeight / height;
27305                     }
27306                 }
27307                 
27308                 context.scale(scale, scale);
27309                 
27310                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27311                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27312
27313                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27314                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27315                 
27316                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27317                 
27318                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27319                 
27320                 break;
27321             case 180 :
27322                 
27323                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27324                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27325                 
27326                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27327                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27328                 
27329                 var targetWidth = this.minWidth - 2 * x;
27330                 var targetHeight = this.minHeight - 2 * y;
27331                 
27332                 var scale = 1;
27333                 
27334                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27335                     scale = targetWidth / width;
27336                 }
27337                 
27338                 if(x > 0 && y == 0){
27339                     scale = targetHeight / height;
27340                 }
27341                 
27342                 if(x > 0 && y > 0){
27343                     scale = targetWidth / width;
27344                     
27345                     if(width < height){
27346                         scale = targetHeight / height;
27347                     }
27348                 }
27349                 
27350                 context.scale(scale, scale);
27351                 
27352                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27353                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27354
27355                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27356                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27357
27358                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27359                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27360                 
27361                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27362                 
27363                 break;
27364             case 270 :
27365                 
27366                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27367                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27368                 
27369                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27370                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27371                 
27372                 var targetWidth = this.minWidth - 2 * x;
27373                 var targetHeight = this.minHeight - 2 * y;
27374                 
27375                 var scale = 1;
27376                 
27377                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27378                     scale = targetWidth / width;
27379                 }
27380                 
27381                 if(x > 0 && y == 0){
27382                     scale = targetHeight / height;
27383                 }
27384                 
27385                 if(x > 0 && y > 0){
27386                     scale = targetWidth / width;
27387                     
27388                     if(width < height){
27389                         scale = targetHeight / height;
27390                     }
27391                 }
27392                 
27393                 context.scale(scale, scale);
27394                 
27395                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27396                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27397
27398                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27399                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27400                 
27401                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27402                 
27403                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27404                 
27405                 break;
27406             default : 
27407                 break;
27408         }
27409         
27410         this.cropData = canvas.toDataURL(this.cropType);
27411         
27412         if(this.fireEvent('crop', this, this.cropData) !== false){
27413             this.process(this.file, this.cropData);
27414         }
27415         
27416         return;
27417         
27418     },
27419     
27420     setThumbBoxSize : function()
27421     {
27422         var width, height;
27423         
27424         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27425             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27426             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27427             
27428             this.minWidth = width;
27429             this.minHeight = height;
27430             
27431             if(this.rotate == 90 || this.rotate == 270){
27432                 this.minWidth = height;
27433                 this.minHeight = width;
27434             }
27435         }
27436         
27437         height = 300;
27438         width = Math.ceil(this.minWidth * height / this.minHeight);
27439         
27440         if(this.minWidth > this.minHeight){
27441             width = 300;
27442             height = Math.ceil(this.minHeight * width / this.minWidth);
27443         }
27444         
27445         this.thumbEl.setStyle({
27446             width : width + 'px',
27447             height : height + 'px'
27448         });
27449
27450         return;
27451             
27452     },
27453     
27454     setThumbBoxPosition : function()
27455     {
27456         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27457         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27458         
27459         this.thumbEl.setLeft(x);
27460         this.thumbEl.setTop(y);
27461         
27462     },
27463     
27464     baseRotateLevel : function()
27465     {
27466         this.baseRotate = 1;
27467         
27468         if(
27469                 typeof(this.exif) != 'undefined' &&
27470                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27471                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27472         ){
27473             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27474         }
27475         
27476         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27477         
27478     },
27479     
27480     baseScaleLevel : function()
27481     {
27482         var width, height;
27483         
27484         if(this.isDocument){
27485             
27486             if(this.baseRotate == 6 || this.baseRotate == 8){
27487             
27488                 height = this.thumbEl.getHeight();
27489                 this.baseScale = height / this.imageEl.OriginWidth;
27490
27491                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27492                     width = this.thumbEl.getWidth();
27493                     this.baseScale = width / this.imageEl.OriginHeight;
27494                 }
27495
27496                 return;
27497             }
27498
27499             height = this.thumbEl.getHeight();
27500             this.baseScale = height / this.imageEl.OriginHeight;
27501
27502             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27503                 width = this.thumbEl.getWidth();
27504                 this.baseScale = width / this.imageEl.OriginWidth;
27505             }
27506
27507             return;
27508         }
27509         
27510         if(this.baseRotate == 6 || this.baseRotate == 8){
27511             
27512             width = this.thumbEl.getHeight();
27513             this.baseScale = width / this.imageEl.OriginHeight;
27514             
27515             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27516                 height = this.thumbEl.getWidth();
27517                 this.baseScale = height / this.imageEl.OriginHeight;
27518             }
27519             
27520             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27521                 height = this.thumbEl.getWidth();
27522                 this.baseScale = height / this.imageEl.OriginHeight;
27523                 
27524                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27525                     width = this.thumbEl.getHeight();
27526                     this.baseScale = width / this.imageEl.OriginWidth;
27527                 }
27528             }
27529             
27530             return;
27531         }
27532         
27533         width = this.thumbEl.getWidth();
27534         this.baseScale = width / this.imageEl.OriginWidth;
27535         
27536         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27537             height = this.thumbEl.getHeight();
27538             this.baseScale = height / this.imageEl.OriginHeight;
27539         }
27540         
27541         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27542             
27543             height = this.thumbEl.getHeight();
27544             this.baseScale = height / this.imageEl.OriginHeight;
27545             
27546             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27547                 width = this.thumbEl.getWidth();
27548                 this.baseScale = width / this.imageEl.OriginWidth;
27549             }
27550             
27551         }
27552         
27553         return;
27554     },
27555     
27556     getScaleLevel : function()
27557     {
27558         return this.baseScale * Math.pow(1.1, this.scale);
27559     },
27560     
27561     onTouchStart : function(e)
27562     {
27563         if(!this.canvasLoaded){
27564             this.beforeSelectFile(e);
27565             return;
27566         }
27567         
27568         var touches = e.browserEvent.touches;
27569         
27570         if(!touches){
27571             return;
27572         }
27573         
27574         if(touches.length == 1){
27575             this.onMouseDown(e);
27576             return;
27577         }
27578         
27579         if(touches.length != 2){
27580             return;
27581         }
27582         
27583         var coords = [];
27584         
27585         for(var i = 0, finger; finger = touches[i]; i++){
27586             coords.push(finger.pageX, finger.pageY);
27587         }
27588         
27589         var x = Math.pow(coords[0] - coords[2], 2);
27590         var y = Math.pow(coords[1] - coords[3], 2);
27591         
27592         this.startDistance = Math.sqrt(x + y);
27593         
27594         this.startScale = this.scale;
27595         
27596         this.pinching = true;
27597         this.dragable = false;
27598         
27599     },
27600     
27601     onTouchMove : function(e)
27602     {
27603         if(!this.pinching && !this.dragable){
27604             return;
27605         }
27606         
27607         var touches = e.browserEvent.touches;
27608         
27609         if(!touches){
27610             return;
27611         }
27612         
27613         if(this.dragable){
27614             this.onMouseMove(e);
27615             return;
27616         }
27617         
27618         var coords = [];
27619         
27620         for(var i = 0, finger; finger = touches[i]; i++){
27621             coords.push(finger.pageX, finger.pageY);
27622         }
27623         
27624         var x = Math.pow(coords[0] - coords[2], 2);
27625         var y = Math.pow(coords[1] - coords[3], 2);
27626         
27627         this.endDistance = Math.sqrt(x + y);
27628         
27629         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
27630         
27631         if(!this.zoomable()){
27632             this.scale = this.startScale;
27633             return;
27634         }
27635         
27636         this.draw();
27637         
27638     },
27639     
27640     onTouchEnd : function(e)
27641     {
27642         this.pinching = false;
27643         this.dragable = false;
27644         
27645     },
27646     
27647     process : function(file, crop)
27648     {
27649         if(this.loadMask){
27650             this.maskEl.mask(this.loadingText);
27651         }
27652         
27653         this.xhr = new XMLHttpRequest();
27654         
27655         file.xhr = this.xhr;
27656
27657         this.xhr.open(this.method, this.url, true);
27658         
27659         var headers = {
27660             "Accept": "application/json",
27661             "Cache-Control": "no-cache",
27662             "X-Requested-With": "XMLHttpRequest"
27663         };
27664         
27665         for (var headerName in headers) {
27666             var headerValue = headers[headerName];
27667             if (headerValue) {
27668                 this.xhr.setRequestHeader(headerName, headerValue);
27669             }
27670         }
27671         
27672         var _this = this;
27673         
27674         this.xhr.onload = function()
27675         {
27676             _this.xhrOnLoad(_this.xhr);
27677         }
27678         
27679         this.xhr.onerror = function()
27680         {
27681             _this.xhrOnError(_this.xhr);
27682         }
27683         
27684         var formData = new FormData();
27685
27686         formData.append('returnHTML', 'NO');
27687         
27688         if(crop){
27689             formData.append('crop', crop);
27690         }
27691         
27692         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
27693             formData.append(this.paramName, file, file.name);
27694         }
27695         
27696         if(typeof(file.filename) != 'undefined'){
27697             formData.append('filename', file.filename);
27698         }
27699         
27700         if(typeof(file.mimetype) != 'undefined'){
27701             formData.append('mimetype', file.mimetype);
27702         }
27703         
27704         if(this.fireEvent('arrange', this, formData) != false){
27705             this.xhr.send(formData);
27706         };
27707     },
27708     
27709     xhrOnLoad : function(xhr)
27710     {
27711         if(this.loadMask){
27712             this.maskEl.unmask();
27713         }
27714         
27715         if (xhr.readyState !== 4) {
27716             this.fireEvent('exception', this, xhr);
27717             return;
27718         }
27719
27720         var response = Roo.decode(xhr.responseText);
27721         
27722         if(!response.success){
27723             this.fireEvent('exception', this, xhr);
27724             return;
27725         }
27726         
27727         var response = Roo.decode(xhr.responseText);
27728         
27729         this.fireEvent('upload', this, response);
27730         
27731     },
27732     
27733     xhrOnError : function()
27734     {
27735         if(this.loadMask){
27736             this.maskEl.unmask();
27737         }
27738         
27739         Roo.log('xhr on error');
27740         
27741         var response = Roo.decode(xhr.responseText);
27742           
27743         Roo.log(response);
27744         
27745     },
27746     
27747     prepare : function(file)
27748     {   
27749         if(this.loadMask){
27750             this.maskEl.mask(this.loadingText);
27751         }
27752         
27753         this.file = false;
27754         this.exif = {};
27755         
27756         if(typeof(file) === 'string'){
27757             this.loadCanvas(file);
27758             return;
27759         }
27760         
27761         if(!file || !this.urlAPI){
27762             return;
27763         }
27764         
27765         this.file = file;
27766         this.cropType = file.type;
27767         
27768         var _this = this;
27769         
27770         if(this.fireEvent('prepare', this, this.file) != false){
27771             
27772             var reader = new FileReader();
27773             
27774             reader.onload = function (e) {
27775                 if (e.target.error) {
27776                     Roo.log(e.target.error);
27777                     return;
27778                 }
27779                 
27780                 var buffer = e.target.result,
27781                     dataView = new DataView(buffer),
27782                     offset = 2,
27783                     maxOffset = dataView.byteLength - 4,
27784                     markerBytes,
27785                     markerLength;
27786                 
27787                 if (dataView.getUint16(0) === 0xffd8) {
27788                     while (offset < maxOffset) {
27789                         markerBytes = dataView.getUint16(offset);
27790                         
27791                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
27792                             markerLength = dataView.getUint16(offset + 2) + 2;
27793                             if (offset + markerLength > dataView.byteLength) {
27794                                 Roo.log('Invalid meta data: Invalid segment size.');
27795                                 break;
27796                             }
27797                             
27798                             if(markerBytes == 0xffe1){
27799                                 _this.parseExifData(
27800                                     dataView,
27801                                     offset,
27802                                     markerLength
27803                                 );
27804                             }
27805                             
27806                             offset += markerLength;
27807                             
27808                             continue;
27809                         }
27810                         
27811                         break;
27812                     }
27813                     
27814                 }
27815                 
27816                 var url = _this.urlAPI.createObjectURL(_this.file);
27817                 
27818                 _this.loadCanvas(url);
27819                 
27820                 return;
27821             }
27822             
27823             reader.readAsArrayBuffer(this.file);
27824             
27825         }
27826         
27827     },
27828     
27829     parseExifData : function(dataView, offset, length)
27830     {
27831         var tiffOffset = offset + 10,
27832             littleEndian,
27833             dirOffset;
27834     
27835         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27836             // No Exif data, might be XMP data instead
27837             return;
27838         }
27839         
27840         // Check for the ASCII code for "Exif" (0x45786966):
27841         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27842             // No Exif data, might be XMP data instead
27843             return;
27844         }
27845         if (tiffOffset + 8 > dataView.byteLength) {
27846             Roo.log('Invalid Exif data: Invalid segment size.');
27847             return;
27848         }
27849         // Check for the two null bytes:
27850         if (dataView.getUint16(offset + 8) !== 0x0000) {
27851             Roo.log('Invalid Exif data: Missing byte alignment offset.');
27852             return;
27853         }
27854         // Check the byte alignment:
27855         switch (dataView.getUint16(tiffOffset)) {
27856         case 0x4949:
27857             littleEndian = true;
27858             break;
27859         case 0x4D4D:
27860             littleEndian = false;
27861             break;
27862         default:
27863             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
27864             return;
27865         }
27866         // Check for the TIFF tag marker (0x002A):
27867         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
27868             Roo.log('Invalid Exif data: Missing TIFF marker.');
27869             return;
27870         }
27871         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
27872         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
27873         
27874         this.parseExifTags(
27875             dataView,
27876             tiffOffset,
27877             tiffOffset + dirOffset,
27878             littleEndian
27879         );
27880     },
27881     
27882     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
27883     {
27884         var tagsNumber,
27885             dirEndOffset,
27886             i;
27887         if (dirOffset + 6 > dataView.byteLength) {
27888             Roo.log('Invalid Exif data: Invalid directory offset.');
27889             return;
27890         }
27891         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
27892         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
27893         if (dirEndOffset + 4 > dataView.byteLength) {
27894             Roo.log('Invalid Exif data: Invalid directory size.');
27895             return;
27896         }
27897         for (i = 0; i < tagsNumber; i += 1) {
27898             this.parseExifTag(
27899                 dataView,
27900                 tiffOffset,
27901                 dirOffset + 2 + 12 * i, // tag offset
27902                 littleEndian
27903             );
27904         }
27905         // Return the offset to the next directory:
27906         return dataView.getUint32(dirEndOffset, littleEndian);
27907     },
27908     
27909     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
27910     {
27911         var tag = dataView.getUint16(offset, littleEndian);
27912         
27913         this.exif[tag] = this.getExifValue(
27914             dataView,
27915             tiffOffset,
27916             offset,
27917             dataView.getUint16(offset + 2, littleEndian), // tag type
27918             dataView.getUint32(offset + 4, littleEndian), // tag length
27919             littleEndian
27920         );
27921     },
27922     
27923     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
27924     {
27925         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
27926             tagSize,
27927             dataOffset,
27928             values,
27929             i,
27930             str,
27931             c;
27932     
27933         if (!tagType) {
27934             Roo.log('Invalid Exif data: Invalid tag type.');
27935             return;
27936         }
27937         
27938         tagSize = tagType.size * length;
27939         // Determine if the value is contained in the dataOffset bytes,
27940         // or if the value at the dataOffset is a pointer to the actual data:
27941         dataOffset = tagSize > 4 ?
27942                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
27943         if (dataOffset + tagSize > dataView.byteLength) {
27944             Roo.log('Invalid Exif data: Invalid data offset.');
27945             return;
27946         }
27947         if (length === 1) {
27948             return tagType.getValue(dataView, dataOffset, littleEndian);
27949         }
27950         values = [];
27951         for (i = 0; i < length; i += 1) {
27952             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
27953         }
27954         
27955         if (tagType.ascii) {
27956             str = '';
27957             // Concatenate the chars:
27958             for (i = 0; i < values.length; i += 1) {
27959                 c = values[i];
27960                 // Ignore the terminating NULL byte(s):
27961                 if (c === '\u0000') {
27962                     break;
27963                 }
27964                 str += c;
27965             }
27966             return str;
27967         }
27968         return values;
27969     }
27970     
27971 });
27972
27973 Roo.apply(Roo.bootstrap.UploadCropbox, {
27974     tags : {
27975         'Orientation': 0x0112
27976     },
27977     
27978     Orientation: {
27979             1: 0, //'top-left',
27980 //            2: 'top-right',
27981             3: 180, //'bottom-right',
27982 //            4: 'bottom-left',
27983 //            5: 'left-top',
27984             6: 90, //'right-top',
27985 //            7: 'right-bottom',
27986             8: 270 //'left-bottom'
27987     },
27988     
27989     exifTagTypes : {
27990         // byte, 8-bit unsigned int:
27991         1: {
27992             getValue: function (dataView, dataOffset) {
27993                 return dataView.getUint8(dataOffset);
27994             },
27995             size: 1
27996         },
27997         // ascii, 8-bit byte:
27998         2: {
27999             getValue: function (dataView, dataOffset) {
28000                 return String.fromCharCode(dataView.getUint8(dataOffset));
28001             },
28002             size: 1,
28003             ascii: true
28004         },
28005         // short, 16 bit int:
28006         3: {
28007             getValue: function (dataView, dataOffset, littleEndian) {
28008                 return dataView.getUint16(dataOffset, littleEndian);
28009             },
28010             size: 2
28011         },
28012         // long, 32 bit int:
28013         4: {
28014             getValue: function (dataView, dataOffset, littleEndian) {
28015                 return dataView.getUint32(dataOffset, littleEndian);
28016             },
28017             size: 4
28018         },
28019         // rational = two long values, first is numerator, second is denominator:
28020         5: {
28021             getValue: function (dataView, dataOffset, littleEndian) {
28022                 return dataView.getUint32(dataOffset, littleEndian) /
28023                     dataView.getUint32(dataOffset + 4, littleEndian);
28024             },
28025             size: 8
28026         },
28027         // slong, 32 bit signed int:
28028         9: {
28029             getValue: function (dataView, dataOffset, littleEndian) {
28030                 return dataView.getInt32(dataOffset, littleEndian);
28031             },
28032             size: 4
28033         },
28034         // srational, two slongs, first is numerator, second is denominator:
28035         10: {
28036             getValue: function (dataView, dataOffset, littleEndian) {
28037                 return dataView.getInt32(dataOffset, littleEndian) /
28038                     dataView.getInt32(dataOffset + 4, littleEndian);
28039             },
28040             size: 8
28041         }
28042     },
28043     
28044     footer : {
28045         STANDARD : [
28046             {
28047                 tag : 'div',
28048                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28049                 action : 'rotate-left',
28050                 cn : [
28051                     {
28052                         tag : 'button',
28053                         cls : 'btn btn-default',
28054                         html : '<i class="fa fa-undo"></i>'
28055                     }
28056                 ]
28057             },
28058             {
28059                 tag : 'div',
28060                 cls : 'btn-group roo-upload-cropbox-picture',
28061                 action : 'picture',
28062                 cn : [
28063                     {
28064                         tag : 'button',
28065                         cls : 'btn btn-default',
28066                         html : '<i class="fa fa-picture-o"></i>'
28067                     }
28068                 ]
28069             },
28070             {
28071                 tag : 'div',
28072                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28073                 action : 'rotate-right',
28074                 cn : [
28075                     {
28076                         tag : 'button',
28077                         cls : 'btn btn-default',
28078                         html : '<i class="fa fa-repeat"></i>'
28079                     }
28080                 ]
28081             }
28082         ],
28083         DOCUMENT : [
28084             {
28085                 tag : 'div',
28086                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28087                 action : 'rotate-left',
28088                 cn : [
28089                     {
28090                         tag : 'button',
28091                         cls : 'btn btn-default',
28092                         html : '<i class="fa fa-undo"></i>'
28093                     }
28094                 ]
28095             },
28096             {
28097                 tag : 'div',
28098                 cls : 'btn-group roo-upload-cropbox-download',
28099                 action : 'download',
28100                 cn : [
28101                     {
28102                         tag : 'button',
28103                         cls : 'btn btn-default',
28104                         html : '<i class="fa fa-download"></i>'
28105                     }
28106                 ]
28107             },
28108             {
28109                 tag : 'div',
28110                 cls : 'btn-group roo-upload-cropbox-crop',
28111                 action : 'crop',
28112                 cn : [
28113                     {
28114                         tag : 'button',
28115                         cls : 'btn btn-default',
28116                         html : '<i class="fa fa-crop"></i>'
28117                     }
28118                 ]
28119             },
28120             {
28121                 tag : 'div',
28122                 cls : 'btn-group roo-upload-cropbox-trash',
28123                 action : 'trash',
28124                 cn : [
28125                     {
28126                         tag : 'button',
28127                         cls : 'btn btn-default',
28128                         html : '<i class="fa fa-trash"></i>'
28129                     }
28130                 ]
28131             },
28132             {
28133                 tag : 'div',
28134                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28135                 action : 'rotate-right',
28136                 cn : [
28137                     {
28138                         tag : 'button',
28139                         cls : 'btn btn-default',
28140                         html : '<i class="fa fa-repeat"></i>'
28141                     }
28142                 ]
28143             }
28144         ],
28145         ROTATOR : [
28146             {
28147                 tag : 'div',
28148                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28149                 action : 'rotate-left',
28150                 cn : [
28151                     {
28152                         tag : 'button',
28153                         cls : 'btn btn-default',
28154                         html : '<i class="fa fa-undo"></i>'
28155                     }
28156                 ]
28157             },
28158             {
28159                 tag : 'div',
28160                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28161                 action : 'rotate-right',
28162                 cn : [
28163                     {
28164                         tag : 'button',
28165                         cls : 'btn btn-default',
28166                         html : '<i class="fa fa-repeat"></i>'
28167                     }
28168                 ]
28169             }
28170         ]
28171     }
28172 });
28173
28174 /*
28175 * Licence: LGPL
28176 */
28177
28178 /**
28179  * @class Roo.bootstrap.DocumentManager
28180  * @extends Roo.bootstrap.Component
28181  * Bootstrap DocumentManager class
28182  * @cfg {String} paramName default 'imageUpload'
28183  * @cfg {String} toolTipName default 'filename'
28184  * @cfg {String} method default POST
28185  * @cfg {String} url action url
28186  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28187  * @cfg {Boolean} multiple multiple upload default true
28188  * @cfg {Number} thumbSize default 300
28189  * @cfg {String} fieldLabel
28190  * @cfg {Number} labelWidth default 4
28191  * @cfg {String} labelAlign (left|top) default left
28192  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28193 * @cfg {Number} labellg set the width of label (1-12)
28194  * @cfg {Number} labelmd set the width of label (1-12)
28195  * @cfg {Number} labelsm set the width of label (1-12)
28196  * @cfg {Number} labelxs set the width of label (1-12)
28197  * 
28198  * @constructor
28199  * Create a new DocumentManager
28200  * @param {Object} config The config object
28201  */
28202
28203 Roo.bootstrap.DocumentManager = function(config){
28204     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28205     
28206     this.files = [];
28207     this.delegates = [];
28208     
28209     this.addEvents({
28210         /**
28211          * @event initial
28212          * Fire when initial the DocumentManager
28213          * @param {Roo.bootstrap.DocumentManager} this
28214          */
28215         "initial" : true,
28216         /**
28217          * @event inspect
28218          * inspect selected file
28219          * @param {Roo.bootstrap.DocumentManager} this
28220          * @param {File} file
28221          */
28222         "inspect" : true,
28223         /**
28224          * @event exception
28225          * Fire when xhr load exception
28226          * @param {Roo.bootstrap.DocumentManager} this
28227          * @param {XMLHttpRequest} xhr
28228          */
28229         "exception" : true,
28230         /**
28231          * @event afterupload
28232          * Fire when xhr load exception
28233          * @param {Roo.bootstrap.DocumentManager} this
28234          * @param {XMLHttpRequest} xhr
28235          */
28236         "afterupload" : true,
28237         /**
28238          * @event prepare
28239          * prepare the form data
28240          * @param {Roo.bootstrap.DocumentManager} this
28241          * @param {Object} formData
28242          */
28243         "prepare" : true,
28244         /**
28245          * @event remove
28246          * Fire when remove the file
28247          * @param {Roo.bootstrap.DocumentManager} this
28248          * @param {Object} file
28249          */
28250         "remove" : true,
28251         /**
28252          * @event refresh
28253          * Fire after refresh the file
28254          * @param {Roo.bootstrap.DocumentManager} this
28255          */
28256         "refresh" : true,
28257         /**
28258          * @event click
28259          * Fire after click the image
28260          * @param {Roo.bootstrap.DocumentManager} this
28261          * @param {Object} file
28262          */
28263         "click" : true,
28264         /**
28265          * @event edit
28266          * Fire when upload a image and editable set to true
28267          * @param {Roo.bootstrap.DocumentManager} this
28268          * @param {Object} file
28269          */
28270         "edit" : true,
28271         /**
28272          * @event beforeselectfile
28273          * Fire before select file
28274          * @param {Roo.bootstrap.DocumentManager} this
28275          */
28276         "beforeselectfile" : true,
28277         /**
28278          * @event process
28279          * Fire before process file
28280          * @param {Roo.bootstrap.DocumentManager} this
28281          * @param {Object} file
28282          */
28283         "process" : true
28284         
28285     });
28286 };
28287
28288 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28289     
28290     boxes : 0,
28291     inputName : '',
28292     thumbSize : 300,
28293     multiple : true,
28294     files : false,
28295     method : 'POST',
28296     url : '',
28297     paramName : 'imageUpload',
28298     toolTipName : 'filename',
28299     fieldLabel : '',
28300     labelWidth : 4,
28301     labelAlign : 'left',
28302     editable : true,
28303     delegates : false,
28304     xhr : false, 
28305     
28306     labellg : 0,
28307     labelmd : 0,
28308     labelsm : 0,
28309     labelxs : 0,
28310     
28311     getAutoCreate : function()
28312     {   
28313         var managerWidget = {
28314             tag : 'div',
28315             cls : 'roo-document-manager',
28316             cn : [
28317                 {
28318                     tag : 'input',
28319                     cls : 'roo-document-manager-selector',
28320                     type : 'file'
28321                 },
28322                 {
28323                     tag : 'div',
28324                     cls : 'roo-document-manager-uploader',
28325                     cn : [
28326                         {
28327                             tag : 'div',
28328                             cls : 'roo-document-manager-upload-btn',
28329                             html : '<i class="fa fa-plus"></i>'
28330                         }
28331                     ]
28332                     
28333                 }
28334             ]
28335         };
28336         
28337         var content = [
28338             {
28339                 tag : 'div',
28340                 cls : 'column col-md-12',
28341                 cn : managerWidget
28342             }
28343         ];
28344         
28345         if(this.fieldLabel.length){
28346             
28347             content = [
28348                 {
28349                     tag : 'div',
28350                     cls : 'column col-md-12',
28351                     html : this.fieldLabel
28352                 },
28353                 {
28354                     tag : 'div',
28355                     cls : 'column col-md-12',
28356                     cn : managerWidget
28357                 }
28358             ];
28359
28360             if(this.labelAlign == 'left'){
28361                 content = [
28362                     {
28363                         tag : 'div',
28364                         cls : 'column',
28365                         html : this.fieldLabel
28366                     },
28367                     {
28368                         tag : 'div',
28369                         cls : 'column',
28370                         cn : managerWidget
28371                     }
28372                 ];
28373                 
28374                 if(this.labelWidth > 12){
28375                     content[0].style = "width: " + this.labelWidth + 'px';
28376                 }
28377
28378                 if(this.labelWidth < 13 && this.labelmd == 0){
28379                     this.labelmd = this.labelWidth;
28380                 }
28381
28382                 if(this.labellg > 0){
28383                     content[0].cls += ' col-lg-' + this.labellg;
28384                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28385                 }
28386
28387                 if(this.labelmd > 0){
28388                     content[0].cls += ' col-md-' + this.labelmd;
28389                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28390                 }
28391
28392                 if(this.labelsm > 0){
28393                     content[0].cls += ' col-sm-' + this.labelsm;
28394                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28395                 }
28396
28397                 if(this.labelxs > 0){
28398                     content[0].cls += ' col-xs-' + this.labelxs;
28399                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28400                 }
28401                 
28402             }
28403         }
28404         
28405         var cfg = {
28406             tag : 'div',
28407             cls : 'row clearfix',
28408             cn : content
28409         };
28410         
28411         return cfg;
28412         
28413     },
28414     
28415     initEvents : function()
28416     {
28417         this.managerEl = this.el.select('.roo-document-manager', true).first();
28418         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28419         
28420         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28421         this.selectorEl.hide();
28422         
28423         if(this.multiple){
28424             this.selectorEl.attr('multiple', 'multiple');
28425         }
28426         
28427         this.selectorEl.on('change', this.onFileSelected, this);
28428         
28429         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28430         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28431         
28432         this.uploader.on('click', this.onUploaderClick, this);
28433         
28434         this.renderProgressDialog();
28435         
28436         var _this = this;
28437         
28438         window.addEventListener("resize", function() { _this.refresh(); } );
28439         
28440         this.fireEvent('initial', this);
28441     },
28442     
28443     renderProgressDialog : function()
28444     {
28445         var _this = this;
28446         
28447         this.progressDialog = new Roo.bootstrap.Modal({
28448             cls : 'roo-document-manager-progress-dialog',
28449             allow_close : false,
28450             title : '',
28451             buttons : [
28452                 {
28453                     name  :'cancel',
28454                     weight : 'danger',
28455                     html : 'Cancel'
28456                 }
28457             ], 
28458             listeners : { 
28459                 btnclick : function() {
28460                     _this.uploadCancel();
28461                     this.hide();
28462                 }
28463             }
28464         });
28465          
28466         this.progressDialog.render(Roo.get(document.body));
28467          
28468         this.progress = new Roo.bootstrap.Progress({
28469             cls : 'roo-document-manager-progress',
28470             active : true,
28471             striped : true
28472         });
28473         
28474         this.progress.render(this.progressDialog.getChildContainer());
28475         
28476         this.progressBar = new Roo.bootstrap.ProgressBar({
28477             cls : 'roo-document-manager-progress-bar',
28478             aria_valuenow : 0,
28479             aria_valuemin : 0,
28480             aria_valuemax : 12,
28481             panel : 'success'
28482         });
28483         
28484         this.progressBar.render(this.progress.getChildContainer());
28485     },
28486     
28487     onUploaderClick : function(e)
28488     {
28489         e.preventDefault();
28490      
28491         if(this.fireEvent('beforeselectfile', this) != false){
28492             this.selectorEl.dom.click();
28493         }
28494         
28495     },
28496     
28497     onFileSelected : function(e)
28498     {
28499         e.preventDefault();
28500         
28501         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28502             return;
28503         }
28504         
28505         Roo.each(this.selectorEl.dom.files, function(file){
28506             if(this.fireEvent('inspect', this, file) != false){
28507                 this.files.push(file);
28508             }
28509         }, this);
28510         
28511         this.queue();
28512         
28513     },
28514     
28515     queue : function()
28516     {
28517         this.selectorEl.dom.value = '';
28518         
28519         if(!this.files.length){
28520             return;
28521         }
28522         
28523         if(this.boxes > 0 && this.files.length > this.boxes){
28524             this.files = this.files.slice(0, this.boxes);
28525         }
28526         
28527         this.uploader.show();
28528         
28529         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28530             this.uploader.hide();
28531         }
28532         
28533         var _this = this;
28534         
28535         var files = [];
28536         
28537         var docs = [];
28538         
28539         Roo.each(this.files, function(file){
28540             
28541             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28542                 var f = this.renderPreview(file);
28543                 files.push(f);
28544                 return;
28545             }
28546             
28547             if(file.type.indexOf('image') != -1){
28548                 this.delegates.push(
28549                     (function(){
28550                         _this.process(file);
28551                     }).createDelegate(this)
28552                 );
28553         
28554                 return;
28555             }
28556             
28557             docs.push(
28558                 (function(){
28559                     _this.process(file);
28560                 }).createDelegate(this)
28561             );
28562             
28563         }, this);
28564         
28565         this.files = files;
28566         
28567         this.delegates = this.delegates.concat(docs);
28568         
28569         if(!this.delegates.length){
28570             this.refresh();
28571             return;
28572         }
28573         
28574         this.progressBar.aria_valuemax = this.delegates.length;
28575         
28576         this.arrange();
28577         
28578         return;
28579     },
28580     
28581     arrange : function()
28582     {
28583         if(!this.delegates.length){
28584             this.progressDialog.hide();
28585             this.refresh();
28586             return;
28587         }
28588         
28589         var delegate = this.delegates.shift();
28590         
28591         this.progressDialog.show();
28592         
28593         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
28594         
28595         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
28596         
28597         delegate();
28598     },
28599     
28600     refresh : function()
28601     {
28602         this.uploader.show();
28603         
28604         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28605             this.uploader.hide();
28606         }
28607         
28608         Roo.isTouch ? this.closable(false) : this.closable(true);
28609         
28610         this.fireEvent('refresh', this);
28611     },
28612     
28613     onRemove : function(e, el, o)
28614     {
28615         e.preventDefault();
28616         
28617         this.fireEvent('remove', this, o);
28618         
28619     },
28620     
28621     remove : function(o)
28622     {
28623         var files = [];
28624         
28625         Roo.each(this.files, function(file){
28626             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
28627                 files.push(file);
28628                 return;
28629             }
28630
28631             o.target.remove();
28632
28633         }, this);
28634         
28635         this.files = files;
28636         
28637         this.refresh();
28638     },
28639     
28640     clear : function()
28641     {
28642         Roo.each(this.files, function(file){
28643             if(!file.target){
28644                 return;
28645             }
28646             
28647             file.target.remove();
28648
28649         }, this);
28650         
28651         this.files = [];
28652         
28653         this.refresh();
28654     },
28655     
28656     onClick : function(e, el, o)
28657     {
28658         e.preventDefault();
28659         
28660         this.fireEvent('click', this, o);
28661         
28662     },
28663     
28664     closable : function(closable)
28665     {
28666         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
28667             
28668             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28669             
28670             if(closable){
28671                 el.show();
28672                 return;
28673             }
28674             
28675             el.hide();
28676             
28677         }, this);
28678     },
28679     
28680     xhrOnLoad : function(xhr)
28681     {
28682         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28683             el.remove();
28684         }, this);
28685         
28686         if (xhr.readyState !== 4) {
28687             this.arrange();
28688             this.fireEvent('exception', this, xhr);
28689             return;
28690         }
28691
28692         var response = Roo.decode(xhr.responseText);
28693         
28694         if(!response.success){
28695             this.arrange();
28696             this.fireEvent('exception', this, xhr);
28697             return;
28698         }
28699         
28700         var file = this.renderPreview(response.data);
28701         
28702         this.files.push(file);
28703         
28704         this.arrange();
28705         
28706         this.fireEvent('afterupload', this, xhr);
28707         
28708     },
28709     
28710     xhrOnError : function(xhr)
28711     {
28712         Roo.log('xhr on error');
28713         
28714         var response = Roo.decode(xhr.responseText);
28715           
28716         Roo.log(response);
28717         
28718         this.arrange();
28719     },
28720     
28721     process : function(file)
28722     {
28723         if(this.fireEvent('process', this, file) !== false){
28724             if(this.editable && file.type.indexOf('image') != -1){
28725                 this.fireEvent('edit', this, file);
28726                 return;
28727             }
28728
28729             this.uploadStart(file, false);
28730
28731             return;
28732         }
28733         
28734     },
28735     
28736     uploadStart : function(file, crop)
28737     {
28738         this.xhr = new XMLHttpRequest();
28739         
28740         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28741             this.arrange();
28742             return;
28743         }
28744         
28745         file.xhr = this.xhr;
28746             
28747         this.managerEl.createChild({
28748             tag : 'div',
28749             cls : 'roo-document-manager-loading',
28750             cn : [
28751                 {
28752                     tag : 'div',
28753                     tooltip : file.name,
28754                     cls : 'roo-document-manager-thumb',
28755                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28756                 }
28757             ]
28758
28759         });
28760
28761         this.xhr.open(this.method, this.url, true);
28762         
28763         var headers = {
28764             "Accept": "application/json",
28765             "Cache-Control": "no-cache",
28766             "X-Requested-With": "XMLHttpRequest"
28767         };
28768         
28769         for (var headerName in headers) {
28770             var headerValue = headers[headerName];
28771             if (headerValue) {
28772                 this.xhr.setRequestHeader(headerName, headerValue);
28773             }
28774         }
28775         
28776         var _this = this;
28777         
28778         this.xhr.onload = function()
28779         {
28780             _this.xhrOnLoad(_this.xhr);
28781         }
28782         
28783         this.xhr.onerror = function()
28784         {
28785             _this.xhrOnError(_this.xhr);
28786         }
28787         
28788         var formData = new FormData();
28789
28790         formData.append('returnHTML', 'NO');
28791         
28792         if(crop){
28793             formData.append('crop', crop);
28794         }
28795         
28796         formData.append(this.paramName, file, file.name);
28797         
28798         var options = {
28799             file : file, 
28800             manually : false
28801         };
28802         
28803         if(this.fireEvent('prepare', this, formData, options) != false){
28804             
28805             if(options.manually){
28806                 return;
28807             }
28808             
28809             this.xhr.send(formData);
28810             return;
28811         };
28812         
28813         this.uploadCancel();
28814     },
28815     
28816     uploadCancel : function()
28817     {
28818         if (this.xhr) {
28819             this.xhr.abort();
28820         }
28821         
28822         this.delegates = [];
28823         
28824         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28825             el.remove();
28826         }, this);
28827         
28828         this.arrange();
28829     },
28830     
28831     renderPreview : function(file)
28832     {
28833         if(typeof(file.target) != 'undefined' && file.target){
28834             return file;
28835         }
28836         
28837         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
28838         
28839         var previewEl = this.managerEl.createChild({
28840             tag : 'div',
28841             cls : 'roo-document-manager-preview',
28842             cn : [
28843                 {
28844                     tag : 'div',
28845                     tooltip : file[this.toolTipName],
28846                     cls : 'roo-document-manager-thumb',
28847                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
28848                 },
28849                 {
28850                     tag : 'button',
28851                     cls : 'close',
28852                     html : '<i class="fa fa-times-circle"></i>'
28853                 }
28854             ]
28855         });
28856
28857         var close = previewEl.select('button.close', true).first();
28858
28859         close.on('click', this.onRemove, this, file);
28860
28861         file.target = previewEl;
28862
28863         var image = previewEl.select('img', true).first();
28864         
28865         var _this = this;
28866         
28867         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
28868         
28869         image.on('click', this.onClick, this, file);
28870         
28871         return file;
28872         
28873     },
28874     
28875     onPreviewLoad : function(file, image)
28876     {
28877         if(typeof(file.target) == 'undefined' || !file.target){
28878             return;
28879         }
28880         
28881         var width = image.dom.naturalWidth || image.dom.width;
28882         var height = image.dom.naturalHeight || image.dom.height;
28883         
28884         if(width > height){
28885             file.target.addClass('wide');
28886             return;
28887         }
28888         
28889         file.target.addClass('tall');
28890         return;
28891         
28892     },
28893     
28894     uploadFromSource : function(file, crop)
28895     {
28896         this.xhr = new XMLHttpRequest();
28897         
28898         this.managerEl.createChild({
28899             tag : 'div',
28900             cls : 'roo-document-manager-loading',
28901             cn : [
28902                 {
28903                     tag : 'div',
28904                     tooltip : file.name,
28905                     cls : 'roo-document-manager-thumb',
28906                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28907                 }
28908             ]
28909
28910         });
28911
28912         this.xhr.open(this.method, this.url, true);
28913         
28914         var headers = {
28915             "Accept": "application/json",
28916             "Cache-Control": "no-cache",
28917             "X-Requested-With": "XMLHttpRequest"
28918         };
28919         
28920         for (var headerName in headers) {
28921             var headerValue = headers[headerName];
28922             if (headerValue) {
28923                 this.xhr.setRequestHeader(headerName, headerValue);
28924             }
28925         }
28926         
28927         var _this = this;
28928         
28929         this.xhr.onload = function()
28930         {
28931             _this.xhrOnLoad(_this.xhr);
28932         }
28933         
28934         this.xhr.onerror = function()
28935         {
28936             _this.xhrOnError(_this.xhr);
28937         }
28938         
28939         var formData = new FormData();
28940
28941         formData.append('returnHTML', 'NO');
28942         
28943         formData.append('crop', crop);
28944         
28945         if(typeof(file.filename) != 'undefined'){
28946             formData.append('filename', file.filename);
28947         }
28948         
28949         if(typeof(file.mimetype) != 'undefined'){
28950             formData.append('mimetype', file.mimetype);
28951         }
28952         
28953         Roo.log(formData);
28954         
28955         if(this.fireEvent('prepare', this, formData) != false){
28956             this.xhr.send(formData);
28957         };
28958     }
28959 });
28960
28961 /*
28962 * Licence: LGPL
28963 */
28964
28965 /**
28966  * @class Roo.bootstrap.DocumentViewer
28967  * @extends Roo.bootstrap.Component
28968  * Bootstrap DocumentViewer class
28969  * @cfg {Boolean} showDownload (true|false) show download button (default true)
28970  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
28971  * 
28972  * @constructor
28973  * Create a new DocumentViewer
28974  * @param {Object} config The config object
28975  */
28976
28977 Roo.bootstrap.DocumentViewer = function(config){
28978     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
28979     
28980     this.addEvents({
28981         /**
28982          * @event initial
28983          * Fire after initEvent
28984          * @param {Roo.bootstrap.DocumentViewer} this
28985          */
28986         "initial" : true,
28987         /**
28988          * @event click
28989          * Fire after click
28990          * @param {Roo.bootstrap.DocumentViewer} this
28991          */
28992         "click" : true,
28993         /**
28994          * @event download
28995          * Fire after download button
28996          * @param {Roo.bootstrap.DocumentViewer} this
28997          */
28998         "download" : true,
28999         /**
29000          * @event trash
29001          * Fire after trash button
29002          * @param {Roo.bootstrap.DocumentViewer} this
29003          */
29004         "trash" : true
29005         
29006     });
29007 };
29008
29009 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29010     
29011     showDownload : true,
29012     
29013     showTrash : true,
29014     
29015     getAutoCreate : function()
29016     {
29017         var cfg = {
29018             tag : 'div',
29019             cls : 'roo-document-viewer',
29020             cn : [
29021                 {
29022                     tag : 'div',
29023                     cls : 'roo-document-viewer-body',
29024                     cn : [
29025                         {
29026                             tag : 'div',
29027                             cls : 'roo-document-viewer-thumb',
29028                             cn : [
29029                                 {
29030                                     tag : 'img',
29031                                     cls : 'roo-document-viewer-image'
29032                                 }
29033                             ]
29034                         }
29035                     ]
29036                 },
29037                 {
29038                     tag : 'div',
29039                     cls : 'roo-document-viewer-footer',
29040                     cn : {
29041                         tag : 'div',
29042                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29043                         cn : [
29044                             {
29045                                 tag : 'div',
29046                                 cls : 'btn-group roo-document-viewer-download',
29047                                 cn : [
29048                                     {
29049                                         tag : 'button',
29050                                         cls : 'btn btn-default',
29051                                         html : '<i class="fa fa-download"></i>'
29052                                     }
29053                                 ]
29054                             },
29055                             {
29056                                 tag : 'div',
29057                                 cls : 'btn-group roo-document-viewer-trash',
29058                                 cn : [
29059                                     {
29060                                         tag : 'button',
29061                                         cls : 'btn btn-default',
29062                                         html : '<i class="fa fa-trash"></i>'
29063                                     }
29064                                 ]
29065                             }
29066                         ]
29067                     }
29068                 }
29069             ]
29070         };
29071         
29072         return cfg;
29073     },
29074     
29075     initEvents : function()
29076     {
29077         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29078         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29079         
29080         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29081         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29082         
29083         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29084         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29085         
29086         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29087         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29088         
29089         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29090         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29091         
29092         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29093         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29094         
29095         this.bodyEl.on('click', this.onClick, this);
29096         this.downloadBtn.on('click', this.onDownload, this);
29097         this.trashBtn.on('click', this.onTrash, this);
29098         
29099         this.downloadBtn.hide();
29100         this.trashBtn.hide();
29101         
29102         if(this.showDownload){
29103             this.downloadBtn.show();
29104         }
29105         
29106         if(this.showTrash){
29107             this.trashBtn.show();
29108         }
29109         
29110         if(!this.showDownload && !this.showTrash) {
29111             this.footerEl.hide();
29112         }
29113         
29114     },
29115     
29116     initial : function()
29117     {
29118         this.fireEvent('initial', this);
29119         
29120     },
29121     
29122     onClick : function(e)
29123     {
29124         e.preventDefault();
29125         
29126         this.fireEvent('click', this);
29127     },
29128     
29129     onDownload : function(e)
29130     {
29131         e.preventDefault();
29132         
29133         this.fireEvent('download', this);
29134     },
29135     
29136     onTrash : function(e)
29137     {
29138         e.preventDefault();
29139         
29140         this.fireEvent('trash', this);
29141     }
29142     
29143 });
29144 /*
29145  * - LGPL
29146  *
29147  * nav progress bar
29148  * 
29149  */
29150
29151 /**
29152  * @class Roo.bootstrap.NavProgressBar
29153  * @extends Roo.bootstrap.Component
29154  * Bootstrap NavProgressBar class
29155  * 
29156  * @constructor
29157  * Create a new nav progress bar
29158  * @param {Object} config The config object
29159  */
29160
29161 Roo.bootstrap.NavProgressBar = function(config){
29162     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29163
29164     this.bullets = this.bullets || [];
29165    
29166 //    Roo.bootstrap.NavProgressBar.register(this);
29167      this.addEvents({
29168         /**
29169              * @event changed
29170              * Fires when the active item changes
29171              * @param {Roo.bootstrap.NavProgressBar} this
29172              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29173              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29174          */
29175         'changed': true
29176      });
29177     
29178 };
29179
29180 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29181     
29182     bullets : [],
29183     barItems : [],
29184     
29185     getAutoCreate : function()
29186     {
29187         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29188         
29189         cfg = {
29190             tag : 'div',
29191             cls : 'roo-navigation-bar-group',
29192             cn : [
29193                 {
29194                     tag : 'div',
29195                     cls : 'roo-navigation-top-bar'
29196                 },
29197                 {
29198                     tag : 'div',
29199                     cls : 'roo-navigation-bullets-bar',
29200                     cn : [
29201                         {
29202                             tag : 'ul',
29203                             cls : 'roo-navigation-bar'
29204                         }
29205                     ]
29206                 },
29207                 
29208                 {
29209                     tag : 'div',
29210                     cls : 'roo-navigation-bottom-bar'
29211                 }
29212             ]
29213             
29214         };
29215         
29216         return cfg;
29217         
29218     },
29219     
29220     initEvents: function() 
29221     {
29222         
29223     },
29224     
29225     onRender : function(ct, position) 
29226     {
29227         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29228         
29229         if(this.bullets.length){
29230             Roo.each(this.bullets, function(b){
29231                this.addItem(b);
29232             }, this);
29233         }
29234         
29235         this.format();
29236         
29237     },
29238     
29239     addItem : function(cfg)
29240     {
29241         var item = new Roo.bootstrap.NavProgressItem(cfg);
29242         
29243         item.parentId = this.id;
29244         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29245         
29246         if(cfg.html){
29247             var top = new Roo.bootstrap.Element({
29248                 tag : 'div',
29249                 cls : 'roo-navigation-bar-text'
29250             });
29251             
29252             var bottom = new Roo.bootstrap.Element({
29253                 tag : 'div',
29254                 cls : 'roo-navigation-bar-text'
29255             });
29256             
29257             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29258             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29259             
29260             var topText = new Roo.bootstrap.Element({
29261                 tag : 'span',
29262                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29263             });
29264             
29265             var bottomText = new Roo.bootstrap.Element({
29266                 tag : 'span',
29267                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29268             });
29269             
29270             topText.onRender(top.el, null);
29271             bottomText.onRender(bottom.el, null);
29272             
29273             item.topEl = top;
29274             item.bottomEl = bottom;
29275         }
29276         
29277         this.barItems.push(item);
29278         
29279         return item;
29280     },
29281     
29282     getActive : function()
29283     {
29284         var active = false;
29285         
29286         Roo.each(this.barItems, function(v){
29287             
29288             if (!v.isActive()) {
29289                 return;
29290             }
29291             
29292             active = v;
29293             return false;
29294             
29295         });
29296         
29297         return active;
29298     },
29299     
29300     setActiveItem : function(item)
29301     {
29302         var prev = false;
29303         
29304         Roo.each(this.barItems, function(v){
29305             if (v.rid == item.rid) {
29306                 return ;
29307             }
29308             
29309             if (v.isActive()) {
29310                 v.setActive(false);
29311                 prev = v;
29312             }
29313         });
29314
29315         item.setActive(true);
29316         
29317         this.fireEvent('changed', this, item, prev);
29318     },
29319     
29320     getBarItem: function(rid)
29321     {
29322         var ret = false;
29323         
29324         Roo.each(this.barItems, function(e) {
29325             if (e.rid != rid) {
29326                 return;
29327             }
29328             
29329             ret =  e;
29330             return false;
29331         });
29332         
29333         return ret;
29334     },
29335     
29336     indexOfItem : function(item)
29337     {
29338         var index = false;
29339         
29340         Roo.each(this.barItems, function(v, i){
29341             
29342             if (v.rid != item.rid) {
29343                 return;
29344             }
29345             
29346             index = i;
29347             return false
29348         });
29349         
29350         return index;
29351     },
29352     
29353     setActiveNext : function()
29354     {
29355         var i = this.indexOfItem(this.getActive());
29356         
29357         if (i > this.barItems.length) {
29358             return;
29359         }
29360         
29361         this.setActiveItem(this.barItems[i+1]);
29362     },
29363     
29364     setActivePrev : function()
29365     {
29366         var i = this.indexOfItem(this.getActive());
29367         
29368         if (i  < 1) {
29369             return;
29370         }
29371         
29372         this.setActiveItem(this.barItems[i-1]);
29373     },
29374     
29375     format : function()
29376     {
29377         if(!this.barItems.length){
29378             return;
29379         }
29380      
29381         var width = 100 / this.barItems.length;
29382         
29383         Roo.each(this.barItems, function(i){
29384             i.el.setStyle('width', width + '%');
29385             i.topEl.el.setStyle('width', width + '%');
29386             i.bottomEl.el.setStyle('width', width + '%');
29387         }, this);
29388         
29389     }
29390     
29391 });
29392 /*
29393  * - LGPL
29394  *
29395  * Nav Progress Item
29396  * 
29397  */
29398
29399 /**
29400  * @class Roo.bootstrap.NavProgressItem
29401  * @extends Roo.bootstrap.Component
29402  * Bootstrap NavProgressItem class
29403  * @cfg {String} rid the reference id
29404  * @cfg {Boolean} active (true|false) Is item active default false
29405  * @cfg {Boolean} disabled (true|false) Is item active default false
29406  * @cfg {String} html
29407  * @cfg {String} position (top|bottom) text position default bottom
29408  * @cfg {String} icon show icon instead of number
29409  * 
29410  * @constructor
29411  * Create a new NavProgressItem
29412  * @param {Object} config The config object
29413  */
29414 Roo.bootstrap.NavProgressItem = function(config){
29415     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29416     this.addEvents({
29417         // raw events
29418         /**
29419          * @event click
29420          * The raw click event for the entire grid.
29421          * @param {Roo.bootstrap.NavProgressItem} this
29422          * @param {Roo.EventObject} e
29423          */
29424         "click" : true
29425     });
29426    
29427 };
29428
29429 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29430     
29431     rid : '',
29432     active : false,
29433     disabled : false,
29434     html : '',
29435     position : 'bottom',
29436     icon : false,
29437     
29438     getAutoCreate : function()
29439     {
29440         var iconCls = 'roo-navigation-bar-item-icon';
29441         
29442         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29443         
29444         var cfg = {
29445             tag: 'li',
29446             cls: 'roo-navigation-bar-item',
29447             cn : [
29448                 {
29449                     tag : 'i',
29450                     cls : iconCls
29451                 }
29452             ]
29453         };
29454         
29455         if(this.active){
29456             cfg.cls += ' active';
29457         }
29458         if(this.disabled){
29459             cfg.cls += ' disabled';
29460         }
29461         
29462         return cfg;
29463     },
29464     
29465     disable : function()
29466     {
29467         this.setDisabled(true);
29468     },
29469     
29470     enable : function()
29471     {
29472         this.setDisabled(false);
29473     },
29474     
29475     initEvents: function() 
29476     {
29477         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29478         
29479         this.iconEl.on('click', this.onClick, this);
29480     },
29481     
29482     onClick : function(e)
29483     {
29484         e.preventDefault();
29485         
29486         if(this.disabled){
29487             return;
29488         }
29489         
29490         if(this.fireEvent('click', this, e) === false){
29491             return;
29492         };
29493         
29494         this.parent().setActiveItem(this);
29495     },
29496     
29497     isActive: function () 
29498     {
29499         return this.active;
29500     },
29501     
29502     setActive : function(state)
29503     {
29504         if(this.active == state){
29505             return;
29506         }
29507         
29508         this.active = state;
29509         
29510         if (state) {
29511             this.el.addClass('active');
29512             return;
29513         }
29514         
29515         this.el.removeClass('active');
29516         
29517         return;
29518     },
29519     
29520     setDisabled : function(state)
29521     {
29522         if(this.disabled == state){
29523             return;
29524         }
29525         
29526         this.disabled = state;
29527         
29528         if (state) {
29529             this.el.addClass('disabled');
29530             return;
29531         }
29532         
29533         this.el.removeClass('disabled');
29534     },
29535     
29536     tooltipEl : function()
29537     {
29538         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29539     }
29540 });
29541  
29542
29543  /*
29544  * - LGPL
29545  *
29546  * FieldLabel
29547  * 
29548  */
29549
29550 /**
29551  * @class Roo.bootstrap.FieldLabel
29552  * @extends Roo.bootstrap.Component
29553  * Bootstrap FieldLabel class
29554  * @cfg {String} html contents of the element
29555  * @cfg {String} tag tag of the element default label
29556  * @cfg {String} cls class of the element
29557  * @cfg {String} target label target 
29558  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29559  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
29560  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
29561  * @cfg {String} iconTooltip default "This field is required"
29562  * 
29563  * @constructor
29564  * Create a new FieldLabel
29565  * @param {Object} config The config object
29566  */
29567
29568 Roo.bootstrap.FieldLabel = function(config){
29569     Roo.bootstrap.Element.superclass.constructor.call(this, config);
29570     
29571     this.addEvents({
29572             /**
29573              * @event invalid
29574              * Fires after the field has been marked as invalid.
29575              * @param {Roo.form.FieldLabel} this
29576              * @param {String} msg The validation message
29577              */
29578             invalid : true,
29579             /**
29580              * @event valid
29581              * Fires after the field has been validated with no errors.
29582              * @param {Roo.form.FieldLabel} this
29583              */
29584             valid : true
29585         });
29586 };
29587
29588 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
29589     
29590     tag: 'label',
29591     cls: '',
29592     html: '',
29593     target: '',
29594     allowBlank : true,
29595     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
29596     validClass : 'text-success fa fa-lg fa-check',
29597     iconTooltip : 'This field is required',
29598     
29599     getAutoCreate : function(){
29600         
29601         var cfg = {
29602             tag : this.tag,
29603             cls : 'roo-bootstrap-field-label ' + this.cls,
29604             for : this.target,
29605             cn : [
29606                 {
29607                     tag : 'i',
29608                     cls : '',
29609                     tooltip : this.iconTooltip
29610                 },
29611                 {
29612                     tag : 'span',
29613                     html : this.html
29614                 }
29615             ] 
29616         };
29617         
29618         return cfg;
29619     },
29620     
29621     initEvents: function() 
29622     {
29623         Roo.bootstrap.Element.superclass.initEvents.call(this);
29624         
29625         this.iconEl = this.el.select('i', true).first();
29626         
29627         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
29628         
29629         Roo.bootstrap.FieldLabel.register(this);
29630     },
29631     
29632     /**
29633      * Mark this field as valid
29634      */
29635     markValid : function()
29636     {
29637         this.iconEl.show();
29638         
29639         this.iconEl.removeClass(this.invalidClass);
29640         
29641         this.iconEl.addClass(this.validClass);
29642         
29643         this.fireEvent('valid', this);
29644     },
29645     
29646     /**
29647      * Mark this field as invalid
29648      * @param {String} msg The validation message
29649      */
29650     markInvalid : function(msg)
29651     {
29652         this.iconEl.show();
29653         
29654         this.iconEl.removeClass(this.validClass);
29655         
29656         this.iconEl.addClass(this.invalidClass);
29657         
29658         this.fireEvent('invalid', this, msg);
29659     }
29660     
29661    
29662 });
29663
29664 Roo.apply(Roo.bootstrap.FieldLabel, {
29665     
29666     groups: {},
29667     
29668      /**
29669     * register a FieldLabel Group
29670     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
29671     */
29672     register : function(label)
29673     {
29674         if(this.groups.hasOwnProperty(label.target)){
29675             return;
29676         }
29677      
29678         this.groups[label.target] = label;
29679         
29680     },
29681     /**
29682     * fetch a FieldLabel Group based on the target
29683     * @param {string} target
29684     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
29685     */
29686     get: function(target) {
29687         if (typeof(this.groups[target]) == 'undefined') {
29688             return false;
29689         }
29690         
29691         return this.groups[target] ;
29692     }
29693 });
29694
29695  
29696
29697  /*
29698  * - LGPL
29699  *
29700  * page DateSplitField.
29701  * 
29702  */
29703
29704
29705 /**
29706  * @class Roo.bootstrap.DateSplitField
29707  * @extends Roo.bootstrap.Component
29708  * Bootstrap DateSplitField class
29709  * @cfg {string} fieldLabel - the label associated
29710  * @cfg {Number} labelWidth set the width of label (0-12)
29711  * @cfg {String} labelAlign (top|left)
29712  * @cfg {Boolean} dayAllowBlank (true|false) default false
29713  * @cfg {Boolean} monthAllowBlank (true|false) default false
29714  * @cfg {Boolean} yearAllowBlank (true|false) default false
29715  * @cfg {string} dayPlaceholder 
29716  * @cfg {string} monthPlaceholder
29717  * @cfg {string} yearPlaceholder
29718  * @cfg {string} dayFormat default 'd'
29719  * @cfg {string} monthFormat default 'm'
29720  * @cfg {string} yearFormat default 'Y'
29721  * @cfg {Number} labellg set the width of label (1-12)
29722  * @cfg {Number} labelmd set the width of label (1-12)
29723  * @cfg {Number} labelsm set the width of label (1-12)
29724  * @cfg {Number} labelxs set the width of label (1-12)
29725
29726  *     
29727  * @constructor
29728  * Create a new DateSplitField
29729  * @param {Object} config The config object
29730  */
29731
29732 Roo.bootstrap.DateSplitField = function(config){
29733     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
29734     
29735     this.addEvents({
29736         // raw events
29737          /**
29738          * @event years
29739          * getting the data of years
29740          * @param {Roo.bootstrap.DateSplitField} this
29741          * @param {Object} years
29742          */
29743         "years" : true,
29744         /**
29745          * @event days
29746          * getting the data of days
29747          * @param {Roo.bootstrap.DateSplitField} this
29748          * @param {Object} days
29749          */
29750         "days" : true,
29751         /**
29752          * @event invalid
29753          * Fires after the field has been marked as invalid.
29754          * @param {Roo.form.Field} this
29755          * @param {String} msg The validation message
29756          */
29757         invalid : true,
29758        /**
29759          * @event valid
29760          * Fires after the field has been validated with no errors.
29761          * @param {Roo.form.Field} this
29762          */
29763         valid : true
29764     });
29765 };
29766
29767 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
29768     
29769     fieldLabel : '',
29770     labelAlign : 'top',
29771     labelWidth : 3,
29772     dayAllowBlank : false,
29773     monthAllowBlank : false,
29774     yearAllowBlank : false,
29775     dayPlaceholder : '',
29776     monthPlaceholder : '',
29777     yearPlaceholder : '',
29778     dayFormat : 'd',
29779     monthFormat : 'm',
29780     yearFormat : 'Y',
29781     isFormField : true,
29782     labellg : 0,
29783     labelmd : 0,
29784     labelsm : 0,
29785     labelxs : 0,
29786     
29787     getAutoCreate : function()
29788     {
29789         var cfg = {
29790             tag : 'div',
29791             cls : 'row roo-date-split-field-group',
29792             cn : [
29793                 {
29794                     tag : 'input',
29795                     type : 'hidden',
29796                     cls : 'form-hidden-field roo-date-split-field-group-value',
29797                     name : this.name
29798                 }
29799             ]
29800         };
29801         
29802         var labelCls = 'col-md-12';
29803         var contentCls = 'col-md-4';
29804         
29805         if(this.fieldLabel){
29806             
29807             var label = {
29808                 tag : 'div',
29809                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
29810                 cn : [
29811                     {
29812                         tag : 'label',
29813                         html : this.fieldLabel
29814                     }
29815                 ]
29816             };
29817             
29818             if(this.labelAlign == 'left'){
29819             
29820                 if(this.labelWidth > 12){
29821                     label.style = "width: " + this.labelWidth + 'px';
29822                 }
29823
29824                 if(this.labelWidth < 13 && this.labelmd == 0){
29825                     this.labelmd = this.labelWidth;
29826                 }
29827
29828                 if(this.labellg > 0){
29829                     labelCls = ' col-lg-' + this.labellg;
29830                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
29831                 }
29832
29833                 if(this.labelmd > 0){
29834                     labelCls = ' col-md-' + this.labelmd;
29835                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
29836                 }
29837
29838                 if(this.labelsm > 0){
29839                     labelCls = ' col-sm-' + this.labelsm;
29840                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
29841                 }
29842
29843                 if(this.labelxs > 0){
29844                     labelCls = ' col-xs-' + this.labelxs;
29845                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
29846                 }
29847             }
29848             
29849             label.cls += ' ' + labelCls;
29850             
29851             cfg.cn.push(label);
29852         }
29853         
29854         Roo.each(['day', 'month', 'year'], function(t){
29855             cfg.cn.push({
29856                 tag : 'div',
29857                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
29858             });
29859         }, this);
29860         
29861         return cfg;
29862     },
29863     
29864     inputEl: function ()
29865     {
29866         return this.el.select('.roo-date-split-field-group-value', true).first();
29867     },
29868     
29869     onRender : function(ct, position) 
29870     {
29871         var _this = this;
29872         
29873         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29874         
29875         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
29876         
29877         this.dayField = new Roo.bootstrap.ComboBox({
29878             allowBlank : this.dayAllowBlank,
29879             alwaysQuery : true,
29880             displayField : 'value',
29881             editable : false,
29882             fieldLabel : '',
29883             forceSelection : true,
29884             mode : 'local',
29885             placeholder : this.dayPlaceholder,
29886             selectOnFocus : true,
29887             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29888             triggerAction : 'all',
29889             typeAhead : true,
29890             valueField : 'value',
29891             store : new Roo.data.SimpleStore({
29892                 data : (function() {    
29893                     var days = [];
29894                     _this.fireEvent('days', _this, days);
29895                     return days;
29896                 })(),
29897                 fields : [ 'value' ]
29898             }),
29899             listeners : {
29900                 select : function (_self, record, index)
29901                 {
29902                     _this.setValue(_this.getValue());
29903                 }
29904             }
29905         });
29906
29907         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
29908         
29909         this.monthField = new Roo.bootstrap.MonthField({
29910             after : '<i class=\"fa fa-calendar\"></i>',
29911             allowBlank : this.monthAllowBlank,
29912             placeholder : this.monthPlaceholder,
29913             readOnly : true,
29914             listeners : {
29915                 render : function (_self)
29916                 {
29917                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
29918                         e.preventDefault();
29919                         _self.focus();
29920                     });
29921                 },
29922                 select : function (_self, oldvalue, newvalue)
29923                 {
29924                     _this.setValue(_this.getValue());
29925                 }
29926             }
29927         });
29928         
29929         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
29930         
29931         this.yearField = new Roo.bootstrap.ComboBox({
29932             allowBlank : this.yearAllowBlank,
29933             alwaysQuery : true,
29934             displayField : 'value',
29935             editable : false,
29936             fieldLabel : '',
29937             forceSelection : true,
29938             mode : 'local',
29939             placeholder : this.yearPlaceholder,
29940             selectOnFocus : true,
29941             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29942             triggerAction : 'all',
29943             typeAhead : true,
29944             valueField : 'value',
29945             store : new Roo.data.SimpleStore({
29946                 data : (function() {
29947                     var years = [];
29948                     _this.fireEvent('years', _this, years);
29949                     return years;
29950                 })(),
29951                 fields : [ 'value' ]
29952             }),
29953             listeners : {
29954                 select : function (_self, record, index)
29955                 {
29956                     _this.setValue(_this.getValue());
29957                 }
29958             }
29959         });
29960
29961         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
29962     },
29963     
29964     setValue : function(v, format)
29965     {
29966         this.inputEl.dom.value = v;
29967         
29968         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
29969         
29970         var d = Date.parseDate(v, f);
29971         
29972         if(!d){
29973             this.validate();
29974             return;
29975         }
29976         
29977         this.setDay(d.format(this.dayFormat));
29978         this.setMonth(d.format(this.monthFormat));
29979         this.setYear(d.format(this.yearFormat));
29980         
29981         this.validate();
29982         
29983         return;
29984     },
29985     
29986     setDay : function(v)
29987     {
29988         this.dayField.setValue(v);
29989         this.inputEl.dom.value = this.getValue();
29990         this.validate();
29991         return;
29992     },
29993     
29994     setMonth : function(v)
29995     {
29996         this.monthField.setValue(v, true);
29997         this.inputEl.dom.value = this.getValue();
29998         this.validate();
29999         return;
30000     },
30001     
30002     setYear : function(v)
30003     {
30004         this.yearField.setValue(v);
30005         this.inputEl.dom.value = this.getValue();
30006         this.validate();
30007         return;
30008     },
30009     
30010     getDay : function()
30011     {
30012         return this.dayField.getValue();
30013     },
30014     
30015     getMonth : function()
30016     {
30017         return this.monthField.getValue();
30018     },
30019     
30020     getYear : function()
30021     {
30022         return this.yearField.getValue();
30023     },
30024     
30025     getValue : function()
30026     {
30027         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30028         
30029         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30030         
30031         return date;
30032     },
30033     
30034     reset : function()
30035     {
30036         this.setDay('');
30037         this.setMonth('');
30038         this.setYear('');
30039         this.inputEl.dom.value = '';
30040         this.validate();
30041         return;
30042     },
30043     
30044     validate : function()
30045     {
30046         var d = this.dayField.validate();
30047         var m = this.monthField.validate();
30048         var y = this.yearField.validate();
30049         
30050         var valid = true;
30051         
30052         if(
30053                 (!this.dayAllowBlank && !d) ||
30054                 (!this.monthAllowBlank && !m) ||
30055                 (!this.yearAllowBlank && !y)
30056         ){
30057             valid = false;
30058         }
30059         
30060         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30061             return valid;
30062         }
30063         
30064         if(valid){
30065             this.markValid();
30066             return valid;
30067         }
30068         
30069         this.markInvalid();
30070         
30071         return valid;
30072     },
30073     
30074     markValid : function()
30075     {
30076         
30077         var label = this.el.select('label', true).first();
30078         var icon = this.el.select('i.fa-star', true).first();
30079
30080         if(label && icon){
30081             icon.remove();
30082         }
30083         
30084         this.fireEvent('valid', this);
30085     },
30086     
30087      /**
30088      * Mark this field as invalid
30089      * @param {String} msg The validation message
30090      */
30091     markInvalid : function(msg)
30092     {
30093         
30094         var label = this.el.select('label', true).first();
30095         var icon = this.el.select('i.fa-star', true).first();
30096
30097         if(label && !icon){
30098             this.el.select('.roo-date-split-field-label', true).createChild({
30099                 tag : 'i',
30100                 cls : 'text-danger fa fa-lg fa-star',
30101                 tooltip : 'This field is required',
30102                 style : 'margin-right:5px;'
30103             }, label, true);
30104         }
30105         
30106         this.fireEvent('invalid', this, msg);
30107     },
30108     
30109     clearInvalid : function()
30110     {
30111         var label = this.el.select('label', true).first();
30112         var icon = this.el.select('i.fa-star', true).first();
30113
30114         if(label && icon){
30115             icon.remove();
30116         }
30117         
30118         this.fireEvent('valid', this);
30119     },
30120     
30121     getName: function()
30122     {
30123         return this.name;
30124     }
30125     
30126 });
30127
30128  /**
30129  *
30130  * This is based on 
30131  * http://masonry.desandro.com
30132  *
30133  * The idea is to render all the bricks based on vertical width...
30134  *
30135  * The original code extends 'outlayer' - we might need to use that....
30136  * 
30137  */
30138
30139
30140 /**
30141  * @class Roo.bootstrap.LayoutMasonry
30142  * @extends Roo.bootstrap.Component
30143  * Bootstrap Layout Masonry class
30144  * 
30145  * @constructor
30146  * Create a new Element
30147  * @param {Object} config The config object
30148  */
30149
30150 Roo.bootstrap.LayoutMasonry = function(config){
30151     
30152     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30153     
30154     this.bricks = [];
30155     
30156     Roo.bootstrap.LayoutMasonry.register(this);
30157     
30158     this.addEvents({
30159         // raw events
30160         /**
30161          * @event layout
30162          * Fire after layout the items
30163          * @param {Roo.bootstrap.LayoutMasonry} this
30164          * @param {Roo.EventObject} e
30165          */
30166         "layout" : true
30167     });
30168     
30169 };
30170
30171 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30172     
30173     /**
30174      * @cfg {Boolean} isLayoutInstant = no animation?
30175      */   
30176     isLayoutInstant : false, // needed?
30177    
30178     /**
30179      * @cfg {Number} boxWidth  width of the columns
30180      */   
30181     boxWidth : 450,
30182     
30183       /**
30184      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30185      */   
30186     boxHeight : 0,
30187     
30188     /**
30189      * @cfg {Number} padWidth padding below box..
30190      */   
30191     padWidth : 10, 
30192     
30193     /**
30194      * @cfg {Number} gutter gutter width..
30195      */   
30196     gutter : 10,
30197     
30198      /**
30199      * @cfg {Number} maxCols maximum number of columns
30200      */   
30201     
30202     maxCols: 0,
30203     
30204     /**
30205      * @cfg {Boolean} isAutoInitial defalut true
30206      */   
30207     isAutoInitial : true, 
30208     
30209     containerWidth: 0,
30210     
30211     /**
30212      * @cfg {Boolean} isHorizontal defalut false
30213      */   
30214     isHorizontal : false, 
30215
30216     currentSize : null,
30217     
30218     tag: 'div',
30219     
30220     cls: '',
30221     
30222     bricks: null, //CompositeElement
30223     
30224     cols : 1,
30225     
30226     _isLayoutInited : false,
30227     
30228 //    isAlternative : false, // only use for vertical layout...
30229     
30230     /**
30231      * @cfg {Number} alternativePadWidth padding below box..
30232      */   
30233     alternativePadWidth : 50,
30234     
30235     selectedBrick : [],
30236     
30237     getAutoCreate : function(){
30238         
30239         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30240         
30241         var cfg = {
30242             tag: this.tag,
30243             cls: 'blog-masonary-wrapper ' + this.cls,
30244             cn : {
30245                 cls : 'mas-boxes masonary'
30246             }
30247         };
30248         
30249         return cfg;
30250     },
30251     
30252     getChildContainer: function( )
30253     {
30254         if (this.boxesEl) {
30255             return this.boxesEl;
30256         }
30257         
30258         this.boxesEl = this.el.select('.mas-boxes').first();
30259         
30260         return this.boxesEl;
30261     },
30262     
30263     
30264     initEvents : function()
30265     {
30266         var _this = this;
30267         
30268         if(this.isAutoInitial){
30269             Roo.log('hook children rendered');
30270             this.on('childrenrendered', function() {
30271                 Roo.log('children rendered');
30272                 _this.initial();
30273             } ,this);
30274         }
30275     },
30276     
30277     initial : function()
30278     {
30279         this.selectedBrick = [];
30280         
30281         this.currentSize = this.el.getBox(true);
30282         
30283         Roo.EventManager.onWindowResize(this.resize, this); 
30284
30285         if(!this.isAutoInitial){
30286             this.layout();
30287             return;
30288         }
30289         
30290         this.layout();
30291         
30292         return;
30293         //this.layout.defer(500,this);
30294         
30295     },
30296     
30297     resize : function()
30298     {
30299         var cs = this.el.getBox(true);
30300         
30301         if (
30302                 this.currentSize.width == cs.width && 
30303                 this.currentSize.x == cs.x && 
30304                 this.currentSize.height == cs.height && 
30305                 this.currentSize.y == cs.y 
30306         ) {
30307             Roo.log("no change in with or X or Y");
30308             return;
30309         }
30310         
30311         this.currentSize = cs;
30312         
30313         this.layout();
30314         
30315     },
30316     
30317     layout : function()
30318     {   
30319         this._resetLayout();
30320         
30321         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30322         
30323         this.layoutItems( isInstant );
30324       
30325         this._isLayoutInited = true;
30326         
30327         this.fireEvent('layout', this);
30328         
30329     },
30330     
30331     _resetLayout : function()
30332     {
30333         if(this.isHorizontal){
30334             this.horizontalMeasureColumns();
30335             return;
30336         }
30337         
30338         this.verticalMeasureColumns();
30339         
30340     },
30341     
30342     verticalMeasureColumns : function()
30343     {
30344         this.getContainerWidth();
30345         
30346 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30347 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30348 //            return;
30349 //        }
30350         
30351         var boxWidth = this.boxWidth + this.padWidth;
30352         
30353         if(this.containerWidth < this.boxWidth){
30354             boxWidth = this.containerWidth
30355         }
30356         
30357         var containerWidth = this.containerWidth;
30358         
30359         var cols = Math.floor(containerWidth / boxWidth);
30360         
30361         this.cols = Math.max( cols, 1 );
30362         
30363         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30364         
30365         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30366         
30367         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30368         
30369         this.colWidth = boxWidth + avail - this.padWidth;
30370         
30371         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30372         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30373     },
30374     
30375     horizontalMeasureColumns : function()
30376     {
30377         this.getContainerWidth();
30378         
30379         var boxWidth = this.boxWidth;
30380         
30381         if(this.containerWidth < boxWidth){
30382             boxWidth = this.containerWidth;
30383         }
30384         
30385         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30386         
30387         this.el.setHeight(boxWidth);
30388         
30389     },
30390     
30391     getContainerWidth : function()
30392     {
30393         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30394     },
30395     
30396     layoutItems : function( isInstant )
30397     {
30398         Roo.log(this.bricks);
30399         
30400         var items = Roo.apply([], this.bricks);
30401         
30402         if(this.isHorizontal){
30403             this._horizontalLayoutItems( items , isInstant );
30404             return;
30405         }
30406         
30407 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30408 //            this._verticalAlternativeLayoutItems( items , isInstant );
30409 //            return;
30410 //        }
30411         
30412         this._verticalLayoutItems( items , isInstant );
30413         
30414     },
30415     
30416     _verticalLayoutItems : function ( items , isInstant)
30417     {
30418         if ( !items || !items.length ) {
30419             return;
30420         }
30421         
30422         var standard = [
30423             ['xs', 'xs', 'xs', 'tall'],
30424             ['xs', 'xs', 'tall'],
30425             ['xs', 'xs', 'sm'],
30426             ['xs', 'xs', 'xs'],
30427             ['xs', 'tall'],
30428             ['xs', 'sm'],
30429             ['xs', 'xs'],
30430             ['xs'],
30431             
30432             ['sm', 'xs', 'xs'],
30433             ['sm', 'xs'],
30434             ['sm'],
30435             
30436             ['tall', 'xs', 'xs', 'xs'],
30437             ['tall', 'xs', 'xs'],
30438             ['tall', 'xs'],
30439             ['tall']
30440             
30441         ];
30442         
30443         var queue = [];
30444         
30445         var boxes = [];
30446         
30447         var box = [];
30448         
30449         Roo.each(items, function(item, k){
30450             
30451             switch (item.size) {
30452                 // these layouts take up a full box,
30453                 case 'md' :
30454                 case 'md-left' :
30455                 case 'md-right' :
30456                 case 'wide' :
30457                     
30458                     if(box.length){
30459                         boxes.push(box);
30460                         box = [];
30461                     }
30462                     
30463                     boxes.push([item]);
30464                     
30465                     break;
30466                     
30467                 case 'xs' :
30468                 case 'sm' :
30469                 case 'tall' :
30470                     
30471                     box.push(item);
30472                     
30473                     break;
30474                 default :
30475                     break;
30476                     
30477             }
30478             
30479         }, this);
30480         
30481         if(box.length){
30482             boxes.push(box);
30483             box = [];
30484         }
30485         
30486         var filterPattern = function(box, length)
30487         {
30488             if(!box.length){
30489                 return;
30490             }
30491             
30492             var match = false;
30493             
30494             var pattern = box.slice(0, length);
30495             
30496             var format = [];
30497             
30498             Roo.each(pattern, function(i){
30499                 format.push(i.size);
30500             }, this);
30501             
30502             Roo.each(standard, function(s){
30503                 
30504                 if(String(s) != String(format)){
30505                     return;
30506                 }
30507                 
30508                 match = true;
30509                 return false;
30510                 
30511             }, this);
30512             
30513             if(!match && length == 1){
30514                 return;
30515             }
30516             
30517             if(!match){
30518                 filterPattern(box, length - 1);
30519                 return;
30520             }
30521                 
30522             queue.push(pattern);
30523
30524             box = box.slice(length, box.length);
30525
30526             filterPattern(box, 4);
30527
30528             return;
30529             
30530         }
30531         
30532         Roo.each(boxes, function(box, k){
30533             
30534             if(!box.length){
30535                 return;
30536             }
30537             
30538             if(box.length == 1){
30539                 queue.push(box);
30540                 return;
30541             }
30542             
30543             filterPattern(box, 4);
30544             
30545         }, this);
30546         
30547         this._processVerticalLayoutQueue( queue, isInstant );
30548         
30549     },
30550     
30551 //    _verticalAlternativeLayoutItems : function( items , isInstant )
30552 //    {
30553 //        if ( !items || !items.length ) {
30554 //            return;
30555 //        }
30556 //
30557 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
30558 //        
30559 //    },
30560     
30561     _horizontalLayoutItems : function ( items , isInstant)
30562     {
30563         if ( !items || !items.length || items.length < 3) {
30564             return;
30565         }
30566         
30567         items.reverse();
30568         
30569         var eItems = items.slice(0, 3);
30570         
30571         items = items.slice(3, items.length);
30572         
30573         var standard = [
30574             ['xs', 'xs', 'xs', 'wide'],
30575             ['xs', 'xs', 'wide'],
30576             ['xs', 'xs', 'sm'],
30577             ['xs', 'xs', 'xs'],
30578             ['xs', 'wide'],
30579             ['xs', 'sm'],
30580             ['xs', 'xs'],
30581             ['xs'],
30582             
30583             ['sm', 'xs', 'xs'],
30584             ['sm', 'xs'],
30585             ['sm'],
30586             
30587             ['wide', 'xs', 'xs', 'xs'],
30588             ['wide', 'xs', 'xs'],
30589             ['wide', 'xs'],
30590             ['wide'],
30591             
30592             ['wide-thin']
30593         ];
30594         
30595         var queue = [];
30596         
30597         var boxes = [];
30598         
30599         var box = [];
30600         
30601         Roo.each(items, function(item, k){
30602             
30603             switch (item.size) {
30604                 case 'md' :
30605                 case 'md-left' :
30606                 case 'md-right' :
30607                 case 'tall' :
30608                     
30609                     if(box.length){
30610                         boxes.push(box);
30611                         box = [];
30612                     }
30613                     
30614                     boxes.push([item]);
30615                     
30616                     break;
30617                     
30618                 case 'xs' :
30619                 case 'sm' :
30620                 case 'wide' :
30621                 case 'wide-thin' :
30622                     
30623                     box.push(item);
30624                     
30625                     break;
30626                 default :
30627                     break;
30628                     
30629             }
30630             
30631         }, this);
30632         
30633         if(box.length){
30634             boxes.push(box);
30635             box = [];
30636         }
30637         
30638         var filterPattern = function(box, length)
30639         {
30640             if(!box.length){
30641                 return;
30642             }
30643             
30644             var match = false;
30645             
30646             var pattern = box.slice(0, length);
30647             
30648             var format = [];
30649             
30650             Roo.each(pattern, function(i){
30651                 format.push(i.size);
30652             }, this);
30653             
30654             Roo.each(standard, function(s){
30655                 
30656                 if(String(s) != String(format)){
30657                     return;
30658                 }
30659                 
30660                 match = true;
30661                 return false;
30662                 
30663             }, this);
30664             
30665             if(!match && length == 1){
30666                 return;
30667             }
30668             
30669             if(!match){
30670                 filterPattern(box, length - 1);
30671                 return;
30672             }
30673                 
30674             queue.push(pattern);
30675
30676             box = box.slice(length, box.length);
30677
30678             filterPattern(box, 4);
30679
30680             return;
30681             
30682         }
30683         
30684         Roo.each(boxes, function(box, k){
30685             
30686             if(!box.length){
30687                 return;
30688             }
30689             
30690             if(box.length == 1){
30691                 queue.push(box);
30692                 return;
30693             }
30694             
30695             filterPattern(box, 4);
30696             
30697         }, this);
30698         
30699         
30700         var prune = [];
30701         
30702         var pos = this.el.getBox(true);
30703         
30704         var minX = pos.x;
30705         
30706         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30707         
30708         var hit_end = false;
30709         
30710         Roo.each(queue, function(box){
30711             
30712             if(hit_end){
30713                 
30714                 Roo.each(box, function(b){
30715                 
30716                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30717                     b.el.hide();
30718
30719                 }, this);
30720
30721                 return;
30722             }
30723             
30724             var mx = 0;
30725             
30726             Roo.each(box, function(b){
30727                 
30728                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
30729                 b.el.show();
30730
30731                 mx = Math.max(mx, b.x);
30732                 
30733             }, this);
30734             
30735             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
30736             
30737             if(maxX < minX){
30738                 
30739                 Roo.each(box, function(b){
30740                 
30741                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30742                     b.el.hide();
30743                     
30744                 }, this);
30745                 
30746                 hit_end = true;
30747                 
30748                 return;
30749             }
30750             
30751             prune.push(box);
30752             
30753         }, this);
30754         
30755         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
30756     },
30757     
30758     /** Sets position of item in DOM
30759     * @param {Element} item
30760     * @param {Number} x - horizontal position
30761     * @param {Number} y - vertical position
30762     * @param {Boolean} isInstant - disables transitions
30763     */
30764     _processVerticalLayoutQueue : function( queue, isInstant )
30765     {
30766         var pos = this.el.getBox(true);
30767         var x = pos.x;
30768         var y = pos.y;
30769         var maxY = [];
30770         
30771         for (var i = 0; i < this.cols; i++){
30772             maxY[i] = pos.y;
30773         }
30774         
30775         Roo.each(queue, function(box, k){
30776             
30777             var col = k % this.cols;
30778             
30779             Roo.each(box, function(b,kk){
30780                 
30781                 b.el.position('absolute');
30782                 
30783                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30784                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30785                 
30786                 if(b.size == 'md-left' || b.size == 'md-right'){
30787                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30788                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30789                 }
30790                 
30791                 b.el.setWidth(width);
30792                 b.el.setHeight(height);
30793                 // iframe?
30794                 b.el.select('iframe',true).setSize(width,height);
30795                 
30796             }, this);
30797             
30798             for (var i = 0; i < this.cols; i++){
30799                 
30800                 if(maxY[i] < maxY[col]){
30801                     col = i;
30802                     continue;
30803                 }
30804                 
30805                 col = Math.min(col, i);
30806                 
30807             }
30808             
30809             x = pos.x + col * (this.colWidth + this.padWidth);
30810             
30811             y = maxY[col];
30812             
30813             var positions = [];
30814             
30815             switch (box.length){
30816                 case 1 :
30817                     positions = this.getVerticalOneBoxColPositions(x, y, box);
30818                     break;
30819                 case 2 :
30820                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
30821                     break;
30822                 case 3 :
30823                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
30824                     break;
30825                 case 4 :
30826                     positions = this.getVerticalFourBoxColPositions(x, y, box);
30827                     break;
30828                 default :
30829                     break;
30830             }
30831             
30832             Roo.each(box, function(b,kk){
30833                 
30834                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30835                 
30836                 var sz = b.el.getSize();
30837                 
30838                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
30839                 
30840             }, this);
30841             
30842         }, this);
30843         
30844         var mY = 0;
30845         
30846         for (var i = 0; i < this.cols; i++){
30847             mY = Math.max(mY, maxY[i]);
30848         }
30849         
30850         this.el.setHeight(mY - pos.y);
30851         
30852     },
30853     
30854 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
30855 //    {
30856 //        var pos = this.el.getBox(true);
30857 //        var x = pos.x;
30858 //        var y = pos.y;
30859 //        var maxX = pos.right;
30860 //        
30861 //        var maxHeight = 0;
30862 //        
30863 //        Roo.each(items, function(item, k){
30864 //            
30865 //            var c = k % 2;
30866 //            
30867 //            item.el.position('absolute');
30868 //                
30869 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
30870 //
30871 //            item.el.setWidth(width);
30872 //
30873 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
30874 //
30875 //            item.el.setHeight(height);
30876 //            
30877 //            if(c == 0){
30878 //                item.el.setXY([x, y], isInstant ? false : true);
30879 //            } else {
30880 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
30881 //            }
30882 //            
30883 //            y = y + height + this.alternativePadWidth;
30884 //            
30885 //            maxHeight = maxHeight + height + this.alternativePadWidth;
30886 //            
30887 //        }, this);
30888 //        
30889 //        this.el.setHeight(maxHeight);
30890 //        
30891 //    },
30892     
30893     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
30894     {
30895         var pos = this.el.getBox(true);
30896         
30897         var minX = pos.x;
30898         var minY = pos.y;
30899         
30900         var maxX = pos.right;
30901         
30902         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
30903         
30904         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30905         
30906         Roo.each(queue, function(box, k){
30907             
30908             Roo.each(box, function(b, kk){
30909                 
30910                 b.el.position('absolute');
30911                 
30912                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30913                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30914                 
30915                 if(b.size == 'md-left' || b.size == 'md-right'){
30916                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30917                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30918                 }
30919                 
30920                 b.el.setWidth(width);
30921                 b.el.setHeight(height);
30922                 
30923             }, this);
30924             
30925             if(!box.length){
30926                 return;
30927             }
30928             
30929             var positions = [];
30930             
30931             switch (box.length){
30932                 case 1 :
30933                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
30934                     break;
30935                 case 2 :
30936                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
30937                     break;
30938                 case 3 :
30939                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
30940                     break;
30941                 case 4 :
30942                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
30943                     break;
30944                 default :
30945                     break;
30946             }
30947             
30948             Roo.each(box, function(b,kk){
30949                 
30950                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30951                 
30952                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
30953                 
30954             }, this);
30955             
30956         }, this);
30957         
30958     },
30959     
30960     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
30961     {
30962         Roo.each(eItems, function(b,k){
30963             
30964             b.size = (k == 0) ? 'sm' : 'xs';
30965             b.x = (k == 0) ? 2 : 1;
30966             b.y = (k == 0) ? 2 : 1;
30967             
30968             b.el.position('absolute');
30969             
30970             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30971                 
30972             b.el.setWidth(width);
30973             
30974             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30975             
30976             b.el.setHeight(height);
30977             
30978         }, this);
30979
30980         var positions = [];
30981         
30982         positions.push({
30983             x : maxX - this.unitWidth * 2 - this.gutter,
30984             y : minY
30985         });
30986         
30987         positions.push({
30988             x : maxX - this.unitWidth,
30989             y : minY + (this.unitWidth + this.gutter) * 2
30990         });
30991         
30992         positions.push({
30993             x : maxX - this.unitWidth * 3 - this.gutter * 2,
30994             y : minY
30995         });
30996         
30997         Roo.each(eItems, function(b,k){
30998             
30999             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31000
31001         }, this);
31002         
31003     },
31004     
31005     getVerticalOneBoxColPositions : function(x, y, box)
31006     {
31007         var pos = [];
31008         
31009         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31010         
31011         if(box[0].size == 'md-left'){
31012             rand = 0;
31013         }
31014         
31015         if(box[0].size == 'md-right'){
31016             rand = 1;
31017         }
31018         
31019         pos.push({
31020             x : x + (this.unitWidth + this.gutter) * rand,
31021             y : y
31022         });
31023         
31024         return pos;
31025     },
31026     
31027     getVerticalTwoBoxColPositions : function(x, y, box)
31028     {
31029         var pos = [];
31030         
31031         if(box[0].size == 'xs'){
31032             
31033             pos.push({
31034                 x : x,
31035                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31036             });
31037
31038             pos.push({
31039                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31040                 y : y
31041             });
31042             
31043             return pos;
31044             
31045         }
31046         
31047         pos.push({
31048             x : x,
31049             y : y
31050         });
31051
31052         pos.push({
31053             x : x + (this.unitWidth + this.gutter) * 2,
31054             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31055         });
31056         
31057         return pos;
31058         
31059     },
31060     
31061     getVerticalThreeBoxColPositions : function(x, y, box)
31062     {
31063         var pos = [];
31064         
31065         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31066             
31067             pos.push({
31068                 x : x,
31069                 y : y
31070             });
31071
31072             pos.push({
31073                 x : x + (this.unitWidth + this.gutter) * 1,
31074                 y : y
31075             });
31076             
31077             pos.push({
31078                 x : x + (this.unitWidth + this.gutter) * 2,
31079                 y : y
31080             });
31081             
31082             return pos;
31083             
31084         }
31085         
31086         if(box[0].size == 'xs' && box[1].size == 'xs'){
31087             
31088             pos.push({
31089                 x : x,
31090                 y : y
31091             });
31092
31093             pos.push({
31094                 x : x,
31095                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31096             });
31097             
31098             pos.push({
31099                 x : x + (this.unitWidth + this.gutter) * 1,
31100                 y : y
31101             });
31102             
31103             return pos;
31104             
31105         }
31106         
31107         pos.push({
31108             x : x,
31109             y : y
31110         });
31111
31112         pos.push({
31113             x : x + (this.unitWidth + this.gutter) * 2,
31114             y : y
31115         });
31116
31117         pos.push({
31118             x : x + (this.unitWidth + this.gutter) * 2,
31119             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31120         });
31121             
31122         return pos;
31123         
31124     },
31125     
31126     getVerticalFourBoxColPositions : function(x, y, box)
31127     {
31128         var pos = [];
31129         
31130         if(box[0].size == 'xs'){
31131             
31132             pos.push({
31133                 x : x,
31134                 y : y
31135             });
31136
31137             pos.push({
31138                 x : x,
31139                 y : y + (this.unitHeight + this.gutter) * 1
31140             });
31141             
31142             pos.push({
31143                 x : x,
31144                 y : y + (this.unitHeight + this.gutter) * 2
31145             });
31146             
31147             pos.push({
31148                 x : x + (this.unitWidth + this.gutter) * 1,
31149                 y : y
31150             });
31151             
31152             return pos;
31153             
31154         }
31155         
31156         pos.push({
31157             x : x,
31158             y : y
31159         });
31160
31161         pos.push({
31162             x : x + (this.unitWidth + this.gutter) * 2,
31163             y : y
31164         });
31165
31166         pos.push({
31167             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31168             y : y + (this.unitHeight + this.gutter) * 1
31169         });
31170
31171         pos.push({
31172             x : x + (this.unitWidth + this.gutter) * 2,
31173             y : y + (this.unitWidth + this.gutter) * 2
31174         });
31175
31176         return pos;
31177         
31178     },
31179     
31180     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31181     {
31182         var pos = [];
31183         
31184         if(box[0].size == 'md-left'){
31185             pos.push({
31186                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31187                 y : minY
31188             });
31189             
31190             return pos;
31191         }
31192         
31193         if(box[0].size == 'md-right'){
31194             pos.push({
31195                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31196                 y : minY + (this.unitWidth + this.gutter) * 1
31197             });
31198             
31199             return pos;
31200         }
31201         
31202         var rand = Math.floor(Math.random() * (4 - box[0].y));
31203         
31204         pos.push({
31205             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31206             y : minY + (this.unitWidth + this.gutter) * rand
31207         });
31208         
31209         return pos;
31210         
31211     },
31212     
31213     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31214     {
31215         var pos = [];
31216         
31217         if(box[0].size == 'xs'){
31218             
31219             pos.push({
31220                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31221                 y : minY
31222             });
31223
31224             pos.push({
31225                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31226                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31227             });
31228             
31229             return pos;
31230             
31231         }
31232         
31233         pos.push({
31234             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31235             y : minY
31236         });
31237
31238         pos.push({
31239             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31240             y : minY + (this.unitWidth + this.gutter) * 2
31241         });
31242         
31243         return pos;
31244         
31245     },
31246     
31247     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31248     {
31249         var pos = [];
31250         
31251         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31252             
31253             pos.push({
31254                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31255                 y : minY
31256             });
31257
31258             pos.push({
31259                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31260                 y : minY + (this.unitWidth + this.gutter) * 1
31261             });
31262             
31263             pos.push({
31264                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31265                 y : minY + (this.unitWidth + this.gutter) * 2
31266             });
31267             
31268             return pos;
31269             
31270         }
31271         
31272         if(box[0].size == 'xs' && box[1].size == 'xs'){
31273             
31274             pos.push({
31275                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31276                 y : minY
31277             });
31278
31279             pos.push({
31280                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31281                 y : minY
31282             });
31283             
31284             pos.push({
31285                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31286                 y : minY + (this.unitWidth + this.gutter) * 1
31287             });
31288             
31289             return pos;
31290             
31291         }
31292         
31293         pos.push({
31294             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31295             y : minY
31296         });
31297
31298         pos.push({
31299             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31300             y : minY + (this.unitWidth + this.gutter) * 2
31301         });
31302
31303         pos.push({
31304             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31305             y : minY + (this.unitWidth + this.gutter) * 2
31306         });
31307             
31308         return pos;
31309         
31310     },
31311     
31312     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31313     {
31314         var pos = [];
31315         
31316         if(box[0].size == 'xs'){
31317             
31318             pos.push({
31319                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31320                 y : minY
31321             });
31322
31323             pos.push({
31324                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31325                 y : minY
31326             });
31327             
31328             pos.push({
31329                 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),
31330                 y : minY
31331             });
31332             
31333             pos.push({
31334                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31335                 y : minY + (this.unitWidth + this.gutter) * 1
31336             });
31337             
31338             return pos;
31339             
31340         }
31341         
31342         pos.push({
31343             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31344             y : minY
31345         });
31346         
31347         pos.push({
31348             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31349             y : minY + (this.unitWidth + this.gutter) * 2
31350         });
31351         
31352         pos.push({
31353             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31354             y : minY + (this.unitWidth + this.gutter) * 2
31355         });
31356         
31357         pos.push({
31358             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),
31359             y : minY + (this.unitWidth + this.gutter) * 2
31360         });
31361
31362         return pos;
31363         
31364     },
31365     
31366     /**
31367     * remove a Masonry Brick
31368     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31369     */
31370     removeBrick : function(brick_id)
31371     {
31372         if (!brick_id) {
31373             return;
31374         }
31375         
31376         for (var i = 0; i<this.bricks.length; i++) {
31377             if (this.bricks[i].id == brick_id) {
31378                 this.bricks.splice(i,1);
31379                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31380                 this.initial();
31381             }
31382         }
31383     },
31384     
31385     /**
31386     * adds a Masonry Brick
31387     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31388     */
31389     addBrick : function(cfg)
31390     {
31391         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31392         //this.register(cn);
31393         cn.parentId = this.id;
31394         cn.onRender(this.el, null);
31395         return cn;
31396     },
31397     
31398     /**
31399     * register a Masonry Brick
31400     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31401     */
31402     
31403     register : function(brick)
31404     {
31405         this.bricks.push(brick);
31406         brick.masonryId = this.id;
31407     },
31408     
31409     /**
31410     * clear all the Masonry Brick
31411     */
31412     clearAll : function()
31413     {
31414         this.bricks = [];
31415         //this.getChildContainer().dom.innerHTML = "";
31416         this.el.dom.innerHTML = '';
31417     },
31418     
31419     getSelected : function()
31420     {
31421         if (!this.selectedBrick) {
31422             return false;
31423         }
31424         
31425         return this.selectedBrick;
31426     }
31427 });
31428
31429 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31430     
31431     groups: {},
31432      /**
31433     * register a Masonry Layout
31434     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31435     */
31436     
31437     register : function(layout)
31438     {
31439         this.groups[layout.id] = layout;
31440     },
31441     /**
31442     * fetch a  Masonry Layout based on the masonry layout ID
31443     * @param {string} the masonry layout to add
31444     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31445     */
31446     
31447     get: function(layout_id) {
31448         if (typeof(this.groups[layout_id]) == 'undefined') {
31449             return false;
31450         }
31451         return this.groups[layout_id] ;
31452     }
31453     
31454     
31455     
31456 });
31457
31458  
31459
31460  /**
31461  *
31462  * This is based on 
31463  * http://masonry.desandro.com
31464  *
31465  * The idea is to render all the bricks based on vertical width...
31466  *
31467  * The original code extends 'outlayer' - we might need to use that....
31468  * 
31469  */
31470
31471
31472 /**
31473  * @class Roo.bootstrap.LayoutMasonryAuto
31474  * @extends Roo.bootstrap.Component
31475  * Bootstrap Layout Masonry class
31476  * 
31477  * @constructor
31478  * Create a new Element
31479  * @param {Object} config The config object
31480  */
31481
31482 Roo.bootstrap.LayoutMasonryAuto = function(config){
31483     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31484 };
31485
31486 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
31487     
31488       /**
31489      * @cfg {Boolean} isFitWidth  - resize the width..
31490      */   
31491     isFitWidth : false,  // options..
31492     /**
31493      * @cfg {Boolean} isOriginLeft = left align?
31494      */   
31495     isOriginLeft : true,
31496     /**
31497      * @cfg {Boolean} isOriginTop = top align?
31498      */   
31499     isOriginTop : false,
31500     /**
31501      * @cfg {Boolean} isLayoutInstant = no animation?
31502      */   
31503     isLayoutInstant : false, // needed?
31504     /**
31505      * @cfg {Boolean} isResizingContainer = not sure if this is used..
31506      */   
31507     isResizingContainer : true,
31508     /**
31509      * @cfg {Number} columnWidth  width of the columns 
31510      */   
31511     
31512     columnWidth : 0,
31513     
31514     /**
31515      * @cfg {Number} maxCols maximum number of columns
31516      */   
31517     
31518     maxCols: 0,
31519     /**
31520      * @cfg {Number} padHeight padding below box..
31521      */   
31522     
31523     padHeight : 10, 
31524     
31525     /**
31526      * @cfg {Boolean} isAutoInitial defalut true
31527      */   
31528     
31529     isAutoInitial : true, 
31530     
31531     // private?
31532     gutter : 0,
31533     
31534     containerWidth: 0,
31535     initialColumnWidth : 0,
31536     currentSize : null,
31537     
31538     colYs : null, // array.
31539     maxY : 0,
31540     padWidth: 10,
31541     
31542     
31543     tag: 'div',
31544     cls: '',
31545     bricks: null, //CompositeElement
31546     cols : 0, // array?
31547     // element : null, // wrapped now this.el
31548     _isLayoutInited : null, 
31549     
31550     
31551     getAutoCreate : function(){
31552         
31553         var cfg = {
31554             tag: this.tag,
31555             cls: 'blog-masonary-wrapper ' + this.cls,
31556             cn : {
31557                 cls : 'mas-boxes masonary'
31558             }
31559         };
31560         
31561         return cfg;
31562     },
31563     
31564     getChildContainer: function( )
31565     {
31566         if (this.boxesEl) {
31567             return this.boxesEl;
31568         }
31569         
31570         this.boxesEl = this.el.select('.mas-boxes').first();
31571         
31572         return this.boxesEl;
31573     },
31574     
31575     
31576     initEvents : function()
31577     {
31578         var _this = this;
31579         
31580         if(this.isAutoInitial){
31581             Roo.log('hook children rendered');
31582             this.on('childrenrendered', function() {
31583                 Roo.log('children rendered');
31584                 _this.initial();
31585             } ,this);
31586         }
31587         
31588     },
31589     
31590     initial : function()
31591     {
31592         this.reloadItems();
31593
31594         this.currentSize = this.el.getBox(true);
31595
31596         /// was window resize... - let's see if this works..
31597         Roo.EventManager.onWindowResize(this.resize, this); 
31598
31599         if(!this.isAutoInitial){
31600             this.layout();
31601             return;
31602         }
31603         
31604         this.layout.defer(500,this);
31605     },
31606     
31607     reloadItems: function()
31608     {
31609         this.bricks = this.el.select('.masonry-brick', true);
31610         
31611         this.bricks.each(function(b) {
31612             //Roo.log(b.getSize());
31613             if (!b.attr('originalwidth')) {
31614                 b.attr('originalwidth',  b.getSize().width);
31615             }
31616             
31617         });
31618         
31619         Roo.log(this.bricks.elements.length);
31620     },
31621     
31622     resize : function()
31623     {
31624         Roo.log('resize');
31625         var cs = this.el.getBox(true);
31626         
31627         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
31628             Roo.log("no change in with or X");
31629             return;
31630         }
31631         this.currentSize = cs;
31632         this.layout();
31633     },
31634     
31635     layout : function()
31636     {
31637          Roo.log('layout');
31638         this._resetLayout();
31639         //this._manageStamps();
31640       
31641         // don't animate first layout
31642         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31643         this.layoutItems( isInstant );
31644       
31645         // flag for initalized
31646         this._isLayoutInited = true;
31647     },
31648     
31649     layoutItems : function( isInstant )
31650     {
31651         //var items = this._getItemsForLayout( this.items );
31652         // original code supports filtering layout items.. we just ignore it..
31653         
31654         this._layoutItems( this.bricks , isInstant );
31655       
31656         this._postLayout();
31657     },
31658     _layoutItems : function ( items , isInstant)
31659     {
31660        //this.fireEvent( 'layout', this, items );
31661     
31662
31663         if ( !items || !items.elements.length ) {
31664           // no items, emit event with empty array
31665             return;
31666         }
31667
31668         var queue = [];
31669         items.each(function(item) {
31670             Roo.log("layout item");
31671             Roo.log(item);
31672             // get x/y object from method
31673             var position = this._getItemLayoutPosition( item );
31674             // enqueue
31675             position.item = item;
31676             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
31677             queue.push( position );
31678         }, this);
31679       
31680         this._processLayoutQueue( queue );
31681     },
31682     /** Sets position of item in DOM
31683     * @param {Element} item
31684     * @param {Number} x - horizontal position
31685     * @param {Number} y - vertical position
31686     * @param {Boolean} isInstant - disables transitions
31687     */
31688     _processLayoutQueue : function( queue )
31689     {
31690         for ( var i=0, len = queue.length; i < len; i++ ) {
31691             var obj = queue[i];
31692             obj.item.position('absolute');
31693             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
31694         }
31695     },
31696       
31697     
31698     /**
31699     * Any logic you want to do after each layout,
31700     * i.e. size the container
31701     */
31702     _postLayout : function()
31703     {
31704         this.resizeContainer();
31705     },
31706     
31707     resizeContainer : function()
31708     {
31709         if ( !this.isResizingContainer ) {
31710             return;
31711         }
31712         var size = this._getContainerSize();
31713         if ( size ) {
31714             this.el.setSize(size.width,size.height);
31715             this.boxesEl.setSize(size.width,size.height);
31716         }
31717     },
31718     
31719     
31720     
31721     _resetLayout : function()
31722     {
31723         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
31724         this.colWidth = this.el.getWidth();
31725         //this.gutter = this.el.getWidth(); 
31726         
31727         this.measureColumns();
31728
31729         // reset column Y
31730         var i = this.cols;
31731         this.colYs = [];
31732         while (i--) {
31733             this.colYs.push( 0 );
31734         }
31735     
31736         this.maxY = 0;
31737     },
31738
31739     measureColumns : function()
31740     {
31741         this.getContainerWidth();
31742       // if columnWidth is 0, default to outerWidth of first item
31743         if ( !this.columnWidth ) {
31744             var firstItem = this.bricks.first();
31745             Roo.log(firstItem);
31746             this.columnWidth  = this.containerWidth;
31747             if (firstItem && firstItem.attr('originalwidth') ) {
31748                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
31749             }
31750             // columnWidth fall back to item of first element
31751             Roo.log("set column width?");
31752                         this.initialColumnWidth = this.columnWidth  ;
31753
31754             // if first elem has no width, default to size of container
31755             
31756         }
31757         
31758         
31759         if (this.initialColumnWidth) {
31760             this.columnWidth = this.initialColumnWidth;
31761         }
31762         
31763         
31764             
31765         // column width is fixed at the top - however if container width get's smaller we should
31766         // reduce it...
31767         
31768         // this bit calcs how man columns..
31769             
31770         var columnWidth = this.columnWidth += this.gutter;
31771       
31772         // calculate columns
31773         var containerWidth = this.containerWidth + this.gutter;
31774         
31775         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
31776         // fix rounding errors, typically with gutters
31777         var excess = columnWidth - containerWidth % columnWidth;
31778         
31779         
31780         // if overshoot is less than a pixel, round up, otherwise floor it
31781         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
31782         cols = Math[ mathMethod ]( cols );
31783         this.cols = Math.max( cols, 1 );
31784         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31785         
31786          // padding positioning..
31787         var totalColWidth = this.cols * this.columnWidth;
31788         var padavail = this.containerWidth - totalColWidth;
31789         // so for 2 columns - we need 3 'pads'
31790         
31791         var padNeeded = (1+this.cols) * this.padWidth;
31792         
31793         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
31794         
31795         this.columnWidth += padExtra
31796         //this.padWidth = Math.floor(padavail /  ( this.cols));
31797         
31798         // adjust colum width so that padding is fixed??
31799         
31800         // we have 3 columns ... total = width * 3
31801         // we have X left over... that should be used by 
31802         
31803         //if (this.expandC) {
31804             
31805         //}
31806         
31807         
31808         
31809     },
31810     
31811     getContainerWidth : function()
31812     {
31813        /* // container is parent if fit width
31814         var container = this.isFitWidth ? this.element.parentNode : this.element;
31815         // check that this.size and size are there
31816         // IE8 triggers resize on body size change, so they might not be
31817         
31818         var size = getSize( container );  //FIXME
31819         this.containerWidth = size && size.innerWidth; //FIXME
31820         */
31821          
31822         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31823         
31824     },
31825     
31826     _getItemLayoutPosition : function( item )  // what is item?
31827     {
31828         // we resize the item to our columnWidth..
31829       
31830         item.setWidth(this.columnWidth);
31831         item.autoBoxAdjust  = false;
31832         
31833         var sz = item.getSize();
31834  
31835         // how many columns does this brick span
31836         var remainder = this.containerWidth % this.columnWidth;
31837         
31838         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
31839         // round if off by 1 pixel, otherwise use ceil
31840         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
31841         colSpan = Math.min( colSpan, this.cols );
31842         
31843         // normally this should be '1' as we dont' currently allow multi width columns..
31844         
31845         var colGroup = this._getColGroup( colSpan );
31846         // get the minimum Y value from the columns
31847         var minimumY = Math.min.apply( Math, colGroup );
31848         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
31849         
31850         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
31851          
31852         // position the brick
31853         var position = {
31854             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
31855             y: this.currentSize.y + minimumY + this.padHeight
31856         };
31857         
31858         Roo.log(position);
31859         // apply setHeight to necessary columns
31860         var setHeight = minimumY + sz.height + this.padHeight;
31861         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
31862         
31863         var setSpan = this.cols + 1 - colGroup.length;
31864         for ( var i = 0; i < setSpan; i++ ) {
31865           this.colYs[ shortColIndex + i ] = setHeight ;
31866         }
31867       
31868         return position;
31869     },
31870     
31871     /**
31872      * @param {Number} colSpan - number of columns the element spans
31873      * @returns {Array} colGroup
31874      */
31875     _getColGroup : function( colSpan )
31876     {
31877         if ( colSpan < 2 ) {
31878           // if brick spans only one column, use all the column Ys
31879           return this.colYs;
31880         }
31881       
31882         var colGroup = [];
31883         // how many different places could this brick fit horizontally
31884         var groupCount = this.cols + 1 - colSpan;
31885         // for each group potential horizontal position
31886         for ( var i = 0; i < groupCount; i++ ) {
31887           // make an array of colY values for that one group
31888           var groupColYs = this.colYs.slice( i, i + colSpan );
31889           // and get the max value of the array
31890           colGroup[i] = Math.max.apply( Math, groupColYs );
31891         }
31892         return colGroup;
31893     },
31894     /*
31895     _manageStamp : function( stamp )
31896     {
31897         var stampSize =  stamp.getSize();
31898         var offset = stamp.getBox();
31899         // get the columns that this stamp affects
31900         var firstX = this.isOriginLeft ? offset.x : offset.right;
31901         var lastX = firstX + stampSize.width;
31902         var firstCol = Math.floor( firstX / this.columnWidth );
31903         firstCol = Math.max( 0, firstCol );
31904         
31905         var lastCol = Math.floor( lastX / this.columnWidth );
31906         // lastCol should not go over if multiple of columnWidth #425
31907         lastCol -= lastX % this.columnWidth ? 0 : 1;
31908         lastCol = Math.min( this.cols - 1, lastCol );
31909         
31910         // set colYs to bottom of the stamp
31911         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
31912             stampSize.height;
31913             
31914         for ( var i = firstCol; i <= lastCol; i++ ) {
31915           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
31916         }
31917     },
31918     */
31919     
31920     _getContainerSize : function()
31921     {
31922         this.maxY = Math.max.apply( Math, this.colYs );
31923         var size = {
31924             height: this.maxY
31925         };
31926       
31927         if ( this.isFitWidth ) {
31928             size.width = this._getContainerFitWidth();
31929         }
31930       
31931         return size;
31932     },
31933     
31934     _getContainerFitWidth : function()
31935     {
31936         var unusedCols = 0;
31937         // count unused columns
31938         var i = this.cols;
31939         while ( --i ) {
31940           if ( this.colYs[i] !== 0 ) {
31941             break;
31942           }
31943           unusedCols++;
31944         }
31945         // fit container to columns that have been used
31946         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
31947     },
31948     
31949     needsResizeLayout : function()
31950     {
31951         var previousWidth = this.containerWidth;
31952         this.getContainerWidth();
31953         return previousWidth !== this.containerWidth;
31954     }
31955  
31956 });
31957
31958  
31959
31960  /*
31961  * - LGPL
31962  *
31963  * element
31964  * 
31965  */
31966
31967 /**
31968  * @class Roo.bootstrap.MasonryBrick
31969  * @extends Roo.bootstrap.Component
31970  * Bootstrap MasonryBrick class
31971  * 
31972  * @constructor
31973  * Create a new MasonryBrick
31974  * @param {Object} config The config object
31975  */
31976
31977 Roo.bootstrap.MasonryBrick = function(config){
31978     
31979     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
31980     
31981     Roo.bootstrap.MasonryBrick.register(this);
31982     
31983     this.addEvents({
31984         // raw events
31985         /**
31986          * @event click
31987          * When a MasonryBrick is clcik
31988          * @param {Roo.bootstrap.MasonryBrick} this
31989          * @param {Roo.EventObject} e
31990          */
31991         "click" : true
31992     });
31993 };
31994
31995 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
31996     
31997     /**
31998      * @cfg {String} title
31999      */   
32000     title : '',
32001     /**
32002      * @cfg {String} html
32003      */   
32004     html : '',
32005     /**
32006      * @cfg {String} bgimage
32007      */   
32008     bgimage : '',
32009     /**
32010      * @cfg {String} videourl
32011      */   
32012     videourl : '',
32013     /**
32014      * @cfg {String} cls
32015      */   
32016     cls : '',
32017     /**
32018      * @cfg {String} href
32019      */   
32020     href : '',
32021     /**
32022      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32023      */   
32024     size : 'xs',
32025     
32026     /**
32027      * @cfg {String} placetitle (center|bottom)
32028      */   
32029     placetitle : '',
32030     
32031     /**
32032      * @cfg {Boolean} isFitContainer defalut true
32033      */   
32034     isFitContainer : true, 
32035     
32036     /**
32037      * @cfg {Boolean} preventDefault defalut false
32038      */   
32039     preventDefault : false, 
32040     
32041     /**
32042      * @cfg {Boolean} inverse defalut false
32043      */   
32044     maskInverse : false, 
32045     
32046     getAutoCreate : function()
32047     {
32048         if(!this.isFitContainer){
32049             return this.getSplitAutoCreate();
32050         }
32051         
32052         var cls = 'masonry-brick masonry-brick-full';
32053         
32054         if(this.href.length){
32055             cls += ' masonry-brick-link';
32056         }
32057         
32058         if(this.bgimage.length){
32059             cls += ' masonry-brick-image';
32060         }
32061         
32062         if(this.maskInverse){
32063             cls += ' mask-inverse';
32064         }
32065         
32066         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32067             cls += ' enable-mask';
32068         }
32069         
32070         if(this.size){
32071             cls += ' masonry-' + this.size + '-brick';
32072         }
32073         
32074         if(this.placetitle.length){
32075             
32076             switch (this.placetitle) {
32077                 case 'center' :
32078                     cls += ' masonry-center-title';
32079                     break;
32080                 case 'bottom' :
32081                     cls += ' masonry-bottom-title';
32082                     break;
32083                 default:
32084                     break;
32085             }
32086             
32087         } else {
32088             if(!this.html.length && !this.bgimage.length){
32089                 cls += ' masonry-center-title';
32090             }
32091
32092             if(!this.html.length && this.bgimage.length){
32093                 cls += ' masonry-bottom-title';
32094             }
32095         }
32096         
32097         if(this.cls){
32098             cls += ' ' + this.cls;
32099         }
32100         
32101         var cfg = {
32102             tag: (this.href.length) ? 'a' : 'div',
32103             cls: cls,
32104             cn: [
32105                 {
32106                     tag: 'div',
32107                     cls: 'masonry-brick-mask'
32108                 },
32109                 {
32110                     tag: 'div',
32111                     cls: 'masonry-brick-paragraph',
32112                     cn: []
32113                 }
32114             ]
32115         };
32116         
32117         if(this.href.length){
32118             cfg.href = this.href;
32119         }
32120         
32121         var cn = cfg.cn[1].cn;
32122         
32123         if(this.title.length){
32124             cn.push({
32125                 tag: 'h4',
32126                 cls: 'masonry-brick-title',
32127                 html: this.title
32128             });
32129         }
32130         
32131         if(this.html.length){
32132             cn.push({
32133                 tag: 'p',
32134                 cls: 'masonry-brick-text',
32135                 html: this.html
32136             });
32137         }
32138         
32139         if (!this.title.length && !this.html.length) {
32140             cfg.cn[1].cls += ' hide';
32141         }
32142         
32143         if(this.bgimage.length){
32144             cfg.cn.push({
32145                 tag: 'img',
32146                 cls: 'masonry-brick-image-view',
32147                 src: this.bgimage
32148             });
32149         }
32150         
32151         if(this.videourl.length){
32152             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32153             // youtube support only?
32154             cfg.cn.push({
32155                 tag: 'iframe',
32156                 cls: 'masonry-brick-image-view',
32157                 src: vurl,
32158                 frameborder : 0,
32159                 allowfullscreen : true
32160             });
32161         }
32162         
32163         return cfg;
32164         
32165     },
32166     
32167     getSplitAutoCreate : function()
32168     {
32169         var cls = 'masonry-brick masonry-brick-split';
32170         
32171         if(this.href.length){
32172             cls += ' masonry-brick-link';
32173         }
32174         
32175         if(this.bgimage.length){
32176             cls += ' masonry-brick-image';
32177         }
32178         
32179         if(this.size){
32180             cls += ' masonry-' + this.size + '-brick';
32181         }
32182         
32183         switch (this.placetitle) {
32184             case 'center' :
32185                 cls += ' masonry-center-title';
32186                 break;
32187             case 'bottom' :
32188                 cls += ' masonry-bottom-title';
32189                 break;
32190             default:
32191                 if(!this.bgimage.length){
32192                     cls += ' masonry-center-title';
32193                 }
32194
32195                 if(this.bgimage.length){
32196                     cls += ' masonry-bottom-title';
32197                 }
32198                 break;
32199         }
32200         
32201         if(this.cls){
32202             cls += ' ' + this.cls;
32203         }
32204         
32205         var cfg = {
32206             tag: (this.href.length) ? 'a' : 'div',
32207             cls: cls,
32208             cn: [
32209                 {
32210                     tag: 'div',
32211                     cls: 'masonry-brick-split-head',
32212                     cn: [
32213                         {
32214                             tag: 'div',
32215                             cls: 'masonry-brick-paragraph',
32216                             cn: []
32217                         }
32218                     ]
32219                 },
32220                 {
32221                     tag: 'div',
32222                     cls: 'masonry-brick-split-body',
32223                     cn: []
32224                 }
32225             ]
32226         };
32227         
32228         if(this.href.length){
32229             cfg.href = this.href;
32230         }
32231         
32232         if(this.title.length){
32233             cfg.cn[0].cn[0].cn.push({
32234                 tag: 'h4',
32235                 cls: 'masonry-brick-title',
32236                 html: this.title
32237             });
32238         }
32239         
32240         if(this.html.length){
32241             cfg.cn[1].cn.push({
32242                 tag: 'p',
32243                 cls: 'masonry-brick-text',
32244                 html: this.html
32245             });
32246         }
32247
32248         if(this.bgimage.length){
32249             cfg.cn[0].cn.push({
32250                 tag: 'img',
32251                 cls: 'masonry-brick-image-view',
32252                 src: this.bgimage
32253             });
32254         }
32255         
32256         if(this.videourl.length){
32257             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32258             // youtube support only?
32259             cfg.cn[0].cn.cn.push({
32260                 tag: 'iframe',
32261                 cls: 'masonry-brick-image-view',
32262                 src: vurl,
32263                 frameborder : 0,
32264                 allowfullscreen : true
32265             });
32266         }
32267         
32268         return cfg;
32269     },
32270     
32271     initEvents: function() 
32272     {
32273         switch (this.size) {
32274             case 'xs' :
32275                 this.x = 1;
32276                 this.y = 1;
32277                 break;
32278             case 'sm' :
32279                 this.x = 2;
32280                 this.y = 2;
32281                 break;
32282             case 'md' :
32283             case 'md-left' :
32284             case 'md-right' :
32285                 this.x = 3;
32286                 this.y = 3;
32287                 break;
32288             case 'tall' :
32289                 this.x = 2;
32290                 this.y = 3;
32291                 break;
32292             case 'wide' :
32293                 this.x = 3;
32294                 this.y = 2;
32295                 break;
32296             case 'wide-thin' :
32297                 this.x = 3;
32298                 this.y = 1;
32299                 break;
32300                         
32301             default :
32302                 break;
32303         }
32304         
32305         if(Roo.isTouch){
32306             this.el.on('touchstart', this.onTouchStart, this);
32307             this.el.on('touchmove', this.onTouchMove, this);
32308             this.el.on('touchend', this.onTouchEnd, this);
32309             this.el.on('contextmenu', this.onContextMenu, this);
32310         } else {
32311             this.el.on('mouseenter'  ,this.enter, this);
32312             this.el.on('mouseleave', this.leave, this);
32313             this.el.on('click', this.onClick, this);
32314         }
32315         
32316         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32317             this.parent().bricks.push(this);   
32318         }
32319         
32320     },
32321     
32322     onClick: function(e, el)
32323     {
32324         var time = this.endTimer - this.startTimer;
32325         // Roo.log(e.preventDefault());
32326         if(Roo.isTouch){
32327             if(time > 1000){
32328                 e.preventDefault();
32329                 return;
32330             }
32331         }
32332         
32333         if(!this.preventDefault){
32334             return;
32335         }
32336         
32337         e.preventDefault();
32338         
32339         if (this.activcClass != '') {
32340             this.selectBrick();
32341         }
32342         
32343         this.fireEvent('click', this);
32344     },
32345     
32346     enter: function(e, el)
32347     {
32348         e.preventDefault();
32349         
32350         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32351             return;
32352         }
32353         
32354         if(this.bgimage.length && this.html.length){
32355             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32356         }
32357     },
32358     
32359     leave: function(e, el)
32360     {
32361         e.preventDefault();
32362         
32363         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32364             return;
32365         }
32366         
32367         if(this.bgimage.length && this.html.length){
32368             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32369         }
32370     },
32371     
32372     onTouchStart: function(e, el)
32373     {
32374 //        e.preventDefault();
32375         
32376         this.touchmoved = false;
32377         
32378         if(!this.isFitContainer){
32379             return;
32380         }
32381         
32382         if(!this.bgimage.length || !this.html.length){
32383             return;
32384         }
32385         
32386         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32387         
32388         this.timer = new Date().getTime();
32389         
32390     },
32391     
32392     onTouchMove: function(e, el)
32393     {
32394         this.touchmoved = true;
32395     },
32396     
32397     onContextMenu : function(e,el)
32398     {
32399         e.preventDefault();
32400         e.stopPropagation();
32401         return false;
32402     },
32403     
32404     onTouchEnd: function(e, el)
32405     {
32406 //        e.preventDefault();
32407         
32408         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32409         
32410             this.leave(e,el);
32411             
32412             return;
32413         }
32414         
32415         if(!this.bgimage.length || !this.html.length){
32416             
32417             if(this.href.length){
32418                 window.location.href = this.href;
32419             }
32420             
32421             return;
32422         }
32423         
32424         if(!this.isFitContainer){
32425             return;
32426         }
32427         
32428         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32429         
32430         window.location.href = this.href;
32431     },
32432     
32433     //selection on single brick only
32434     selectBrick : function() {
32435         
32436         if (!this.parentId) {
32437             return;
32438         }
32439         
32440         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32441         var index = m.selectedBrick.indexOf(this.id);
32442         
32443         if ( index > -1) {
32444             m.selectedBrick.splice(index,1);
32445             this.el.removeClass(this.activeClass);
32446             return;
32447         }
32448         
32449         for(var i = 0; i < m.selectedBrick.length; i++) {
32450             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32451             b.el.removeClass(b.activeClass);
32452         }
32453         
32454         m.selectedBrick = [];
32455         
32456         m.selectedBrick.push(this.id);
32457         this.el.addClass(this.activeClass);
32458         return;
32459     }
32460     
32461 });
32462
32463 Roo.apply(Roo.bootstrap.MasonryBrick, {
32464     
32465     //groups: {},
32466     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32467      /**
32468     * register a Masonry Brick
32469     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32470     */
32471     
32472     register : function(brick)
32473     {
32474         //this.groups[brick.id] = brick;
32475         this.groups.add(brick.id, brick);
32476     },
32477     /**
32478     * fetch a  masonry brick based on the masonry brick ID
32479     * @param {string} the masonry brick to add
32480     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32481     */
32482     
32483     get: function(brick_id) 
32484     {
32485         // if (typeof(this.groups[brick_id]) == 'undefined') {
32486         //     return false;
32487         // }
32488         // return this.groups[brick_id] ;
32489         
32490         if(this.groups.key(brick_id)) {
32491             return this.groups.key(brick_id);
32492         }
32493         
32494         return false;
32495     }
32496     
32497     
32498     
32499 });
32500
32501  /*
32502  * - LGPL
32503  *
32504  * element
32505  * 
32506  */
32507
32508 /**
32509  * @class Roo.bootstrap.Brick
32510  * @extends Roo.bootstrap.Component
32511  * Bootstrap Brick class
32512  * 
32513  * @constructor
32514  * Create a new Brick
32515  * @param {Object} config The config object
32516  */
32517
32518 Roo.bootstrap.Brick = function(config){
32519     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
32520     
32521     this.addEvents({
32522         // raw events
32523         /**
32524          * @event click
32525          * When a Brick is click
32526          * @param {Roo.bootstrap.Brick} this
32527          * @param {Roo.EventObject} e
32528          */
32529         "click" : true
32530     });
32531 };
32532
32533 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
32534     
32535     /**
32536      * @cfg {String} title
32537      */   
32538     title : '',
32539     /**
32540      * @cfg {String} html
32541      */   
32542     html : '',
32543     /**
32544      * @cfg {String} bgimage
32545      */   
32546     bgimage : '',
32547     /**
32548      * @cfg {String} cls
32549      */   
32550     cls : '',
32551     /**
32552      * @cfg {String} href
32553      */   
32554     href : '',
32555     /**
32556      * @cfg {String} video
32557      */   
32558     video : '',
32559     /**
32560      * @cfg {Boolean} square
32561      */   
32562     square : true,
32563     
32564     getAutoCreate : function()
32565     {
32566         var cls = 'roo-brick';
32567         
32568         if(this.href.length){
32569             cls += ' roo-brick-link';
32570         }
32571         
32572         if(this.bgimage.length){
32573             cls += ' roo-brick-image';
32574         }
32575         
32576         if(!this.html.length && !this.bgimage.length){
32577             cls += ' roo-brick-center-title';
32578         }
32579         
32580         if(!this.html.length && this.bgimage.length){
32581             cls += ' roo-brick-bottom-title';
32582         }
32583         
32584         if(this.cls){
32585             cls += ' ' + this.cls;
32586         }
32587         
32588         var cfg = {
32589             tag: (this.href.length) ? 'a' : 'div',
32590             cls: cls,
32591             cn: [
32592                 {
32593                     tag: 'div',
32594                     cls: 'roo-brick-paragraph',
32595                     cn: []
32596                 }
32597             ]
32598         };
32599         
32600         if(this.href.length){
32601             cfg.href = this.href;
32602         }
32603         
32604         var cn = cfg.cn[0].cn;
32605         
32606         if(this.title.length){
32607             cn.push({
32608                 tag: 'h4',
32609                 cls: 'roo-brick-title',
32610                 html: this.title
32611             });
32612         }
32613         
32614         if(this.html.length){
32615             cn.push({
32616                 tag: 'p',
32617                 cls: 'roo-brick-text',
32618                 html: this.html
32619             });
32620         } else {
32621             cn.cls += ' hide';
32622         }
32623         
32624         if(this.bgimage.length){
32625             cfg.cn.push({
32626                 tag: 'img',
32627                 cls: 'roo-brick-image-view',
32628                 src: this.bgimage
32629             });
32630         }
32631         
32632         return cfg;
32633     },
32634     
32635     initEvents: function() 
32636     {
32637         if(this.title.length || this.html.length){
32638             this.el.on('mouseenter'  ,this.enter, this);
32639             this.el.on('mouseleave', this.leave, this);
32640         }
32641         
32642         Roo.EventManager.onWindowResize(this.resize, this); 
32643         
32644         if(this.bgimage.length){
32645             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
32646             this.imageEl.on('load', this.onImageLoad, this);
32647             return;
32648         }
32649         
32650         this.resize();
32651     },
32652     
32653     onImageLoad : function()
32654     {
32655         this.resize();
32656     },
32657     
32658     resize : function()
32659     {
32660         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
32661         
32662         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
32663         
32664         if(this.bgimage.length){
32665             var image = this.el.select('.roo-brick-image-view', true).first();
32666             
32667             image.setWidth(paragraph.getWidth());
32668             
32669             if(this.square){
32670                 image.setHeight(paragraph.getWidth());
32671             }
32672             
32673             this.el.setHeight(image.getHeight());
32674             paragraph.setHeight(image.getHeight());
32675             
32676         }
32677         
32678     },
32679     
32680     enter: function(e, el)
32681     {
32682         e.preventDefault();
32683         
32684         if(this.bgimage.length){
32685             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
32686             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
32687         }
32688     },
32689     
32690     leave: function(e, el)
32691     {
32692         e.preventDefault();
32693         
32694         if(this.bgimage.length){
32695             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
32696             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
32697         }
32698     }
32699     
32700 });
32701
32702  
32703
32704  /*
32705  * - LGPL
32706  *
32707  * Input
32708  * 
32709  */
32710
32711 /**
32712  * @class Roo.bootstrap.NumberField
32713  * @extends Roo.bootstrap.Input
32714  * Bootstrap NumberField class
32715  * 
32716  * 
32717  * 
32718  * 
32719  * @constructor
32720  * Create a new NumberField
32721  * @param {Object} config The config object
32722  */
32723
32724 Roo.bootstrap.NumberField = function(config){
32725     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
32726 };
32727
32728 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
32729     
32730     /**
32731      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
32732      */
32733     allowDecimals : true,
32734     /**
32735      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
32736      */
32737     decimalSeparator : ".",
32738     /**
32739      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
32740      */
32741     decimalPrecision : 2,
32742     /**
32743      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
32744      */
32745     allowNegative : true,
32746     /**
32747      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
32748      */
32749     minValue : Number.NEGATIVE_INFINITY,
32750     /**
32751      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
32752      */
32753     maxValue : Number.MAX_VALUE,
32754     /**
32755      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
32756      */
32757     minText : "The minimum value for this field is {0}",
32758     /**
32759      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
32760      */
32761     maxText : "The maximum value for this field is {0}",
32762     /**
32763      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
32764      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
32765      */
32766     nanText : "{0} is not a valid number",
32767     /**
32768      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
32769      */
32770     castInt : true,
32771
32772     // private
32773     initEvents : function()
32774     {   
32775         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
32776         
32777         var allowed = "0123456789";
32778         
32779         if(this.allowDecimals){
32780             allowed += this.decimalSeparator;
32781         }
32782         
32783         if(this.allowNegative){
32784             allowed += "-";
32785         }
32786         
32787         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
32788         
32789         var keyPress = function(e){
32790             
32791             var k = e.getKey();
32792             
32793             var c = e.getCharCode();
32794             
32795             if(
32796                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
32797                     allowed.indexOf(String.fromCharCode(c)) === -1
32798             ){
32799                 e.stopEvent();
32800                 return;
32801             }
32802             
32803             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
32804                 return;
32805             }
32806             
32807             if(allowed.indexOf(String.fromCharCode(c)) === -1){
32808                 e.stopEvent();
32809             }
32810         };
32811         
32812         this.el.on("keypress", keyPress, this);
32813     },
32814     
32815     validateValue : function(value)
32816     {
32817         
32818         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
32819             return false;
32820         }
32821         
32822         var num = this.parseValue(value);
32823         
32824         if(isNaN(num)){
32825             this.markInvalid(String.format(this.nanText, value));
32826             return false;
32827         }
32828         
32829         if(num < this.minValue){
32830             this.markInvalid(String.format(this.minText, this.minValue));
32831             return false;
32832         }
32833         
32834         if(num > this.maxValue){
32835             this.markInvalid(String.format(this.maxText, this.maxValue));
32836             return false;
32837         }
32838         
32839         return true;
32840     },
32841
32842     getValue : function()
32843     {
32844         return this.fixPrecision(this.parseValue(Roo.bootstrap.NumberField.superclass.getValue.call(this)));
32845     },
32846
32847     parseValue : function(value)
32848     {
32849         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
32850         return isNaN(value) ? '' : value;
32851     },
32852
32853     fixPrecision : function(value)
32854     {
32855         var nan = isNaN(value);
32856         
32857         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
32858             return nan ? '' : value;
32859         }
32860         return parseFloat(value).toFixed(this.decimalPrecision);
32861     },
32862
32863     setValue : function(v)
32864     {
32865         v = this.fixPrecision(v);
32866         Roo.bootstrap.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
32867     },
32868
32869     decimalPrecisionFcn : function(v)
32870     {
32871         return Math.floor(v);
32872     },
32873
32874     beforeBlur : function()
32875     {
32876         if(!this.castInt){
32877             return;
32878         }
32879         
32880         var v = this.parseValue(this.getRawValue());
32881         if(v){
32882             this.setValue(v);
32883         }
32884     }
32885     
32886 });
32887
32888  
32889
32890 /*
32891 * Licence: LGPL
32892 */
32893
32894 /**
32895  * @class Roo.bootstrap.DocumentSlider
32896  * @extends Roo.bootstrap.Component
32897  * Bootstrap DocumentSlider class
32898  * 
32899  * @constructor
32900  * Create a new DocumentViewer
32901  * @param {Object} config The config object
32902  */
32903
32904 Roo.bootstrap.DocumentSlider = function(config){
32905     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
32906     
32907     this.files = [];
32908     
32909     this.addEvents({
32910         /**
32911          * @event initial
32912          * Fire after initEvent
32913          * @param {Roo.bootstrap.DocumentSlider} this
32914          */
32915         "initial" : true,
32916         /**
32917          * @event update
32918          * Fire after update
32919          * @param {Roo.bootstrap.DocumentSlider} this
32920          */
32921         "update" : true,
32922         /**
32923          * @event click
32924          * Fire after click
32925          * @param {Roo.bootstrap.DocumentSlider} this
32926          */
32927         "click" : true
32928     });
32929 };
32930
32931 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
32932     
32933     files : false,
32934     
32935     indicator : 0,
32936     
32937     getAutoCreate : function()
32938     {
32939         var cfg = {
32940             tag : 'div',
32941             cls : 'roo-document-slider',
32942             cn : [
32943                 {
32944                     tag : 'div',
32945                     cls : 'roo-document-slider-header',
32946                     cn : [
32947                         {
32948                             tag : 'div',
32949                             cls : 'roo-document-slider-header-title'
32950                         }
32951                     ]
32952                 },
32953                 {
32954                     tag : 'div',
32955                     cls : 'roo-document-slider-body',
32956                     cn : [
32957                         {
32958                             tag : 'div',
32959                             cls : 'roo-document-slider-prev',
32960                             cn : [
32961                                 {
32962                                     tag : 'i',
32963                                     cls : 'fa fa-chevron-left'
32964                                 }
32965                             ]
32966                         },
32967                         {
32968                             tag : 'div',
32969                             cls : 'roo-document-slider-thumb',
32970                             cn : [
32971                                 {
32972                                     tag : 'img',
32973                                     cls : 'roo-document-slider-image'
32974                                 }
32975                             ]
32976                         },
32977                         {
32978                             tag : 'div',
32979                             cls : 'roo-document-slider-next',
32980                             cn : [
32981                                 {
32982                                     tag : 'i',
32983                                     cls : 'fa fa-chevron-right'
32984                                 }
32985                             ]
32986                         }
32987                     ]
32988                 }
32989             ]
32990         };
32991         
32992         return cfg;
32993     },
32994     
32995     initEvents : function()
32996     {
32997         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
32998         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
32999         
33000         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33001         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33002         
33003         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33004         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33005         
33006         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33007         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33008         
33009         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33010         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33011         
33012         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33013         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33014         
33015         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33016         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33017         
33018         this.thumbEl.on('click', this.onClick, this);
33019         
33020         this.prevIndicator.on('click', this.prev, this);
33021         
33022         this.nextIndicator.on('click', this.next, this);
33023         
33024     },
33025     
33026     initial : function()
33027     {
33028         if(this.files.length){
33029             this.indicator = 1;
33030             this.update()
33031         }
33032         
33033         this.fireEvent('initial', this);
33034     },
33035     
33036     update : function()
33037     {
33038         this.imageEl.attr('src', this.files[this.indicator - 1]);
33039         
33040         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33041         
33042         this.prevIndicator.show();
33043         
33044         if(this.indicator == 1){
33045             this.prevIndicator.hide();
33046         }
33047         
33048         this.nextIndicator.show();
33049         
33050         if(this.indicator == this.files.length){
33051             this.nextIndicator.hide();
33052         }
33053         
33054         this.thumbEl.scrollTo('top');
33055         
33056         this.fireEvent('update', this);
33057     },
33058     
33059     onClick : function(e)
33060     {
33061         e.preventDefault();
33062         
33063         this.fireEvent('click', this);
33064     },
33065     
33066     prev : function(e)
33067     {
33068         e.preventDefault();
33069         
33070         this.indicator = Math.max(1, this.indicator - 1);
33071         
33072         this.update();
33073     },
33074     
33075     next : function(e)
33076     {
33077         e.preventDefault();
33078         
33079         this.indicator = Math.min(this.files.length, this.indicator + 1);
33080         
33081         this.update();
33082     }
33083 });
33084 /*
33085  * - LGPL
33086  *
33087  * RadioSet
33088  *
33089  *
33090  */
33091
33092 /**
33093  * @class Roo.bootstrap.RadioSet
33094  * @extends Roo.bootstrap.Input
33095  * Bootstrap RadioSet class
33096  * @cfg {String} indicatorpos (left|right) default left
33097  * @cfg {Boolean} inline (true|false) inline the element (default true)
33098  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33099  * @constructor
33100  * Create a new RadioSet
33101  * @param {Object} config The config object
33102  */
33103
33104 Roo.bootstrap.RadioSet = function(config){
33105     
33106     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33107     
33108     this.radioes = [];
33109     
33110     Roo.bootstrap.RadioSet.register(this);
33111     
33112     this.addEvents({
33113         /**
33114         * @event check
33115         * Fires when the element is checked or unchecked.
33116         * @param {Roo.bootstrap.RadioSet} this This radio
33117         * @param {Roo.bootstrap.Radio} item The checked item
33118         */
33119        check : true
33120     });
33121     
33122 };
33123
33124 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33125
33126     radioes : false,
33127     
33128     inline : true,
33129     
33130     weight : '',
33131     
33132     indicatorpos : 'left',
33133     
33134     getAutoCreate : function()
33135     {
33136         var label = {
33137             tag : 'label',
33138             cls : 'roo-radio-set-label',
33139             cn : [
33140                 {
33141                     tag : 'span',
33142                     html : this.fieldLabel
33143                 }
33144             ]
33145         };
33146         
33147         if(this.indicatorpos == 'left'){
33148             label.cn.unshift({
33149                 tag : 'i',
33150                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33151                 tooltip : 'This field is required'
33152             });
33153         } else {
33154             label.cn.push({
33155                 tag : 'i',
33156                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33157                 tooltip : 'This field is required'
33158             });
33159         }
33160         
33161         var items = {
33162             tag : 'div',
33163             cls : 'roo-radio-set-items'
33164         };
33165         
33166         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33167         
33168         if (align === 'left' && this.fieldLabel.length) {
33169             
33170             items = {
33171                 cls : "roo-radio-set-right", 
33172                 cn: [
33173                     items
33174                 ]
33175             };
33176             
33177             if(this.labelWidth > 12){
33178                 label.style = "width: " + this.labelWidth + 'px';
33179             }
33180             
33181             if(this.labelWidth < 13 && this.labelmd == 0){
33182                 this.labelmd = this.labelWidth;
33183             }
33184             
33185             if(this.labellg > 0){
33186                 label.cls += ' col-lg-' + this.labellg;
33187                 items.cls += ' col-lg-' + (12 - this.labellg);
33188             }
33189             
33190             if(this.labelmd > 0){
33191                 label.cls += ' col-md-' + this.labelmd;
33192                 items.cls += ' col-md-' + (12 - this.labelmd);
33193             }
33194             
33195             if(this.labelsm > 0){
33196                 label.cls += ' col-sm-' + this.labelsm;
33197                 items.cls += ' col-sm-' + (12 - this.labelsm);
33198             }
33199             
33200             if(this.labelxs > 0){
33201                 label.cls += ' col-xs-' + this.labelxs;
33202                 items.cls += ' col-xs-' + (12 - this.labelxs);
33203             }
33204         }
33205         
33206         var cfg = {
33207             tag : 'div',
33208             cls : 'roo-radio-set',
33209             cn : [
33210                 {
33211                     tag : 'input',
33212                     cls : 'roo-radio-set-input',
33213                     type : 'hidden',
33214                     name : this.name,
33215                     value : this.value ? this.value :  ''
33216                 },
33217                 label,
33218                 items
33219             ]
33220         };
33221         
33222         if(this.weight.length){
33223             cfg.cls += ' roo-radio-' + this.weight;
33224         }
33225         
33226         if(this.inline) {
33227             cfg.cls += ' roo-radio-set-inline';
33228         }
33229         
33230         return cfg;
33231         
33232     },
33233
33234     initEvents : function()
33235     {
33236         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33237         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33238         
33239         if(!this.fieldLabel.length){
33240             this.labelEl.hide();
33241         }
33242         
33243         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33244         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33245         
33246         this.indicatorEl().setVisibilityMode(Roo.Element.DISPLAY);
33247         this.indicatorEl().hide();
33248         
33249         this.originalValue = this.getValue();
33250         
33251     },
33252     
33253     inputEl: function ()
33254     {
33255         return this.el.select('.roo-radio-set-input', true).first();
33256     },
33257     
33258     getChildContainer : function()
33259     {
33260         return this.itemsEl;
33261     },
33262     
33263     register : function(item)
33264     {
33265         this.radioes.push(item);
33266         
33267     },
33268     
33269     validate : function()
33270     {   
33271         var valid = false;
33272         
33273         Roo.each(this.radioes, function(i){
33274             if(!i.checked){
33275                 return;
33276             }
33277             
33278             valid = true;
33279             return false;
33280         });
33281         
33282         if(this.allowBlank) {
33283             return true;
33284         }
33285         
33286         if(this.disabled || valid){
33287             this.markValid();
33288             return true;
33289         }
33290         
33291         this.markInvalid();
33292         return false;
33293         
33294     },
33295     
33296     markValid : function()
33297     {
33298         if(this.labelEl.isVisible(true)){
33299             this.indicatorEl().hide();
33300         }
33301         
33302         this.el.removeClass([this.invalidClass, this.validClass]);
33303         this.el.addClass(this.validClass);
33304         
33305         this.fireEvent('valid', this);
33306     },
33307     
33308     markInvalid : function(msg)
33309     {
33310         if(this.allowBlank || this.disabled){
33311             return;
33312         }
33313         
33314         if(this.labelEl.isVisible(true)){
33315             this.indicatorEl().show();
33316         }
33317         
33318         this.el.removeClass([this.invalidClass, this.validClass]);
33319         this.el.addClass(this.invalidClass);
33320         
33321         this.fireEvent('invalid', this, msg);
33322         
33323     },
33324     
33325     setValue : function(v, suppressEvent)
33326     {   
33327         this.value = v;
33328         if(this.rendered){
33329             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33330         }
33331         
33332         Roo.each(this.radioes, function(i){
33333             
33334             i.checked = false;
33335             i.el.removeClass('checked');
33336             
33337             if(i.value === v || i.value.toString() === v.toString()){
33338                 i.checked = true;
33339                 i.el.addClass('checked');
33340                 
33341                 if(suppressEvent !== true){
33342                     this.fireEvent('check', this, i);
33343                 }
33344             }
33345             
33346         }, this);
33347         
33348         this.validate();
33349     },
33350     
33351     clearInvalid : function(){
33352         
33353         if(!this.el || this.preventMark){
33354             return;
33355         }
33356         
33357         this.el.removeClass([this.invalidClass]);
33358         
33359         this.fireEvent('valid', this);
33360     }
33361     
33362 });
33363
33364 Roo.apply(Roo.bootstrap.RadioSet, {
33365     
33366     groups: {},
33367     
33368     register : function(set)
33369     {
33370         this.groups[set.name] = set;
33371     },
33372     
33373     get: function(name) 
33374     {
33375         if (typeof(this.groups[name]) == 'undefined') {
33376             return false;
33377         }
33378         
33379         return this.groups[name] ;
33380     }
33381     
33382 });
33383 /*
33384  * Based on:
33385  * Ext JS Library 1.1.1
33386  * Copyright(c) 2006-2007, Ext JS, LLC.
33387  *
33388  * Originally Released Under LGPL - original licence link has changed is not relivant.
33389  *
33390  * Fork - LGPL
33391  * <script type="text/javascript">
33392  */
33393
33394
33395 /**
33396  * @class Roo.bootstrap.SplitBar
33397  * @extends Roo.util.Observable
33398  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33399  * <br><br>
33400  * Usage:
33401  * <pre><code>
33402 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33403                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33404 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
33405 split.minSize = 100;
33406 split.maxSize = 600;
33407 split.animate = true;
33408 split.on('moved', splitterMoved);
33409 </code></pre>
33410  * @constructor
33411  * Create a new SplitBar
33412  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
33413  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
33414  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33415  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
33416                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
33417                         position of the SplitBar).
33418  */
33419 Roo.bootstrap.SplitBar = function(cfg){
33420     
33421     /** @private */
33422     
33423     //{
33424     //  dragElement : elm
33425     //  resizingElement: el,
33426         // optional..
33427     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
33428     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
33429         // existingProxy ???
33430     //}
33431     
33432     this.el = Roo.get(cfg.dragElement, true);
33433     this.el.dom.unselectable = "on";
33434     /** @private */
33435     this.resizingEl = Roo.get(cfg.resizingElement, true);
33436
33437     /**
33438      * @private
33439      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33440      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
33441      * @type Number
33442      */
33443     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
33444     
33445     /**
33446      * The minimum size of the resizing element. (Defaults to 0)
33447      * @type Number
33448      */
33449     this.minSize = 0;
33450     
33451     /**
33452      * The maximum size of the resizing element. (Defaults to 2000)
33453      * @type Number
33454      */
33455     this.maxSize = 2000;
33456     
33457     /**
33458      * Whether to animate the transition to the new size
33459      * @type Boolean
33460      */
33461     this.animate = false;
33462     
33463     /**
33464      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
33465      * @type Boolean
33466      */
33467     this.useShim = false;
33468     
33469     /** @private */
33470     this.shim = null;
33471     
33472     if(!cfg.existingProxy){
33473         /** @private */
33474         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
33475     }else{
33476         this.proxy = Roo.get(cfg.existingProxy).dom;
33477     }
33478     /** @private */
33479     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
33480     
33481     /** @private */
33482     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
33483     
33484     /** @private */
33485     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
33486     
33487     /** @private */
33488     this.dragSpecs = {};
33489     
33490     /**
33491      * @private The adapter to use to positon and resize elements
33492      */
33493     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33494     this.adapter.init(this);
33495     
33496     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33497         /** @private */
33498         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
33499         this.el.addClass("roo-splitbar-h");
33500     }else{
33501         /** @private */
33502         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
33503         this.el.addClass("roo-splitbar-v");
33504     }
33505     
33506     this.addEvents({
33507         /**
33508          * @event resize
33509          * Fires when the splitter is moved (alias for {@link #event-moved})
33510          * @param {Roo.bootstrap.SplitBar} this
33511          * @param {Number} newSize the new width or height
33512          */
33513         "resize" : true,
33514         /**
33515          * @event moved
33516          * Fires when the splitter is moved
33517          * @param {Roo.bootstrap.SplitBar} this
33518          * @param {Number} newSize the new width or height
33519          */
33520         "moved" : true,
33521         /**
33522          * @event beforeresize
33523          * Fires before the splitter is dragged
33524          * @param {Roo.bootstrap.SplitBar} this
33525          */
33526         "beforeresize" : true,
33527
33528         "beforeapply" : true
33529     });
33530
33531     Roo.util.Observable.call(this);
33532 };
33533
33534 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
33535     onStartProxyDrag : function(x, y){
33536         this.fireEvent("beforeresize", this);
33537         if(!this.overlay){
33538             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
33539             o.unselectable();
33540             o.enableDisplayMode("block");
33541             // all splitbars share the same overlay
33542             Roo.bootstrap.SplitBar.prototype.overlay = o;
33543         }
33544         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33545         this.overlay.show();
33546         Roo.get(this.proxy).setDisplayed("block");
33547         var size = this.adapter.getElementSize(this);
33548         this.activeMinSize = this.getMinimumSize();;
33549         this.activeMaxSize = this.getMaximumSize();;
33550         var c1 = size - this.activeMinSize;
33551         var c2 = Math.max(this.activeMaxSize - size, 0);
33552         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33553             this.dd.resetConstraints();
33554             this.dd.setXConstraint(
33555                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
33556                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
33557             );
33558             this.dd.setYConstraint(0, 0);
33559         }else{
33560             this.dd.resetConstraints();
33561             this.dd.setXConstraint(0, 0);
33562             this.dd.setYConstraint(
33563                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
33564                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
33565             );
33566          }
33567         this.dragSpecs.startSize = size;
33568         this.dragSpecs.startPoint = [x, y];
33569         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
33570     },
33571     
33572     /** 
33573      * @private Called after the drag operation by the DDProxy
33574      */
33575     onEndProxyDrag : function(e){
33576         Roo.get(this.proxy).setDisplayed(false);
33577         var endPoint = Roo.lib.Event.getXY(e);
33578         if(this.overlay){
33579             this.overlay.hide();
33580         }
33581         var newSize;
33582         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33583             newSize = this.dragSpecs.startSize + 
33584                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
33585                     endPoint[0] - this.dragSpecs.startPoint[0] :
33586                     this.dragSpecs.startPoint[0] - endPoint[0]
33587                 );
33588         }else{
33589             newSize = this.dragSpecs.startSize + 
33590                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
33591                     endPoint[1] - this.dragSpecs.startPoint[1] :
33592                     this.dragSpecs.startPoint[1] - endPoint[1]
33593                 );
33594         }
33595         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
33596         if(newSize != this.dragSpecs.startSize){
33597             if(this.fireEvent('beforeapply', this, newSize) !== false){
33598                 this.adapter.setElementSize(this, newSize);
33599                 this.fireEvent("moved", this, newSize);
33600                 this.fireEvent("resize", this, newSize);
33601             }
33602         }
33603     },
33604     
33605     /**
33606      * Get the adapter this SplitBar uses
33607      * @return The adapter object
33608      */
33609     getAdapter : function(){
33610         return this.adapter;
33611     },
33612     
33613     /**
33614      * Set the adapter this SplitBar uses
33615      * @param {Object} adapter A SplitBar adapter object
33616      */
33617     setAdapter : function(adapter){
33618         this.adapter = adapter;
33619         this.adapter.init(this);
33620     },
33621     
33622     /**
33623      * Gets the minimum size for the resizing element
33624      * @return {Number} The minimum size
33625      */
33626     getMinimumSize : function(){
33627         return this.minSize;
33628     },
33629     
33630     /**
33631      * Sets the minimum size for the resizing element
33632      * @param {Number} minSize The minimum size
33633      */
33634     setMinimumSize : function(minSize){
33635         this.minSize = minSize;
33636     },
33637     
33638     /**
33639      * Gets the maximum size for the resizing element
33640      * @return {Number} The maximum size
33641      */
33642     getMaximumSize : function(){
33643         return this.maxSize;
33644     },
33645     
33646     /**
33647      * Sets the maximum size for the resizing element
33648      * @param {Number} maxSize The maximum size
33649      */
33650     setMaximumSize : function(maxSize){
33651         this.maxSize = maxSize;
33652     },
33653     
33654     /**
33655      * Sets the initialize size for the resizing element
33656      * @param {Number} size The initial size
33657      */
33658     setCurrentSize : function(size){
33659         var oldAnimate = this.animate;
33660         this.animate = false;
33661         this.adapter.setElementSize(this, size);
33662         this.animate = oldAnimate;
33663     },
33664     
33665     /**
33666      * Destroy this splitbar. 
33667      * @param {Boolean} removeEl True to remove the element
33668      */
33669     destroy : function(removeEl){
33670         if(this.shim){
33671             this.shim.remove();
33672         }
33673         this.dd.unreg();
33674         this.proxy.parentNode.removeChild(this.proxy);
33675         if(removeEl){
33676             this.el.remove();
33677         }
33678     }
33679 });
33680
33681 /**
33682  * @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.
33683  */
33684 Roo.bootstrap.SplitBar.createProxy = function(dir){
33685     var proxy = new Roo.Element(document.createElement("div"));
33686     proxy.unselectable();
33687     var cls = 'roo-splitbar-proxy';
33688     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
33689     document.body.appendChild(proxy.dom);
33690     return proxy.dom;
33691 };
33692
33693 /** 
33694  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
33695  * Default Adapter. It assumes the splitter and resizing element are not positioned
33696  * elements and only gets/sets the width of the element. Generally used for table based layouts.
33697  */
33698 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
33699 };
33700
33701 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
33702     // do nothing for now
33703     init : function(s){
33704     
33705     },
33706     /**
33707      * Called before drag operations to get the current size of the resizing element. 
33708      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33709      */
33710      getElementSize : function(s){
33711         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33712             return s.resizingEl.getWidth();
33713         }else{
33714             return s.resizingEl.getHeight();
33715         }
33716     },
33717     
33718     /**
33719      * Called after drag operations to set the size of the resizing element.
33720      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33721      * @param {Number} newSize The new size to set
33722      * @param {Function} onComplete A function to be invoked when resizing is complete
33723      */
33724     setElementSize : function(s, newSize, onComplete){
33725         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33726             if(!s.animate){
33727                 s.resizingEl.setWidth(newSize);
33728                 if(onComplete){
33729                     onComplete(s, newSize);
33730                 }
33731             }else{
33732                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
33733             }
33734         }else{
33735             
33736             if(!s.animate){
33737                 s.resizingEl.setHeight(newSize);
33738                 if(onComplete){
33739                     onComplete(s, newSize);
33740                 }
33741             }else{
33742                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
33743             }
33744         }
33745     }
33746 };
33747
33748 /** 
33749  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
33750  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
33751  * Adapter that  moves the splitter element to align with the resized sizing element. 
33752  * Used with an absolute positioned SplitBar.
33753  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
33754  * document.body, make sure you assign an id to the body element.
33755  */
33756 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
33757     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33758     this.container = Roo.get(container);
33759 };
33760
33761 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
33762     init : function(s){
33763         this.basic.init(s);
33764     },
33765     
33766     getElementSize : function(s){
33767         return this.basic.getElementSize(s);
33768     },
33769     
33770     setElementSize : function(s, newSize, onComplete){
33771         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
33772     },
33773     
33774     moveSplitter : function(s){
33775         var yes = Roo.bootstrap.SplitBar;
33776         switch(s.placement){
33777             case yes.LEFT:
33778                 s.el.setX(s.resizingEl.getRight());
33779                 break;
33780             case yes.RIGHT:
33781                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
33782                 break;
33783             case yes.TOP:
33784                 s.el.setY(s.resizingEl.getBottom());
33785                 break;
33786             case yes.BOTTOM:
33787                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
33788                 break;
33789         }
33790     }
33791 };
33792
33793 /**
33794  * Orientation constant - Create a vertical SplitBar
33795  * @static
33796  * @type Number
33797  */
33798 Roo.bootstrap.SplitBar.VERTICAL = 1;
33799
33800 /**
33801  * Orientation constant - Create a horizontal SplitBar
33802  * @static
33803  * @type Number
33804  */
33805 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
33806
33807 /**
33808  * Placement constant - The resizing element is to the left of the splitter element
33809  * @static
33810  * @type Number
33811  */
33812 Roo.bootstrap.SplitBar.LEFT = 1;
33813
33814 /**
33815  * Placement constant - The resizing element is to the right of the splitter element
33816  * @static
33817  * @type Number
33818  */
33819 Roo.bootstrap.SplitBar.RIGHT = 2;
33820
33821 /**
33822  * Placement constant - The resizing element is positioned above the splitter element
33823  * @static
33824  * @type Number
33825  */
33826 Roo.bootstrap.SplitBar.TOP = 3;
33827
33828 /**
33829  * Placement constant - The resizing element is positioned under splitter element
33830  * @static
33831  * @type Number
33832  */
33833 Roo.bootstrap.SplitBar.BOTTOM = 4;
33834 Roo.namespace("Roo.bootstrap.layout");/*
33835  * Based on:
33836  * Ext JS Library 1.1.1
33837  * Copyright(c) 2006-2007, Ext JS, LLC.
33838  *
33839  * Originally Released Under LGPL - original licence link has changed is not relivant.
33840  *
33841  * Fork - LGPL
33842  * <script type="text/javascript">
33843  */
33844
33845 /**
33846  * @class Roo.bootstrap.layout.Manager
33847  * @extends Roo.bootstrap.Component
33848  * Base class for layout managers.
33849  */
33850 Roo.bootstrap.layout.Manager = function(config)
33851 {
33852     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
33853
33854
33855
33856
33857
33858     /** false to disable window resize monitoring @type Boolean */
33859     this.monitorWindowResize = true;
33860     this.regions = {};
33861     this.addEvents({
33862         /**
33863          * @event layout
33864          * Fires when a layout is performed.
33865          * @param {Roo.LayoutManager} this
33866          */
33867         "layout" : true,
33868         /**
33869          * @event regionresized
33870          * Fires when the user resizes a region.
33871          * @param {Roo.LayoutRegion} region The resized region
33872          * @param {Number} newSize The new size (width for east/west, height for north/south)
33873          */
33874         "regionresized" : true,
33875         /**
33876          * @event regioncollapsed
33877          * Fires when a region is collapsed.
33878          * @param {Roo.LayoutRegion} region The collapsed region
33879          */
33880         "regioncollapsed" : true,
33881         /**
33882          * @event regionexpanded
33883          * Fires when a region is expanded.
33884          * @param {Roo.LayoutRegion} region The expanded region
33885          */
33886         "regionexpanded" : true
33887     });
33888     this.updating = false;
33889
33890     if (config.el) {
33891         this.el = Roo.get(config.el);
33892         this.initEvents();
33893     }
33894
33895 };
33896
33897 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
33898
33899
33900     regions : null,
33901
33902     monitorWindowResize : true,
33903
33904
33905     updating : false,
33906
33907
33908     onRender : function(ct, position)
33909     {
33910         if(!this.el){
33911             this.el = Roo.get(ct);
33912             this.initEvents();
33913         }
33914         //this.fireEvent('render',this);
33915     },
33916
33917
33918     initEvents: function()
33919     {
33920
33921
33922         // ie scrollbar fix
33923         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
33924             document.body.scroll = "no";
33925         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
33926             this.el.position('relative');
33927         }
33928         this.id = this.el.id;
33929         this.el.addClass("roo-layout-container");
33930         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
33931         if(this.el.dom != document.body ) {
33932             this.el.on('resize', this.layout,this);
33933             this.el.on('show', this.layout,this);
33934         }
33935
33936     },
33937
33938     /**
33939      * Returns true if this layout is currently being updated
33940      * @return {Boolean}
33941      */
33942     isUpdating : function(){
33943         return this.updating;
33944     },
33945
33946     /**
33947      * Suspend the LayoutManager from doing auto-layouts while
33948      * making multiple add or remove calls
33949      */
33950     beginUpdate : function(){
33951         this.updating = true;
33952     },
33953
33954     /**
33955      * Restore auto-layouts and optionally disable the manager from performing a layout
33956      * @param {Boolean} noLayout true to disable a layout update
33957      */
33958     endUpdate : function(noLayout){
33959         this.updating = false;
33960         if(!noLayout){
33961             this.layout();
33962         }
33963     },
33964
33965     layout: function(){
33966         // abstract...
33967     },
33968
33969     onRegionResized : function(region, newSize){
33970         this.fireEvent("regionresized", region, newSize);
33971         this.layout();
33972     },
33973
33974     onRegionCollapsed : function(region){
33975         this.fireEvent("regioncollapsed", region);
33976     },
33977
33978     onRegionExpanded : function(region){
33979         this.fireEvent("regionexpanded", region);
33980     },
33981
33982     /**
33983      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
33984      * performs box-model adjustments.
33985      * @return {Object} The size as an object {width: (the width), height: (the height)}
33986      */
33987     getViewSize : function()
33988     {
33989         var size;
33990         if(this.el.dom != document.body){
33991             size = this.el.getSize();
33992         }else{
33993             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
33994         }
33995         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
33996         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
33997         return size;
33998     },
33999
34000     /**
34001      * Returns the Element this layout is bound to.
34002      * @return {Roo.Element}
34003      */
34004     getEl : function(){
34005         return this.el;
34006     },
34007
34008     /**
34009      * Returns the specified region.
34010      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34011      * @return {Roo.LayoutRegion}
34012      */
34013     getRegion : function(target){
34014         return this.regions[target.toLowerCase()];
34015     },
34016
34017     onWindowResize : function(){
34018         if(this.monitorWindowResize){
34019             this.layout();
34020         }
34021     }
34022 });
34023 /*
34024  * Based on:
34025  * Ext JS Library 1.1.1
34026  * Copyright(c) 2006-2007, Ext JS, LLC.
34027  *
34028  * Originally Released Under LGPL - original licence link has changed is not relivant.
34029  *
34030  * Fork - LGPL
34031  * <script type="text/javascript">
34032  */
34033 /**
34034  * @class Roo.bootstrap.layout.Border
34035  * @extends Roo.bootstrap.layout.Manager
34036  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34037  * please see: examples/bootstrap/nested.html<br><br>
34038  
34039 <b>The container the layout is rendered into can be either the body element or any other element.
34040 If it is not the body element, the container needs to either be an absolute positioned element,
34041 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34042 the container size if it is not the body element.</b>
34043
34044 * @constructor
34045 * Create a new Border
34046 * @param {Object} config Configuration options
34047  */
34048 Roo.bootstrap.layout.Border = function(config){
34049     config = config || {};
34050     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34051     
34052     
34053     
34054     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34055         if(config[region]){
34056             config[region].region = region;
34057             this.addRegion(config[region]);
34058         }
34059     },this);
34060     
34061 };
34062
34063 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34064
34065 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34066     /**
34067      * Creates and adds a new region if it doesn't already exist.
34068      * @param {String} target The target region key (north, south, east, west or center).
34069      * @param {Object} config The regions config object
34070      * @return {BorderLayoutRegion} The new region
34071      */
34072     addRegion : function(config)
34073     {
34074         if(!this.regions[config.region]){
34075             var r = this.factory(config);
34076             this.bindRegion(r);
34077         }
34078         return this.regions[config.region];
34079     },
34080
34081     // private (kinda)
34082     bindRegion : function(r){
34083         this.regions[r.config.region] = r;
34084         
34085         r.on("visibilitychange",    this.layout, this);
34086         r.on("paneladded",          this.layout, this);
34087         r.on("panelremoved",        this.layout, this);
34088         r.on("invalidated",         this.layout, this);
34089         r.on("resized",             this.onRegionResized, this);
34090         r.on("collapsed",           this.onRegionCollapsed, this);
34091         r.on("expanded",            this.onRegionExpanded, this);
34092     },
34093
34094     /**
34095      * Performs a layout update.
34096      */
34097     layout : function()
34098     {
34099         if(this.updating) {
34100             return;
34101         }
34102         
34103         // render all the rebions if they have not been done alreayd?
34104         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34105             if(this.regions[region] && !this.regions[region].bodyEl){
34106                 this.regions[region].onRender(this.el)
34107             }
34108         },this);
34109         
34110         var size = this.getViewSize();
34111         var w = size.width;
34112         var h = size.height;
34113         var centerW = w;
34114         var centerH = h;
34115         var centerY = 0;
34116         var centerX = 0;
34117         //var x = 0, y = 0;
34118
34119         var rs = this.regions;
34120         var north = rs["north"];
34121         var south = rs["south"]; 
34122         var west = rs["west"];
34123         var east = rs["east"];
34124         var center = rs["center"];
34125         //if(this.hideOnLayout){ // not supported anymore
34126             //c.el.setStyle("display", "none");
34127         //}
34128         if(north && north.isVisible()){
34129             var b = north.getBox();
34130             var m = north.getMargins();
34131             b.width = w - (m.left+m.right);
34132             b.x = m.left;
34133             b.y = m.top;
34134             centerY = b.height + b.y + m.bottom;
34135             centerH -= centerY;
34136             north.updateBox(this.safeBox(b));
34137         }
34138         if(south && south.isVisible()){
34139             var b = south.getBox();
34140             var m = south.getMargins();
34141             b.width = w - (m.left+m.right);
34142             b.x = m.left;
34143             var totalHeight = (b.height + m.top + m.bottom);
34144             b.y = h - totalHeight + m.top;
34145             centerH -= totalHeight;
34146             south.updateBox(this.safeBox(b));
34147         }
34148         if(west && west.isVisible()){
34149             var b = west.getBox();
34150             var m = west.getMargins();
34151             b.height = centerH - (m.top+m.bottom);
34152             b.x = m.left;
34153             b.y = centerY + m.top;
34154             var totalWidth = (b.width + m.left + m.right);
34155             centerX += totalWidth;
34156             centerW -= totalWidth;
34157             west.updateBox(this.safeBox(b));
34158         }
34159         if(east && east.isVisible()){
34160             var b = east.getBox();
34161             var m = east.getMargins();
34162             b.height = centerH - (m.top+m.bottom);
34163             var totalWidth = (b.width + m.left + m.right);
34164             b.x = w - totalWidth + m.left;
34165             b.y = centerY + m.top;
34166             centerW -= totalWidth;
34167             east.updateBox(this.safeBox(b));
34168         }
34169         if(center){
34170             var m = center.getMargins();
34171             var centerBox = {
34172                 x: centerX + m.left,
34173                 y: centerY + m.top,
34174                 width: centerW - (m.left+m.right),
34175                 height: centerH - (m.top+m.bottom)
34176             };
34177             //if(this.hideOnLayout){
34178                 //center.el.setStyle("display", "block");
34179             //}
34180             center.updateBox(this.safeBox(centerBox));
34181         }
34182         this.el.repaint();
34183         this.fireEvent("layout", this);
34184     },
34185
34186     // private
34187     safeBox : function(box){
34188         box.width = Math.max(0, box.width);
34189         box.height = Math.max(0, box.height);
34190         return box;
34191     },
34192
34193     /**
34194      * Adds a ContentPanel (or subclass) to this layout.
34195      * @param {String} target The target region key (north, south, east, west or center).
34196      * @param {Roo.ContentPanel} panel The panel to add
34197      * @return {Roo.ContentPanel} The added panel
34198      */
34199     add : function(target, panel){
34200          
34201         target = target.toLowerCase();
34202         return this.regions[target].add(panel);
34203     },
34204
34205     /**
34206      * Remove a ContentPanel (or subclass) to this layout.
34207      * @param {String} target The target region key (north, south, east, west or center).
34208      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34209      * @return {Roo.ContentPanel} The removed panel
34210      */
34211     remove : function(target, panel){
34212         target = target.toLowerCase();
34213         return this.regions[target].remove(panel);
34214     },
34215
34216     /**
34217      * Searches all regions for a panel with the specified id
34218      * @param {String} panelId
34219      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34220      */
34221     findPanel : function(panelId){
34222         var rs = this.regions;
34223         for(var target in rs){
34224             if(typeof rs[target] != "function"){
34225                 var p = rs[target].getPanel(panelId);
34226                 if(p){
34227                     return p;
34228                 }
34229             }
34230         }
34231         return null;
34232     },
34233
34234     /**
34235      * Searches all regions for a panel with the specified id and activates (shows) it.
34236      * @param {String/ContentPanel} panelId The panels id or the panel itself
34237      * @return {Roo.ContentPanel} The shown panel or null
34238      */
34239     showPanel : function(panelId) {
34240       var rs = this.regions;
34241       for(var target in rs){
34242          var r = rs[target];
34243          if(typeof r != "function"){
34244             if(r.hasPanel(panelId)){
34245                return r.showPanel(panelId);
34246             }
34247          }
34248       }
34249       return null;
34250    },
34251
34252    /**
34253      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34254      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34255      */
34256    /*
34257     restoreState : function(provider){
34258         if(!provider){
34259             provider = Roo.state.Manager;
34260         }
34261         var sm = new Roo.LayoutStateManager();
34262         sm.init(this, provider);
34263     },
34264 */
34265  
34266  
34267     /**
34268      * Adds a xtype elements to the layout.
34269      * <pre><code>
34270
34271 layout.addxtype({
34272        xtype : 'ContentPanel',
34273        region: 'west',
34274        items: [ .... ]
34275    }
34276 );
34277
34278 layout.addxtype({
34279         xtype : 'NestedLayoutPanel',
34280         region: 'west',
34281         layout: {
34282            center: { },
34283            west: { }   
34284         },
34285         items : [ ... list of content panels or nested layout panels.. ]
34286    }
34287 );
34288 </code></pre>
34289      * @param {Object} cfg Xtype definition of item to add.
34290      */
34291     addxtype : function(cfg)
34292     {
34293         // basically accepts a pannel...
34294         // can accept a layout region..!?!?
34295         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34296         
34297         
34298         // theory?  children can only be panels??
34299         
34300         //if (!cfg.xtype.match(/Panel$/)) {
34301         //    return false;
34302         //}
34303         var ret = false;
34304         
34305         if (typeof(cfg.region) == 'undefined') {
34306             Roo.log("Failed to add Panel, region was not set");
34307             Roo.log(cfg);
34308             return false;
34309         }
34310         var region = cfg.region;
34311         delete cfg.region;
34312         
34313           
34314         var xitems = [];
34315         if (cfg.items) {
34316             xitems = cfg.items;
34317             delete cfg.items;
34318         }
34319         var nb = false;
34320         
34321         switch(cfg.xtype) 
34322         {
34323             case 'Content':  // ContentPanel (el, cfg)
34324             case 'Scroll':  // ContentPanel (el, cfg)
34325             case 'View': 
34326                 cfg.autoCreate = true;
34327                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34328                 //} else {
34329                 //    var el = this.el.createChild();
34330                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34331                 //}
34332                 
34333                 this.add(region, ret);
34334                 break;
34335             
34336             /*
34337             case 'TreePanel': // our new panel!
34338                 cfg.el = this.el.createChild();
34339                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34340                 this.add(region, ret);
34341                 break;
34342             */
34343             
34344             case 'Nest': 
34345                 // create a new Layout (which is  a Border Layout...
34346                 
34347                 var clayout = cfg.layout;
34348                 clayout.el  = this.el.createChild();
34349                 clayout.items   = clayout.items  || [];
34350                 
34351                 delete cfg.layout;
34352                 
34353                 // replace this exitems with the clayout ones..
34354                 xitems = clayout.items;
34355                  
34356                 // force background off if it's in center...
34357                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34358                     cfg.background = false;
34359                 }
34360                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
34361                 
34362                 
34363                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34364                 //console.log('adding nested layout panel '  + cfg.toSource());
34365                 this.add(region, ret);
34366                 nb = {}; /// find first...
34367                 break;
34368             
34369             case 'Grid':
34370                 
34371                 // needs grid and region
34372                 
34373                 //var el = this.getRegion(region).el.createChild();
34374                 /*
34375                  *var el = this.el.createChild();
34376                 // create the grid first...
34377                 cfg.grid.container = el;
34378                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34379                 */
34380                 
34381                 if (region == 'center' && this.active ) {
34382                     cfg.background = false;
34383                 }
34384                 
34385                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34386                 
34387                 this.add(region, ret);
34388                 /*
34389                 if (cfg.background) {
34390                     // render grid on panel activation (if panel background)
34391                     ret.on('activate', function(gp) {
34392                         if (!gp.grid.rendered) {
34393                     //        gp.grid.render(el);
34394                         }
34395                     });
34396                 } else {
34397                   //  cfg.grid.render(el);
34398                 }
34399                 */
34400                 break;
34401            
34402            
34403             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34404                 // it was the old xcomponent building that caused this before.
34405                 // espeically if border is the top element in the tree.
34406                 ret = this;
34407                 break; 
34408                 
34409                     
34410                 
34411                 
34412                 
34413             default:
34414                 /*
34415                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34416                     
34417                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34418                     this.add(region, ret);
34419                 } else {
34420                 */
34421                     Roo.log(cfg);
34422                     throw "Can not add '" + cfg.xtype + "' to Border";
34423                     return null;
34424              
34425                                 
34426              
34427         }
34428         this.beginUpdate();
34429         // add children..
34430         var region = '';
34431         var abn = {};
34432         Roo.each(xitems, function(i)  {
34433             region = nb && i.region ? i.region : false;
34434             
34435             var add = ret.addxtype(i);
34436            
34437             if (region) {
34438                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
34439                 if (!i.background) {
34440                     abn[region] = nb[region] ;
34441                 }
34442             }
34443             
34444         });
34445         this.endUpdate();
34446
34447         // make the last non-background panel active..
34448         //if (nb) { Roo.log(abn); }
34449         if (nb) {
34450             
34451             for(var r in abn) {
34452                 region = this.getRegion(r);
34453                 if (region) {
34454                     // tried using nb[r], but it does not work..
34455                      
34456                     region.showPanel(abn[r]);
34457                    
34458                 }
34459             }
34460         }
34461         return ret;
34462         
34463     },
34464     
34465     
34466 // private
34467     factory : function(cfg)
34468     {
34469         
34470         var validRegions = Roo.bootstrap.layout.Border.regions;
34471
34472         var target = cfg.region;
34473         cfg.mgr = this;
34474         
34475         var r = Roo.bootstrap.layout;
34476         Roo.log(target);
34477         switch(target){
34478             case "north":
34479                 return new r.North(cfg);
34480             case "south":
34481                 return new r.South(cfg);
34482             case "east":
34483                 return new r.East(cfg);
34484             case "west":
34485                 return new r.West(cfg);
34486             case "center":
34487                 return new r.Center(cfg);
34488         }
34489         throw 'Layout region "'+target+'" not supported.';
34490     }
34491     
34492     
34493 });
34494  /*
34495  * Based on:
34496  * Ext JS Library 1.1.1
34497  * Copyright(c) 2006-2007, Ext JS, LLC.
34498  *
34499  * Originally Released Under LGPL - original licence link has changed is not relivant.
34500  *
34501  * Fork - LGPL
34502  * <script type="text/javascript">
34503  */
34504  
34505 /**
34506  * @class Roo.bootstrap.layout.Basic
34507  * @extends Roo.util.Observable
34508  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
34509  * and does not have a titlebar, tabs or any other features. All it does is size and position 
34510  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
34511  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34512  * @cfg {string}   region  the region that it inhabits..
34513  * @cfg {bool}   skipConfig skip config?
34514  * 
34515
34516  */
34517 Roo.bootstrap.layout.Basic = function(config){
34518     
34519     this.mgr = config.mgr;
34520     
34521     this.position = config.region;
34522     
34523     var skipConfig = config.skipConfig;
34524     
34525     this.events = {
34526         /**
34527          * @scope Roo.BasicLayoutRegion
34528          */
34529         
34530         /**
34531          * @event beforeremove
34532          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
34533          * @param {Roo.LayoutRegion} this
34534          * @param {Roo.ContentPanel} panel The panel
34535          * @param {Object} e The cancel event object
34536          */
34537         "beforeremove" : true,
34538         /**
34539          * @event invalidated
34540          * Fires when the layout for this region is changed.
34541          * @param {Roo.LayoutRegion} this
34542          */
34543         "invalidated" : true,
34544         /**
34545          * @event visibilitychange
34546          * Fires when this region is shown or hidden 
34547          * @param {Roo.LayoutRegion} this
34548          * @param {Boolean} visibility true or false
34549          */
34550         "visibilitychange" : true,
34551         /**
34552          * @event paneladded
34553          * Fires when a panel is added. 
34554          * @param {Roo.LayoutRegion} this
34555          * @param {Roo.ContentPanel} panel The panel
34556          */
34557         "paneladded" : true,
34558         /**
34559          * @event panelremoved
34560          * Fires when a panel is removed. 
34561          * @param {Roo.LayoutRegion} this
34562          * @param {Roo.ContentPanel} panel The panel
34563          */
34564         "panelremoved" : true,
34565         /**
34566          * @event beforecollapse
34567          * Fires when this region before collapse.
34568          * @param {Roo.LayoutRegion} this
34569          */
34570         "beforecollapse" : true,
34571         /**
34572          * @event collapsed
34573          * Fires when this region is collapsed.
34574          * @param {Roo.LayoutRegion} this
34575          */
34576         "collapsed" : true,
34577         /**
34578          * @event expanded
34579          * Fires when this region is expanded.
34580          * @param {Roo.LayoutRegion} this
34581          */
34582         "expanded" : true,
34583         /**
34584          * @event slideshow
34585          * Fires when this region is slid into view.
34586          * @param {Roo.LayoutRegion} this
34587          */
34588         "slideshow" : true,
34589         /**
34590          * @event slidehide
34591          * Fires when this region slides out of view. 
34592          * @param {Roo.LayoutRegion} this
34593          */
34594         "slidehide" : true,
34595         /**
34596          * @event panelactivated
34597          * Fires when a panel is activated. 
34598          * @param {Roo.LayoutRegion} this
34599          * @param {Roo.ContentPanel} panel The activated panel
34600          */
34601         "panelactivated" : true,
34602         /**
34603          * @event resized
34604          * Fires when the user resizes this region. 
34605          * @param {Roo.LayoutRegion} this
34606          * @param {Number} newSize The new size (width for east/west, height for north/south)
34607          */
34608         "resized" : true
34609     };
34610     /** A collection of panels in this region. @type Roo.util.MixedCollection */
34611     this.panels = new Roo.util.MixedCollection();
34612     this.panels.getKey = this.getPanelId.createDelegate(this);
34613     this.box = null;
34614     this.activePanel = null;
34615     // ensure listeners are added...
34616     
34617     if (config.listeners || config.events) {
34618         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
34619             listeners : config.listeners || {},
34620             events : config.events || {}
34621         });
34622     }
34623     
34624     if(skipConfig !== true){
34625         this.applyConfig(config);
34626     }
34627 };
34628
34629 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
34630 {
34631     getPanelId : function(p){
34632         return p.getId();
34633     },
34634     
34635     applyConfig : function(config){
34636         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34637         this.config = config;
34638         
34639     },
34640     
34641     /**
34642      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
34643      * the width, for horizontal (north, south) the height.
34644      * @param {Number} newSize The new width or height
34645      */
34646     resizeTo : function(newSize){
34647         var el = this.el ? this.el :
34648                  (this.activePanel ? this.activePanel.getEl() : null);
34649         if(el){
34650             switch(this.position){
34651                 case "east":
34652                 case "west":
34653                     el.setWidth(newSize);
34654                     this.fireEvent("resized", this, newSize);
34655                 break;
34656                 case "north":
34657                 case "south":
34658                     el.setHeight(newSize);
34659                     this.fireEvent("resized", this, newSize);
34660                 break;                
34661             }
34662         }
34663     },
34664     
34665     getBox : function(){
34666         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
34667     },
34668     
34669     getMargins : function(){
34670         return this.margins;
34671     },
34672     
34673     updateBox : function(box){
34674         this.box = box;
34675         var el = this.activePanel.getEl();
34676         el.dom.style.left = box.x + "px";
34677         el.dom.style.top = box.y + "px";
34678         this.activePanel.setSize(box.width, box.height);
34679     },
34680     
34681     /**
34682      * Returns the container element for this region.
34683      * @return {Roo.Element}
34684      */
34685     getEl : function(){
34686         return this.activePanel;
34687     },
34688     
34689     /**
34690      * Returns true if this region is currently visible.
34691      * @return {Boolean}
34692      */
34693     isVisible : function(){
34694         return this.activePanel ? true : false;
34695     },
34696     
34697     setActivePanel : function(panel){
34698         panel = this.getPanel(panel);
34699         if(this.activePanel && this.activePanel != panel){
34700             this.activePanel.setActiveState(false);
34701             this.activePanel.getEl().setLeftTop(-10000,-10000);
34702         }
34703         this.activePanel = panel;
34704         panel.setActiveState(true);
34705         if(this.box){
34706             panel.setSize(this.box.width, this.box.height);
34707         }
34708         this.fireEvent("panelactivated", this, panel);
34709         this.fireEvent("invalidated");
34710     },
34711     
34712     /**
34713      * Show the specified panel.
34714      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
34715      * @return {Roo.ContentPanel} The shown panel or null
34716      */
34717     showPanel : function(panel){
34718         panel = this.getPanel(panel);
34719         if(panel){
34720             this.setActivePanel(panel);
34721         }
34722         return panel;
34723     },
34724     
34725     /**
34726      * Get the active panel for this region.
34727      * @return {Roo.ContentPanel} The active panel or null
34728      */
34729     getActivePanel : function(){
34730         return this.activePanel;
34731     },
34732     
34733     /**
34734      * Add the passed ContentPanel(s)
34735      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34736      * @return {Roo.ContentPanel} The panel added (if only one was added)
34737      */
34738     add : function(panel){
34739         if(arguments.length > 1){
34740             for(var i = 0, len = arguments.length; i < len; i++) {
34741                 this.add(arguments[i]);
34742             }
34743             return null;
34744         }
34745         if(this.hasPanel(panel)){
34746             this.showPanel(panel);
34747             return panel;
34748         }
34749         var el = panel.getEl();
34750         if(el.dom.parentNode != this.mgr.el.dom){
34751             this.mgr.el.dom.appendChild(el.dom);
34752         }
34753         if(panel.setRegion){
34754             panel.setRegion(this);
34755         }
34756         this.panels.add(panel);
34757         el.setStyle("position", "absolute");
34758         if(!panel.background){
34759             this.setActivePanel(panel);
34760             if(this.config.initialSize && this.panels.getCount()==1){
34761                 this.resizeTo(this.config.initialSize);
34762             }
34763         }
34764         this.fireEvent("paneladded", this, panel);
34765         return panel;
34766     },
34767     
34768     /**
34769      * Returns true if the panel is in this region.
34770      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34771      * @return {Boolean}
34772      */
34773     hasPanel : function(panel){
34774         if(typeof panel == "object"){ // must be panel obj
34775             panel = panel.getId();
34776         }
34777         return this.getPanel(panel) ? true : false;
34778     },
34779     
34780     /**
34781      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34782      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34783      * @param {Boolean} preservePanel Overrides the config preservePanel option
34784      * @return {Roo.ContentPanel} The panel that was removed
34785      */
34786     remove : function(panel, preservePanel){
34787         panel = this.getPanel(panel);
34788         if(!panel){
34789             return null;
34790         }
34791         var e = {};
34792         this.fireEvent("beforeremove", this, panel, e);
34793         if(e.cancel === true){
34794             return null;
34795         }
34796         var panelId = panel.getId();
34797         this.panels.removeKey(panelId);
34798         return panel;
34799     },
34800     
34801     /**
34802      * Returns the panel specified or null if it's not in this region.
34803      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34804      * @return {Roo.ContentPanel}
34805      */
34806     getPanel : function(id){
34807         if(typeof id == "object"){ // must be panel obj
34808             return id;
34809         }
34810         return this.panels.get(id);
34811     },
34812     
34813     /**
34814      * Returns this regions position (north/south/east/west/center).
34815      * @return {String} 
34816      */
34817     getPosition: function(){
34818         return this.position;    
34819     }
34820 });/*
34821  * Based on:
34822  * Ext JS Library 1.1.1
34823  * Copyright(c) 2006-2007, Ext JS, LLC.
34824  *
34825  * Originally Released Under LGPL - original licence link has changed is not relivant.
34826  *
34827  * Fork - LGPL
34828  * <script type="text/javascript">
34829  */
34830  
34831 /**
34832  * @class Roo.bootstrap.layout.Region
34833  * @extends Roo.bootstrap.layout.Basic
34834  * This class represents a region in a layout manager.
34835  
34836  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
34837  * @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})
34838  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
34839  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
34840  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
34841  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
34842  * @cfg {String}    title           The title for the region (overrides panel titles)
34843  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
34844  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
34845  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
34846  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
34847  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
34848  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
34849  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
34850  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
34851  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
34852  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
34853
34854  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
34855  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
34856  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
34857  * @cfg {Number}    width           For East/West panels
34858  * @cfg {Number}    height          For North/South panels
34859  * @cfg {Boolean}   split           To show the splitter
34860  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
34861  * 
34862  * @cfg {string}   cls             Extra CSS classes to add to region
34863  * 
34864  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34865  * @cfg {string}   region  the region that it inhabits..
34866  *
34867
34868  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
34869  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
34870
34871  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
34872  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
34873  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
34874  */
34875 Roo.bootstrap.layout.Region = function(config)
34876 {
34877     this.applyConfig(config);
34878
34879     var mgr = config.mgr;
34880     var pos = config.region;
34881     config.skipConfig = true;
34882     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
34883     
34884     if (mgr.el) {
34885         this.onRender(mgr.el);   
34886     }
34887      
34888     this.visible = true;
34889     this.collapsed = false;
34890     this.unrendered_panels = [];
34891 };
34892
34893 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
34894
34895     position: '', // set by wrapper (eg. north/south etc..)
34896     unrendered_panels : null,  // unrendered panels.
34897     createBody : function(){
34898         /** This region's body element 
34899         * @type Roo.Element */
34900         this.bodyEl = this.el.createChild({
34901                 tag: "div",
34902                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
34903         });
34904     },
34905
34906     onRender: function(ctr, pos)
34907     {
34908         var dh = Roo.DomHelper;
34909         /** This region's container element 
34910         * @type Roo.Element */
34911         this.el = dh.append(ctr.dom, {
34912                 tag: "div",
34913                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
34914             }, true);
34915         /** This region's title element 
34916         * @type Roo.Element */
34917     
34918         this.titleEl = dh.append(this.el.dom,
34919             {
34920                     tag: "div",
34921                     unselectable: "on",
34922                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
34923                     children:[
34924                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
34925                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
34926                     ]}, true);
34927         
34928         this.titleEl.enableDisplayMode();
34929         /** This region's title text element 
34930         * @type HTMLElement */
34931         this.titleTextEl = this.titleEl.dom.firstChild;
34932         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
34933         /*
34934         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
34935         this.closeBtn.enableDisplayMode();
34936         this.closeBtn.on("click", this.closeClicked, this);
34937         this.closeBtn.hide();
34938     */
34939         this.createBody(this.config);
34940         if(this.config.hideWhenEmpty){
34941             this.hide();
34942             this.on("paneladded", this.validateVisibility, this);
34943             this.on("panelremoved", this.validateVisibility, this);
34944         }
34945         if(this.autoScroll){
34946             this.bodyEl.setStyle("overflow", "auto");
34947         }else{
34948             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
34949         }
34950         //if(c.titlebar !== false){
34951             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
34952                 this.titleEl.hide();
34953             }else{
34954                 this.titleEl.show();
34955                 if(this.config.title){
34956                     this.titleTextEl.innerHTML = this.config.title;
34957                 }
34958             }
34959         //}
34960         if(this.config.collapsed){
34961             this.collapse(true);
34962         }
34963         if(this.config.hidden){
34964             this.hide();
34965         }
34966         
34967         if (this.unrendered_panels && this.unrendered_panels.length) {
34968             for (var i =0;i< this.unrendered_panels.length; i++) {
34969                 this.add(this.unrendered_panels[i]);
34970             }
34971             this.unrendered_panels = null;
34972             
34973         }
34974         
34975     },
34976     
34977     applyConfig : function(c)
34978     {
34979         /*
34980          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
34981             var dh = Roo.DomHelper;
34982             if(c.titlebar !== false){
34983                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
34984                 this.collapseBtn.on("click", this.collapse, this);
34985                 this.collapseBtn.enableDisplayMode();
34986                 /*
34987                 if(c.showPin === true || this.showPin){
34988                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
34989                     this.stickBtn.enableDisplayMode();
34990                     this.stickBtn.on("click", this.expand, this);
34991                     this.stickBtn.hide();
34992                 }
34993                 
34994             }
34995             */
34996             /** This region's collapsed element
34997             * @type Roo.Element */
34998             /*
34999              *
35000             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35001                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35002             ]}, true);
35003             
35004             if(c.floatable !== false){
35005                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35006                this.collapsedEl.on("click", this.collapseClick, this);
35007             }
35008
35009             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35010                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35011                    id: "message", unselectable: "on", style:{"float":"left"}});
35012                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35013              }
35014             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35015             this.expandBtn.on("click", this.expand, this);
35016             
35017         }
35018         
35019         if(this.collapseBtn){
35020             this.collapseBtn.setVisible(c.collapsible == true);
35021         }
35022         
35023         this.cmargins = c.cmargins || this.cmargins ||
35024                          (this.position == "west" || this.position == "east" ?
35025                              {top: 0, left: 2, right:2, bottom: 0} :
35026                              {top: 2, left: 0, right:0, bottom: 2});
35027         */
35028         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35029         
35030         
35031         this.bottomTabs = c.tabPosition != "top";
35032         
35033         this.autoScroll = c.autoScroll || false;
35034         
35035         
35036        
35037         
35038         this.duration = c.duration || .30;
35039         this.slideDuration = c.slideDuration || .45;
35040         this.config = c;
35041        
35042     },
35043     /**
35044      * Returns true if this region is currently visible.
35045      * @return {Boolean}
35046      */
35047     isVisible : function(){
35048         return this.visible;
35049     },
35050
35051     /**
35052      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35053      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35054      */
35055     //setCollapsedTitle : function(title){
35056     //    title = title || "&#160;";
35057      //   if(this.collapsedTitleTextEl){
35058       //      this.collapsedTitleTextEl.innerHTML = title;
35059        // }
35060     //},
35061
35062     getBox : function(){
35063         var b;
35064       //  if(!this.collapsed){
35065             b = this.el.getBox(false, true);
35066        // }else{
35067           //  b = this.collapsedEl.getBox(false, true);
35068         //}
35069         return b;
35070     },
35071
35072     getMargins : function(){
35073         return this.margins;
35074         //return this.collapsed ? this.cmargins : this.margins;
35075     },
35076 /*
35077     highlight : function(){
35078         this.el.addClass("x-layout-panel-dragover");
35079     },
35080
35081     unhighlight : function(){
35082         this.el.removeClass("x-layout-panel-dragover");
35083     },
35084 */
35085     updateBox : function(box)
35086     {
35087         if (!this.bodyEl) {
35088             return; // not rendered yet..
35089         }
35090         
35091         this.box = box;
35092         if(!this.collapsed){
35093             this.el.dom.style.left = box.x + "px";
35094             this.el.dom.style.top = box.y + "px";
35095             this.updateBody(box.width, box.height);
35096         }else{
35097             this.collapsedEl.dom.style.left = box.x + "px";
35098             this.collapsedEl.dom.style.top = box.y + "px";
35099             this.collapsedEl.setSize(box.width, box.height);
35100         }
35101         if(this.tabs){
35102             this.tabs.autoSizeTabs();
35103         }
35104     },
35105
35106     updateBody : function(w, h)
35107     {
35108         if(w !== null){
35109             this.el.setWidth(w);
35110             w -= this.el.getBorderWidth("rl");
35111             if(this.config.adjustments){
35112                 w += this.config.adjustments[0];
35113             }
35114         }
35115         if(h !== null && h > 0){
35116             this.el.setHeight(h);
35117             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35118             h -= this.el.getBorderWidth("tb");
35119             if(this.config.adjustments){
35120                 h += this.config.adjustments[1];
35121             }
35122             this.bodyEl.setHeight(h);
35123             if(this.tabs){
35124                 h = this.tabs.syncHeight(h);
35125             }
35126         }
35127         if(this.panelSize){
35128             w = w !== null ? w : this.panelSize.width;
35129             h = h !== null ? h : this.panelSize.height;
35130         }
35131         if(this.activePanel){
35132             var el = this.activePanel.getEl();
35133             w = w !== null ? w : el.getWidth();
35134             h = h !== null ? h : el.getHeight();
35135             this.panelSize = {width: w, height: h};
35136             this.activePanel.setSize(w, h);
35137         }
35138         if(Roo.isIE && this.tabs){
35139             this.tabs.el.repaint();
35140         }
35141     },
35142
35143     /**
35144      * Returns the container element for this region.
35145      * @return {Roo.Element}
35146      */
35147     getEl : function(){
35148         return this.el;
35149     },
35150
35151     /**
35152      * Hides this region.
35153      */
35154     hide : function(){
35155         //if(!this.collapsed){
35156             this.el.dom.style.left = "-2000px";
35157             this.el.hide();
35158         //}else{
35159          //   this.collapsedEl.dom.style.left = "-2000px";
35160          //   this.collapsedEl.hide();
35161        // }
35162         this.visible = false;
35163         this.fireEvent("visibilitychange", this, false);
35164     },
35165
35166     /**
35167      * Shows this region if it was previously hidden.
35168      */
35169     show : function(){
35170         //if(!this.collapsed){
35171             this.el.show();
35172         //}else{
35173         //    this.collapsedEl.show();
35174        // }
35175         this.visible = true;
35176         this.fireEvent("visibilitychange", this, true);
35177     },
35178 /*
35179     closeClicked : function(){
35180         if(this.activePanel){
35181             this.remove(this.activePanel);
35182         }
35183     },
35184
35185     collapseClick : function(e){
35186         if(this.isSlid){
35187            e.stopPropagation();
35188            this.slideIn();
35189         }else{
35190            e.stopPropagation();
35191            this.slideOut();
35192         }
35193     },
35194 */
35195     /**
35196      * Collapses this region.
35197      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35198      */
35199     /*
35200     collapse : function(skipAnim, skipCheck = false){
35201         if(this.collapsed) {
35202             return;
35203         }
35204         
35205         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35206             
35207             this.collapsed = true;
35208             if(this.split){
35209                 this.split.el.hide();
35210             }
35211             if(this.config.animate && skipAnim !== true){
35212                 this.fireEvent("invalidated", this);
35213                 this.animateCollapse();
35214             }else{
35215                 this.el.setLocation(-20000,-20000);
35216                 this.el.hide();
35217                 this.collapsedEl.show();
35218                 this.fireEvent("collapsed", this);
35219                 this.fireEvent("invalidated", this);
35220             }
35221         }
35222         
35223     },
35224 */
35225     animateCollapse : function(){
35226         // overridden
35227     },
35228
35229     /**
35230      * Expands this region if it was previously collapsed.
35231      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35232      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35233      */
35234     /*
35235     expand : function(e, skipAnim){
35236         if(e) {
35237             e.stopPropagation();
35238         }
35239         if(!this.collapsed || this.el.hasActiveFx()) {
35240             return;
35241         }
35242         if(this.isSlid){
35243             this.afterSlideIn();
35244             skipAnim = true;
35245         }
35246         this.collapsed = false;
35247         if(this.config.animate && skipAnim !== true){
35248             this.animateExpand();
35249         }else{
35250             this.el.show();
35251             if(this.split){
35252                 this.split.el.show();
35253             }
35254             this.collapsedEl.setLocation(-2000,-2000);
35255             this.collapsedEl.hide();
35256             this.fireEvent("invalidated", this);
35257             this.fireEvent("expanded", this);
35258         }
35259     },
35260 */
35261     animateExpand : function(){
35262         // overridden
35263     },
35264
35265     initTabs : function()
35266     {
35267         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35268         
35269         var ts = new Roo.bootstrap.panel.Tabs({
35270                 el: this.bodyEl.dom,
35271                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35272                 disableTooltips: this.config.disableTabTips,
35273                 toolbar : this.config.toolbar
35274             });
35275         
35276         if(this.config.hideTabs){
35277             ts.stripWrap.setDisplayed(false);
35278         }
35279         this.tabs = ts;
35280         ts.resizeTabs = this.config.resizeTabs === true;
35281         ts.minTabWidth = this.config.minTabWidth || 40;
35282         ts.maxTabWidth = this.config.maxTabWidth || 250;
35283         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35284         ts.monitorResize = false;
35285         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35286         ts.bodyEl.addClass('roo-layout-tabs-body');
35287         this.panels.each(this.initPanelAsTab, this);
35288     },
35289
35290     initPanelAsTab : function(panel){
35291         var ti = this.tabs.addTab(
35292             panel.getEl().id,
35293             panel.getTitle(),
35294             null,
35295             this.config.closeOnTab && panel.isClosable(),
35296             panel.tpl
35297         );
35298         if(panel.tabTip !== undefined){
35299             ti.setTooltip(panel.tabTip);
35300         }
35301         ti.on("activate", function(){
35302               this.setActivePanel(panel);
35303         }, this);
35304         
35305         if(this.config.closeOnTab){
35306             ti.on("beforeclose", function(t, e){
35307                 e.cancel = true;
35308                 this.remove(panel);
35309             }, this);
35310         }
35311         
35312         panel.tabItem = ti;
35313         
35314         return ti;
35315     },
35316
35317     updatePanelTitle : function(panel, title)
35318     {
35319         if(this.activePanel == panel){
35320             this.updateTitle(title);
35321         }
35322         if(this.tabs){
35323             var ti = this.tabs.getTab(panel.getEl().id);
35324             ti.setText(title);
35325             if(panel.tabTip !== undefined){
35326                 ti.setTooltip(panel.tabTip);
35327             }
35328         }
35329     },
35330
35331     updateTitle : function(title){
35332         if(this.titleTextEl && !this.config.title){
35333             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
35334         }
35335     },
35336
35337     setActivePanel : function(panel)
35338     {
35339         panel = this.getPanel(panel);
35340         if(this.activePanel && this.activePanel != panel){
35341             this.activePanel.setActiveState(false);
35342         }
35343         this.activePanel = panel;
35344         panel.setActiveState(true);
35345         if(this.panelSize){
35346             panel.setSize(this.panelSize.width, this.panelSize.height);
35347         }
35348         if(this.closeBtn){
35349             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35350         }
35351         this.updateTitle(panel.getTitle());
35352         if(this.tabs){
35353             this.fireEvent("invalidated", this);
35354         }
35355         this.fireEvent("panelactivated", this, panel);
35356     },
35357
35358     /**
35359      * Shows the specified panel.
35360      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35361      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35362      */
35363     showPanel : function(panel)
35364     {
35365         panel = this.getPanel(panel);
35366         if(panel){
35367             if(this.tabs){
35368                 var tab = this.tabs.getTab(panel.getEl().id);
35369                 if(tab.isHidden()){
35370                     this.tabs.unhideTab(tab.id);
35371                 }
35372                 tab.activate();
35373             }else{
35374                 this.setActivePanel(panel);
35375             }
35376         }
35377         return panel;
35378     },
35379
35380     /**
35381      * Get the active panel for this region.
35382      * @return {Roo.ContentPanel} The active panel or null
35383      */
35384     getActivePanel : function(){
35385         return this.activePanel;
35386     },
35387
35388     validateVisibility : function(){
35389         if(this.panels.getCount() < 1){
35390             this.updateTitle("&#160;");
35391             this.closeBtn.hide();
35392             this.hide();
35393         }else{
35394             if(!this.isVisible()){
35395                 this.show();
35396             }
35397         }
35398     },
35399
35400     /**
35401      * Adds the passed ContentPanel(s) to this region.
35402      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35403      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35404      */
35405     add : function(panel)
35406     {
35407         if(arguments.length > 1){
35408             for(var i = 0, len = arguments.length; i < len; i++) {
35409                 this.add(arguments[i]);
35410             }
35411             return null;
35412         }
35413         
35414         // if we have not been rendered yet, then we can not really do much of this..
35415         if (!this.bodyEl) {
35416             this.unrendered_panels.push(panel);
35417             return panel;
35418         }
35419         
35420         
35421         
35422         
35423         if(this.hasPanel(panel)){
35424             this.showPanel(panel);
35425             return panel;
35426         }
35427         panel.setRegion(this);
35428         this.panels.add(panel);
35429        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
35430             // sinle panel - no tab...?? would it not be better to render it with the tabs,
35431             // and hide them... ???
35432             this.bodyEl.dom.appendChild(panel.getEl().dom);
35433             if(panel.background !== true){
35434                 this.setActivePanel(panel);
35435             }
35436             this.fireEvent("paneladded", this, panel);
35437             return panel;
35438         }
35439         */
35440         if(!this.tabs){
35441             this.initTabs();
35442         }else{
35443             this.initPanelAsTab(panel);
35444         }
35445         
35446         
35447         if(panel.background !== true){
35448             this.tabs.activate(panel.getEl().id);
35449         }
35450         this.fireEvent("paneladded", this, panel);
35451         return panel;
35452     },
35453
35454     /**
35455      * Hides the tab for the specified panel.
35456      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35457      */
35458     hidePanel : function(panel){
35459         if(this.tabs && (panel = this.getPanel(panel))){
35460             this.tabs.hideTab(panel.getEl().id);
35461         }
35462     },
35463
35464     /**
35465      * Unhides the tab for a previously hidden panel.
35466      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35467      */
35468     unhidePanel : function(panel){
35469         if(this.tabs && (panel = this.getPanel(panel))){
35470             this.tabs.unhideTab(panel.getEl().id);
35471         }
35472     },
35473
35474     clearPanels : function(){
35475         while(this.panels.getCount() > 0){
35476              this.remove(this.panels.first());
35477         }
35478     },
35479
35480     /**
35481      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35482      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35483      * @param {Boolean} preservePanel Overrides the config preservePanel option
35484      * @return {Roo.ContentPanel} The panel that was removed
35485      */
35486     remove : function(panel, preservePanel)
35487     {
35488         panel = this.getPanel(panel);
35489         if(!panel){
35490             return null;
35491         }
35492         var e = {};
35493         this.fireEvent("beforeremove", this, panel, e);
35494         if(e.cancel === true){
35495             return null;
35496         }
35497         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
35498         var panelId = panel.getId();
35499         this.panels.removeKey(panelId);
35500         if(preservePanel){
35501             document.body.appendChild(panel.getEl().dom);
35502         }
35503         if(this.tabs){
35504             this.tabs.removeTab(panel.getEl().id);
35505         }else if (!preservePanel){
35506             this.bodyEl.dom.removeChild(panel.getEl().dom);
35507         }
35508         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
35509             var p = this.panels.first();
35510             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
35511             tempEl.appendChild(p.getEl().dom);
35512             this.bodyEl.update("");
35513             this.bodyEl.dom.appendChild(p.getEl().dom);
35514             tempEl = null;
35515             this.updateTitle(p.getTitle());
35516             this.tabs = null;
35517             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
35518             this.setActivePanel(p);
35519         }
35520         panel.setRegion(null);
35521         if(this.activePanel == panel){
35522             this.activePanel = null;
35523         }
35524         if(this.config.autoDestroy !== false && preservePanel !== true){
35525             try{panel.destroy();}catch(e){}
35526         }
35527         this.fireEvent("panelremoved", this, panel);
35528         return panel;
35529     },
35530
35531     /**
35532      * Returns the TabPanel component used by this region
35533      * @return {Roo.TabPanel}
35534      */
35535     getTabs : function(){
35536         return this.tabs;
35537     },
35538
35539     createTool : function(parentEl, className){
35540         var btn = Roo.DomHelper.append(parentEl, {
35541             tag: "div",
35542             cls: "x-layout-tools-button",
35543             children: [ {
35544                 tag: "div",
35545                 cls: "roo-layout-tools-button-inner " + className,
35546                 html: "&#160;"
35547             }]
35548         }, true);
35549         btn.addClassOnOver("roo-layout-tools-button-over");
35550         return btn;
35551     }
35552 });/*
35553  * Based on:
35554  * Ext JS Library 1.1.1
35555  * Copyright(c) 2006-2007, Ext JS, LLC.
35556  *
35557  * Originally Released Under LGPL - original licence link has changed is not relivant.
35558  *
35559  * Fork - LGPL
35560  * <script type="text/javascript">
35561  */
35562  
35563
35564
35565 /**
35566  * @class Roo.SplitLayoutRegion
35567  * @extends Roo.LayoutRegion
35568  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
35569  */
35570 Roo.bootstrap.layout.Split = function(config){
35571     this.cursor = config.cursor;
35572     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
35573 };
35574
35575 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
35576 {
35577     splitTip : "Drag to resize.",
35578     collapsibleSplitTip : "Drag to resize. Double click to hide.",
35579     useSplitTips : false,
35580
35581     applyConfig : function(config){
35582         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
35583     },
35584     
35585     onRender : function(ctr,pos) {
35586         
35587         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
35588         if(!this.config.split){
35589             return;
35590         }
35591         if(!this.split){
35592             
35593             var splitEl = Roo.DomHelper.append(ctr.dom,  {
35594                             tag: "div",
35595                             id: this.el.id + "-split",
35596                             cls: "roo-layout-split roo-layout-split-"+this.position,
35597                             html: "&#160;"
35598             });
35599             /** The SplitBar for this region 
35600             * @type Roo.SplitBar */
35601             // does not exist yet...
35602             Roo.log([this.position, this.orientation]);
35603             
35604             this.split = new Roo.bootstrap.SplitBar({
35605                 dragElement : splitEl,
35606                 resizingElement: this.el,
35607                 orientation : this.orientation
35608             });
35609             
35610             this.split.on("moved", this.onSplitMove, this);
35611             this.split.useShim = this.config.useShim === true;
35612             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
35613             if(this.useSplitTips){
35614                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
35615             }
35616             //if(config.collapsible){
35617             //    this.split.el.on("dblclick", this.collapse,  this);
35618             //}
35619         }
35620         if(typeof this.config.minSize != "undefined"){
35621             this.split.minSize = this.config.minSize;
35622         }
35623         if(typeof this.config.maxSize != "undefined"){
35624             this.split.maxSize = this.config.maxSize;
35625         }
35626         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
35627             this.hideSplitter();
35628         }
35629         
35630     },
35631
35632     getHMaxSize : function(){
35633          var cmax = this.config.maxSize || 10000;
35634          var center = this.mgr.getRegion("center");
35635          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
35636     },
35637
35638     getVMaxSize : function(){
35639          var cmax = this.config.maxSize || 10000;
35640          var center = this.mgr.getRegion("center");
35641          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
35642     },
35643
35644     onSplitMove : function(split, newSize){
35645         this.fireEvent("resized", this, newSize);
35646     },
35647     
35648     /** 
35649      * Returns the {@link Roo.SplitBar} for this region.
35650      * @return {Roo.SplitBar}
35651      */
35652     getSplitBar : function(){
35653         return this.split;
35654     },
35655     
35656     hide : function(){
35657         this.hideSplitter();
35658         Roo.bootstrap.layout.Split.superclass.hide.call(this);
35659     },
35660
35661     hideSplitter : function(){
35662         if(this.split){
35663             this.split.el.setLocation(-2000,-2000);
35664             this.split.el.hide();
35665         }
35666     },
35667
35668     show : function(){
35669         if(this.split){
35670             this.split.el.show();
35671         }
35672         Roo.bootstrap.layout.Split.superclass.show.call(this);
35673     },
35674     
35675     beforeSlide: function(){
35676         if(Roo.isGecko){// firefox overflow auto bug workaround
35677             this.bodyEl.clip();
35678             if(this.tabs) {
35679                 this.tabs.bodyEl.clip();
35680             }
35681             if(this.activePanel){
35682                 this.activePanel.getEl().clip();
35683                 
35684                 if(this.activePanel.beforeSlide){
35685                     this.activePanel.beforeSlide();
35686                 }
35687             }
35688         }
35689     },
35690     
35691     afterSlide : function(){
35692         if(Roo.isGecko){// firefox overflow auto bug workaround
35693             this.bodyEl.unclip();
35694             if(this.tabs) {
35695                 this.tabs.bodyEl.unclip();
35696             }
35697             if(this.activePanel){
35698                 this.activePanel.getEl().unclip();
35699                 if(this.activePanel.afterSlide){
35700                     this.activePanel.afterSlide();
35701                 }
35702             }
35703         }
35704     },
35705
35706     initAutoHide : function(){
35707         if(this.autoHide !== false){
35708             if(!this.autoHideHd){
35709                 var st = new Roo.util.DelayedTask(this.slideIn, this);
35710                 this.autoHideHd = {
35711                     "mouseout": function(e){
35712                         if(!e.within(this.el, true)){
35713                             st.delay(500);
35714                         }
35715                     },
35716                     "mouseover" : function(e){
35717                         st.cancel();
35718                     },
35719                     scope : this
35720                 };
35721             }
35722             this.el.on(this.autoHideHd);
35723         }
35724     },
35725
35726     clearAutoHide : function(){
35727         if(this.autoHide !== false){
35728             this.el.un("mouseout", this.autoHideHd.mouseout);
35729             this.el.un("mouseover", this.autoHideHd.mouseover);
35730         }
35731     },
35732
35733     clearMonitor : function(){
35734         Roo.get(document).un("click", this.slideInIf, this);
35735     },
35736
35737     // these names are backwards but not changed for compat
35738     slideOut : function(){
35739         if(this.isSlid || this.el.hasActiveFx()){
35740             return;
35741         }
35742         this.isSlid = true;
35743         if(this.collapseBtn){
35744             this.collapseBtn.hide();
35745         }
35746         this.closeBtnState = this.closeBtn.getStyle('display');
35747         this.closeBtn.hide();
35748         if(this.stickBtn){
35749             this.stickBtn.show();
35750         }
35751         this.el.show();
35752         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
35753         this.beforeSlide();
35754         this.el.setStyle("z-index", 10001);
35755         this.el.slideIn(this.getSlideAnchor(), {
35756             callback: function(){
35757                 this.afterSlide();
35758                 this.initAutoHide();
35759                 Roo.get(document).on("click", this.slideInIf, this);
35760                 this.fireEvent("slideshow", this);
35761             },
35762             scope: this,
35763             block: true
35764         });
35765     },
35766
35767     afterSlideIn : function(){
35768         this.clearAutoHide();
35769         this.isSlid = false;
35770         this.clearMonitor();
35771         this.el.setStyle("z-index", "");
35772         if(this.collapseBtn){
35773             this.collapseBtn.show();
35774         }
35775         this.closeBtn.setStyle('display', this.closeBtnState);
35776         if(this.stickBtn){
35777             this.stickBtn.hide();
35778         }
35779         this.fireEvent("slidehide", this);
35780     },
35781
35782     slideIn : function(cb){
35783         if(!this.isSlid || this.el.hasActiveFx()){
35784             Roo.callback(cb);
35785             return;
35786         }
35787         this.isSlid = false;
35788         this.beforeSlide();
35789         this.el.slideOut(this.getSlideAnchor(), {
35790             callback: function(){
35791                 this.el.setLeftTop(-10000, -10000);
35792                 this.afterSlide();
35793                 this.afterSlideIn();
35794                 Roo.callback(cb);
35795             },
35796             scope: this,
35797             block: true
35798         });
35799     },
35800     
35801     slideInIf : function(e){
35802         if(!e.within(this.el)){
35803             this.slideIn();
35804         }
35805     },
35806
35807     animateCollapse : function(){
35808         this.beforeSlide();
35809         this.el.setStyle("z-index", 20000);
35810         var anchor = this.getSlideAnchor();
35811         this.el.slideOut(anchor, {
35812             callback : function(){
35813                 this.el.setStyle("z-index", "");
35814                 this.collapsedEl.slideIn(anchor, {duration:.3});
35815                 this.afterSlide();
35816                 this.el.setLocation(-10000,-10000);
35817                 this.el.hide();
35818                 this.fireEvent("collapsed", this);
35819             },
35820             scope: this,
35821             block: true
35822         });
35823     },
35824
35825     animateExpand : function(){
35826         this.beforeSlide();
35827         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
35828         this.el.setStyle("z-index", 20000);
35829         this.collapsedEl.hide({
35830             duration:.1
35831         });
35832         this.el.slideIn(this.getSlideAnchor(), {
35833             callback : function(){
35834                 this.el.setStyle("z-index", "");
35835                 this.afterSlide();
35836                 if(this.split){
35837                     this.split.el.show();
35838                 }
35839                 this.fireEvent("invalidated", this);
35840                 this.fireEvent("expanded", this);
35841             },
35842             scope: this,
35843             block: true
35844         });
35845     },
35846
35847     anchors : {
35848         "west" : "left",
35849         "east" : "right",
35850         "north" : "top",
35851         "south" : "bottom"
35852     },
35853
35854     sanchors : {
35855         "west" : "l",
35856         "east" : "r",
35857         "north" : "t",
35858         "south" : "b"
35859     },
35860
35861     canchors : {
35862         "west" : "tl-tr",
35863         "east" : "tr-tl",
35864         "north" : "tl-bl",
35865         "south" : "bl-tl"
35866     },
35867
35868     getAnchor : function(){
35869         return this.anchors[this.position];
35870     },
35871
35872     getCollapseAnchor : function(){
35873         return this.canchors[this.position];
35874     },
35875
35876     getSlideAnchor : function(){
35877         return this.sanchors[this.position];
35878     },
35879
35880     getAlignAdj : function(){
35881         var cm = this.cmargins;
35882         switch(this.position){
35883             case "west":
35884                 return [0, 0];
35885             break;
35886             case "east":
35887                 return [0, 0];
35888             break;
35889             case "north":
35890                 return [0, 0];
35891             break;
35892             case "south":
35893                 return [0, 0];
35894             break;
35895         }
35896     },
35897
35898     getExpandAdj : function(){
35899         var c = this.collapsedEl, cm = this.cmargins;
35900         switch(this.position){
35901             case "west":
35902                 return [-(cm.right+c.getWidth()+cm.left), 0];
35903             break;
35904             case "east":
35905                 return [cm.right+c.getWidth()+cm.left, 0];
35906             break;
35907             case "north":
35908                 return [0, -(cm.top+cm.bottom+c.getHeight())];
35909             break;
35910             case "south":
35911                 return [0, cm.top+cm.bottom+c.getHeight()];
35912             break;
35913         }
35914     }
35915 });/*
35916  * Based on:
35917  * Ext JS Library 1.1.1
35918  * Copyright(c) 2006-2007, Ext JS, LLC.
35919  *
35920  * Originally Released Under LGPL - original licence link has changed is not relivant.
35921  *
35922  * Fork - LGPL
35923  * <script type="text/javascript">
35924  */
35925 /*
35926  * These classes are private internal classes
35927  */
35928 Roo.bootstrap.layout.Center = function(config){
35929     config.region = "center";
35930     Roo.bootstrap.layout.Region.call(this, config);
35931     this.visible = true;
35932     this.minWidth = config.minWidth || 20;
35933     this.minHeight = config.minHeight || 20;
35934 };
35935
35936 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
35937     hide : function(){
35938         // center panel can't be hidden
35939     },
35940     
35941     show : function(){
35942         // center panel can't be hidden
35943     },
35944     
35945     getMinWidth: function(){
35946         return this.minWidth;
35947     },
35948     
35949     getMinHeight: function(){
35950         return this.minHeight;
35951     }
35952 });
35953
35954
35955
35956
35957  
35958
35959
35960
35961
35962
35963 Roo.bootstrap.layout.North = function(config)
35964 {
35965     config.region = 'north';
35966     config.cursor = 'n-resize';
35967     
35968     Roo.bootstrap.layout.Split.call(this, config);
35969     
35970     
35971     if(this.split){
35972         this.split.placement = Roo.bootstrap.SplitBar.TOP;
35973         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
35974         this.split.el.addClass("roo-layout-split-v");
35975     }
35976     var size = config.initialSize || config.height;
35977     if(typeof size != "undefined"){
35978         this.el.setHeight(size);
35979     }
35980 };
35981 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
35982 {
35983     orientation: Roo.bootstrap.SplitBar.VERTICAL,
35984     
35985     
35986     
35987     getBox : function(){
35988         if(this.collapsed){
35989             return this.collapsedEl.getBox();
35990         }
35991         var box = this.el.getBox();
35992         if(this.split){
35993             box.height += this.split.el.getHeight();
35994         }
35995         return box;
35996     },
35997     
35998     updateBox : function(box){
35999         if(this.split && !this.collapsed){
36000             box.height -= this.split.el.getHeight();
36001             this.split.el.setLeft(box.x);
36002             this.split.el.setTop(box.y+box.height);
36003             this.split.el.setWidth(box.width);
36004         }
36005         if(this.collapsed){
36006             this.updateBody(box.width, null);
36007         }
36008         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36009     }
36010 });
36011
36012
36013
36014
36015
36016 Roo.bootstrap.layout.South = function(config){
36017     config.region = 'south';
36018     config.cursor = 's-resize';
36019     Roo.bootstrap.layout.Split.call(this, config);
36020     if(this.split){
36021         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36022         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36023         this.split.el.addClass("roo-layout-split-v");
36024     }
36025     var size = config.initialSize || config.height;
36026     if(typeof size != "undefined"){
36027         this.el.setHeight(size);
36028     }
36029 };
36030
36031 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36032     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36033     getBox : function(){
36034         if(this.collapsed){
36035             return this.collapsedEl.getBox();
36036         }
36037         var box = this.el.getBox();
36038         if(this.split){
36039             var sh = this.split.el.getHeight();
36040             box.height += sh;
36041             box.y -= sh;
36042         }
36043         return box;
36044     },
36045     
36046     updateBox : function(box){
36047         if(this.split && !this.collapsed){
36048             var sh = this.split.el.getHeight();
36049             box.height -= sh;
36050             box.y += sh;
36051             this.split.el.setLeft(box.x);
36052             this.split.el.setTop(box.y-sh);
36053             this.split.el.setWidth(box.width);
36054         }
36055         if(this.collapsed){
36056             this.updateBody(box.width, null);
36057         }
36058         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36059     }
36060 });
36061
36062 Roo.bootstrap.layout.East = function(config){
36063     config.region = "east";
36064     config.cursor = "e-resize";
36065     Roo.bootstrap.layout.Split.call(this, config);
36066     if(this.split){
36067         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36068         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36069         this.split.el.addClass("roo-layout-split-h");
36070     }
36071     var size = config.initialSize || config.width;
36072     if(typeof size != "undefined"){
36073         this.el.setWidth(size);
36074     }
36075 };
36076 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36077     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36078     getBox : function(){
36079         if(this.collapsed){
36080             return this.collapsedEl.getBox();
36081         }
36082         var box = this.el.getBox();
36083         if(this.split){
36084             var sw = this.split.el.getWidth();
36085             box.width += sw;
36086             box.x -= sw;
36087         }
36088         return box;
36089     },
36090
36091     updateBox : function(box){
36092         if(this.split && !this.collapsed){
36093             var sw = this.split.el.getWidth();
36094             box.width -= sw;
36095             this.split.el.setLeft(box.x);
36096             this.split.el.setTop(box.y);
36097             this.split.el.setHeight(box.height);
36098             box.x += sw;
36099         }
36100         if(this.collapsed){
36101             this.updateBody(null, box.height);
36102         }
36103         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36104     }
36105 });
36106
36107 Roo.bootstrap.layout.West = function(config){
36108     config.region = "west";
36109     config.cursor = "w-resize";
36110     
36111     Roo.bootstrap.layout.Split.call(this, config);
36112     if(this.split){
36113         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36114         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36115         this.split.el.addClass("roo-layout-split-h");
36116     }
36117     
36118 };
36119 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36120     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36121     
36122     onRender: function(ctr, pos)
36123     {
36124         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36125         var size = this.config.initialSize || this.config.width;
36126         if(typeof size != "undefined"){
36127             this.el.setWidth(size);
36128         }
36129     },
36130     
36131     getBox : function(){
36132         if(this.collapsed){
36133             return this.collapsedEl.getBox();
36134         }
36135         var box = this.el.getBox();
36136         if(this.split){
36137             box.width += this.split.el.getWidth();
36138         }
36139         return box;
36140     },
36141     
36142     updateBox : function(box){
36143         if(this.split && !this.collapsed){
36144             var sw = this.split.el.getWidth();
36145             box.width -= sw;
36146             this.split.el.setLeft(box.x+box.width);
36147             this.split.el.setTop(box.y);
36148             this.split.el.setHeight(box.height);
36149         }
36150         if(this.collapsed){
36151             this.updateBody(null, box.height);
36152         }
36153         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36154     }
36155 });
36156 Roo.namespace("Roo.bootstrap.panel");/*
36157  * Based on:
36158  * Ext JS Library 1.1.1
36159  * Copyright(c) 2006-2007, Ext JS, LLC.
36160  *
36161  * Originally Released Under LGPL - original licence link has changed is not relivant.
36162  *
36163  * Fork - LGPL
36164  * <script type="text/javascript">
36165  */
36166 /**
36167  * @class Roo.ContentPanel
36168  * @extends Roo.util.Observable
36169  * A basic ContentPanel element.
36170  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36171  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36172  * @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
36173  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36174  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36175  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36176  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36177  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36178  * @cfg {String} title          The title for this panel
36179  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36180  * @cfg {String} url            Calls {@link #setUrl} with this value
36181  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36182  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36183  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36184  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36185  * @cfg {Boolean} badges render the badges
36186
36187  * @constructor
36188  * Create a new ContentPanel.
36189  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36190  * @param {String/Object} config A string to set only the title or a config object
36191  * @param {String} content (optional) Set the HTML content for this panel
36192  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36193  */
36194 Roo.bootstrap.panel.Content = function( config){
36195     
36196     this.tpl = config.tpl || false;
36197     
36198     var el = config.el;
36199     var content = config.content;
36200
36201     if(config.autoCreate){ // xtype is available if this is called from factory
36202         el = Roo.id();
36203     }
36204     this.el = Roo.get(el);
36205     if(!this.el && config && config.autoCreate){
36206         if(typeof config.autoCreate == "object"){
36207             if(!config.autoCreate.id){
36208                 config.autoCreate.id = config.id||el;
36209             }
36210             this.el = Roo.DomHelper.append(document.body,
36211                         config.autoCreate, true);
36212         }else{
36213             var elcfg =  {   tag: "div",
36214                             cls: "roo-layout-inactive-content",
36215                             id: config.id||el
36216                             };
36217             if (config.html) {
36218                 elcfg.html = config.html;
36219                 
36220             }
36221                         
36222             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36223         }
36224     } 
36225     this.closable = false;
36226     this.loaded = false;
36227     this.active = false;
36228    
36229       
36230     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36231         
36232         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36233         
36234         this.wrapEl = this.el; //this.el.wrap();
36235         var ti = [];
36236         if (config.toolbar.items) {
36237             ti = config.toolbar.items ;
36238             delete config.toolbar.items ;
36239         }
36240         
36241         var nitems = [];
36242         this.toolbar.render(this.wrapEl, 'before');
36243         for(var i =0;i < ti.length;i++) {
36244           //  Roo.log(['add child', items[i]]);
36245             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36246         }
36247         this.toolbar.items = nitems;
36248         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36249         delete config.toolbar;
36250         
36251     }
36252     /*
36253     // xtype created footer. - not sure if will work as we normally have to render first..
36254     if (this.footer && !this.footer.el && this.footer.xtype) {
36255         if (!this.wrapEl) {
36256             this.wrapEl = this.el.wrap();
36257         }
36258     
36259         this.footer.container = this.wrapEl.createChild();
36260          
36261         this.footer = Roo.factory(this.footer, Roo);
36262         
36263     }
36264     */
36265     
36266      if(typeof config == "string"){
36267         this.title = config;
36268     }else{
36269         Roo.apply(this, config);
36270     }
36271     
36272     if(this.resizeEl){
36273         this.resizeEl = Roo.get(this.resizeEl, true);
36274     }else{
36275         this.resizeEl = this.el;
36276     }
36277     // handle view.xtype
36278     
36279  
36280     
36281     
36282     this.addEvents({
36283         /**
36284          * @event activate
36285          * Fires when this panel is activated. 
36286          * @param {Roo.ContentPanel} this
36287          */
36288         "activate" : true,
36289         /**
36290          * @event deactivate
36291          * Fires when this panel is activated. 
36292          * @param {Roo.ContentPanel} this
36293          */
36294         "deactivate" : true,
36295
36296         /**
36297          * @event resize
36298          * Fires when this panel is resized if fitToFrame is true.
36299          * @param {Roo.ContentPanel} this
36300          * @param {Number} width The width after any component adjustments
36301          * @param {Number} height The height after any component adjustments
36302          */
36303         "resize" : true,
36304         
36305          /**
36306          * @event render
36307          * Fires when this tab is created
36308          * @param {Roo.ContentPanel} this
36309          */
36310         "render" : true
36311         
36312         
36313         
36314     });
36315     
36316
36317     
36318     
36319     if(this.autoScroll){
36320         this.resizeEl.setStyle("overflow", "auto");
36321     } else {
36322         // fix randome scrolling
36323         //this.el.on('scroll', function() {
36324         //    Roo.log('fix random scolling');
36325         //    this.scrollTo('top',0); 
36326         //});
36327     }
36328     content = content || this.content;
36329     if(content){
36330         this.setContent(content);
36331     }
36332     if(config && config.url){
36333         this.setUrl(this.url, this.params, this.loadOnce);
36334     }
36335     
36336     
36337     
36338     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36339     
36340     if (this.view && typeof(this.view.xtype) != 'undefined') {
36341         this.view.el = this.el.appendChild(document.createElement("div"));
36342         this.view = Roo.factory(this.view); 
36343         this.view.render  &&  this.view.render(false, '');  
36344     }
36345     
36346     
36347     this.fireEvent('render', this);
36348 };
36349
36350 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36351     
36352     tabTip : '',
36353     
36354     setRegion : function(region){
36355         this.region = region;
36356         this.setActiveClass(region && !this.background);
36357     },
36358     
36359     
36360     setActiveClass: function(state)
36361     {
36362         if(state){
36363            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36364            this.el.setStyle('position','relative');
36365         }else{
36366            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36367            this.el.setStyle('position', 'absolute');
36368         } 
36369     },
36370     
36371     /**
36372      * Returns the toolbar for this Panel if one was configured. 
36373      * @return {Roo.Toolbar} 
36374      */
36375     getToolbar : function(){
36376         return this.toolbar;
36377     },
36378     
36379     setActiveState : function(active)
36380     {
36381         this.active = active;
36382         this.setActiveClass(active);
36383         if(!active){
36384             this.fireEvent("deactivate", this);
36385         }else{
36386             this.fireEvent("activate", this);
36387         }
36388     },
36389     /**
36390      * Updates this panel's element
36391      * @param {String} content The new content
36392      * @param {Boolean} loadScripts (optional) true to look for and process scripts
36393     */
36394     setContent : function(content, loadScripts){
36395         this.el.update(content, loadScripts);
36396     },
36397
36398     ignoreResize : function(w, h){
36399         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36400             return true;
36401         }else{
36402             this.lastSize = {width: w, height: h};
36403             return false;
36404         }
36405     },
36406     /**
36407      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36408      * @return {Roo.UpdateManager} The UpdateManager
36409      */
36410     getUpdateManager : function(){
36411         return this.el.getUpdateManager();
36412     },
36413      /**
36414      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36415      * @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:
36416 <pre><code>
36417 panel.load({
36418     url: "your-url.php",
36419     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36420     callback: yourFunction,
36421     scope: yourObject, //(optional scope)
36422     discardUrl: false,
36423     nocache: false,
36424     text: "Loading...",
36425     timeout: 30,
36426     scripts: false
36427 });
36428 </code></pre>
36429      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
36430      * 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.
36431      * @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}
36432      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
36433      * @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.
36434      * @return {Roo.ContentPanel} this
36435      */
36436     load : function(){
36437         var um = this.el.getUpdateManager();
36438         um.update.apply(um, arguments);
36439         return this;
36440     },
36441
36442
36443     /**
36444      * 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.
36445      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
36446      * @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)
36447      * @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)
36448      * @return {Roo.UpdateManager} The UpdateManager
36449      */
36450     setUrl : function(url, params, loadOnce){
36451         if(this.refreshDelegate){
36452             this.removeListener("activate", this.refreshDelegate);
36453         }
36454         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36455         this.on("activate", this.refreshDelegate);
36456         return this.el.getUpdateManager();
36457     },
36458     
36459     _handleRefresh : function(url, params, loadOnce){
36460         if(!loadOnce || !this.loaded){
36461             var updater = this.el.getUpdateManager();
36462             updater.update(url, params, this._setLoaded.createDelegate(this));
36463         }
36464     },
36465     
36466     _setLoaded : function(){
36467         this.loaded = true;
36468     }, 
36469     
36470     /**
36471      * Returns this panel's id
36472      * @return {String} 
36473      */
36474     getId : function(){
36475         return this.el.id;
36476     },
36477     
36478     /** 
36479      * Returns this panel's element - used by regiosn to add.
36480      * @return {Roo.Element} 
36481      */
36482     getEl : function(){
36483         return this.wrapEl || this.el;
36484     },
36485     
36486    
36487     
36488     adjustForComponents : function(width, height)
36489     {
36490         //Roo.log('adjustForComponents ');
36491         if(this.resizeEl != this.el){
36492             width -= this.el.getFrameWidth('lr');
36493             height -= this.el.getFrameWidth('tb');
36494         }
36495         if(this.toolbar){
36496             var te = this.toolbar.getEl();
36497             te.setWidth(width);
36498             height -= te.getHeight();
36499         }
36500         if(this.footer){
36501             var te = this.footer.getEl();
36502             te.setWidth(width);
36503             height -= te.getHeight();
36504         }
36505         
36506         
36507         if(this.adjustments){
36508             width += this.adjustments[0];
36509             height += this.adjustments[1];
36510         }
36511         return {"width": width, "height": height};
36512     },
36513     
36514     setSize : function(width, height){
36515         if(this.fitToFrame && !this.ignoreResize(width, height)){
36516             if(this.fitContainer && this.resizeEl != this.el){
36517                 this.el.setSize(width, height);
36518             }
36519             var size = this.adjustForComponents(width, height);
36520             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
36521             this.fireEvent('resize', this, size.width, size.height);
36522         }
36523     },
36524     
36525     /**
36526      * Returns this panel's title
36527      * @return {String} 
36528      */
36529     getTitle : function(){
36530         
36531         if (typeof(this.title) != 'object') {
36532             return this.title;
36533         }
36534         
36535         var t = '';
36536         for (var k in this.title) {
36537             if (!this.title.hasOwnProperty(k)) {
36538                 continue;
36539             }
36540             
36541             if (k.indexOf('-') >= 0) {
36542                 var s = k.split('-');
36543                 for (var i = 0; i<s.length; i++) {
36544                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
36545                 }
36546             } else {
36547                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
36548             }
36549         }
36550         return t;
36551     },
36552     
36553     /**
36554      * Set this panel's title
36555      * @param {String} title
36556      */
36557     setTitle : function(title){
36558         this.title = title;
36559         if(this.region){
36560             this.region.updatePanelTitle(this, title);
36561         }
36562     },
36563     
36564     /**
36565      * Returns true is this panel was configured to be closable
36566      * @return {Boolean} 
36567      */
36568     isClosable : function(){
36569         return this.closable;
36570     },
36571     
36572     beforeSlide : function(){
36573         this.el.clip();
36574         this.resizeEl.clip();
36575     },
36576     
36577     afterSlide : function(){
36578         this.el.unclip();
36579         this.resizeEl.unclip();
36580     },
36581     
36582     /**
36583      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
36584      *   Will fail silently if the {@link #setUrl} method has not been called.
36585      *   This does not activate the panel, just updates its content.
36586      */
36587     refresh : function(){
36588         if(this.refreshDelegate){
36589            this.loaded = false;
36590            this.refreshDelegate();
36591         }
36592     },
36593     
36594     /**
36595      * Destroys this panel
36596      */
36597     destroy : function(){
36598         this.el.removeAllListeners();
36599         var tempEl = document.createElement("span");
36600         tempEl.appendChild(this.el.dom);
36601         tempEl.innerHTML = "";
36602         this.el.remove();
36603         this.el = null;
36604     },
36605     
36606     /**
36607      * form - if the content panel contains a form - this is a reference to it.
36608      * @type {Roo.form.Form}
36609      */
36610     form : false,
36611     /**
36612      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
36613      *    This contains a reference to it.
36614      * @type {Roo.View}
36615      */
36616     view : false,
36617     
36618       /**
36619      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
36620      * <pre><code>
36621
36622 layout.addxtype({
36623        xtype : 'Form',
36624        items: [ .... ]
36625    }
36626 );
36627
36628 </code></pre>
36629      * @param {Object} cfg Xtype definition of item to add.
36630      */
36631     
36632     
36633     getChildContainer: function () {
36634         return this.getEl();
36635     }
36636     
36637     
36638     /*
36639         var  ret = new Roo.factory(cfg);
36640         return ret;
36641         
36642         
36643         // add form..
36644         if (cfg.xtype.match(/^Form$/)) {
36645             
36646             var el;
36647             //if (this.footer) {
36648             //    el = this.footer.container.insertSibling(false, 'before');
36649             //} else {
36650                 el = this.el.createChild();
36651             //}
36652
36653             this.form = new  Roo.form.Form(cfg);
36654             
36655             
36656             if ( this.form.allItems.length) {
36657                 this.form.render(el.dom);
36658             }
36659             return this.form;
36660         }
36661         // should only have one of theses..
36662         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
36663             // views.. should not be just added - used named prop 'view''
36664             
36665             cfg.el = this.el.appendChild(document.createElement("div"));
36666             // factory?
36667             
36668             var ret = new Roo.factory(cfg);
36669              
36670              ret.render && ret.render(false, ''); // render blank..
36671             this.view = ret;
36672             return ret;
36673         }
36674         return false;
36675     }
36676     \*/
36677 });
36678  
36679 /**
36680  * @class Roo.bootstrap.panel.Grid
36681  * @extends Roo.bootstrap.panel.Content
36682  * @constructor
36683  * Create a new GridPanel.
36684  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
36685  * @param {Object} config A the config object
36686   
36687  */
36688
36689
36690
36691 Roo.bootstrap.panel.Grid = function(config)
36692 {
36693     
36694       
36695     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
36696         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
36697
36698     config.el = this.wrapper;
36699     //this.el = this.wrapper;
36700     
36701       if (config.container) {
36702         // ctor'ed from a Border/panel.grid
36703         
36704         
36705         this.wrapper.setStyle("overflow", "hidden");
36706         this.wrapper.addClass('roo-grid-container');
36707
36708     }
36709     
36710     
36711     if(config.toolbar){
36712         var tool_el = this.wrapper.createChild();    
36713         this.toolbar = Roo.factory(config.toolbar);
36714         var ti = [];
36715         if (config.toolbar.items) {
36716             ti = config.toolbar.items ;
36717             delete config.toolbar.items ;
36718         }
36719         
36720         var nitems = [];
36721         this.toolbar.render(tool_el);
36722         for(var i =0;i < ti.length;i++) {
36723           //  Roo.log(['add child', items[i]]);
36724             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36725         }
36726         this.toolbar.items = nitems;
36727         
36728         delete config.toolbar;
36729     }
36730     
36731     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
36732     config.grid.scrollBody = true;;
36733     config.grid.monitorWindowResize = false; // turn off autosizing
36734     config.grid.autoHeight = false;
36735     config.grid.autoWidth = false;
36736     
36737     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
36738     
36739     if (config.background) {
36740         // render grid on panel activation (if panel background)
36741         this.on('activate', function(gp) {
36742             if (!gp.grid.rendered) {
36743                 gp.grid.render(this.wrapper);
36744                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
36745             }
36746         });
36747             
36748     } else {
36749         this.grid.render(this.wrapper);
36750         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
36751
36752     }
36753     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
36754     // ??? needed ??? config.el = this.wrapper;
36755     
36756     
36757     
36758   
36759     // xtype created footer. - not sure if will work as we normally have to render first..
36760     if (this.footer && !this.footer.el && this.footer.xtype) {
36761         
36762         var ctr = this.grid.getView().getFooterPanel(true);
36763         this.footer.dataSource = this.grid.dataSource;
36764         this.footer = Roo.factory(this.footer, Roo);
36765         this.footer.render(ctr);
36766         
36767     }
36768     
36769     
36770     
36771     
36772      
36773 };
36774
36775 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
36776     getId : function(){
36777         return this.grid.id;
36778     },
36779     
36780     /**
36781      * Returns the grid for this panel
36782      * @return {Roo.bootstrap.Table} 
36783      */
36784     getGrid : function(){
36785         return this.grid;    
36786     },
36787     
36788     setSize : function(width, height){
36789         if(!this.ignoreResize(width, height)){
36790             var grid = this.grid;
36791             var size = this.adjustForComponents(width, height);
36792             var gridel = grid.getGridEl();
36793             gridel.setSize(size.width, size.height);
36794             /*
36795             var thd = grid.getGridEl().select('thead',true).first();
36796             var tbd = grid.getGridEl().select('tbody', true).first();
36797             if (tbd) {
36798                 tbd.setSize(width, height - thd.getHeight());
36799             }
36800             */
36801             grid.autoSize();
36802         }
36803     },
36804      
36805     
36806     
36807     beforeSlide : function(){
36808         this.grid.getView().scroller.clip();
36809     },
36810     
36811     afterSlide : function(){
36812         this.grid.getView().scroller.unclip();
36813     },
36814     
36815     destroy : function(){
36816         this.grid.destroy();
36817         delete this.grid;
36818         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
36819     }
36820 });
36821
36822 /**
36823  * @class Roo.bootstrap.panel.Nest
36824  * @extends Roo.bootstrap.panel.Content
36825  * @constructor
36826  * Create a new Panel, that can contain a layout.Border.
36827  * 
36828  * 
36829  * @param {Roo.BorderLayout} layout The layout for this panel
36830  * @param {String/Object} config A string to set only the title or a config object
36831  */
36832 Roo.bootstrap.panel.Nest = function(config)
36833 {
36834     // construct with only one argument..
36835     /* FIXME - implement nicer consturctors
36836     if (layout.layout) {
36837         config = layout;
36838         layout = config.layout;
36839         delete config.layout;
36840     }
36841     if (layout.xtype && !layout.getEl) {
36842         // then layout needs constructing..
36843         layout = Roo.factory(layout, Roo);
36844     }
36845     */
36846     
36847     config.el =  config.layout.getEl();
36848     
36849     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
36850     
36851     config.layout.monitorWindowResize = false; // turn off autosizing
36852     this.layout = config.layout;
36853     this.layout.getEl().addClass("roo-layout-nested-layout");
36854     
36855     
36856     
36857     
36858 };
36859
36860 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
36861
36862     setSize : function(width, height){
36863         if(!this.ignoreResize(width, height)){
36864             var size = this.adjustForComponents(width, height);
36865             var el = this.layout.getEl();
36866             if (size.height < 1) {
36867                 el.setWidth(size.width);   
36868             } else {
36869                 el.setSize(size.width, size.height);
36870             }
36871             var touch = el.dom.offsetWidth;
36872             this.layout.layout();
36873             // ie requires a double layout on the first pass
36874             if(Roo.isIE && !this.initialized){
36875                 this.initialized = true;
36876                 this.layout.layout();
36877             }
36878         }
36879     },
36880     
36881     // activate all subpanels if not currently active..
36882     
36883     setActiveState : function(active){
36884         this.active = active;
36885         this.setActiveClass(active);
36886         
36887         if(!active){
36888             this.fireEvent("deactivate", this);
36889             return;
36890         }
36891         
36892         this.fireEvent("activate", this);
36893         // not sure if this should happen before or after..
36894         if (!this.layout) {
36895             return; // should not happen..
36896         }
36897         var reg = false;
36898         for (var r in this.layout.regions) {
36899             reg = this.layout.getRegion(r);
36900             if (reg.getActivePanel()) {
36901                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
36902                 reg.setActivePanel(reg.getActivePanel());
36903                 continue;
36904             }
36905             if (!reg.panels.length) {
36906                 continue;
36907             }
36908             reg.showPanel(reg.getPanel(0));
36909         }
36910         
36911         
36912         
36913         
36914     },
36915     
36916     /**
36917      * Returns the nested BorderLayout for this panel
36918      * @return {Roo.BorderLayout} 
36919      */
36920     getLayout : function(){
36921         return this.layout;
36922     },
36923     
36924      /**
36925      * Adds a xtype elements to the layout of the nested panel
36926      * <pre><code>
36927
36928 panel.addxtype({
36929        xtype : 'ContentPanel',
36930        region: 'west',
36931        items: [ .... ]
36932    }
36933 );
36934
36935 panel.addxtype({
36936         xtype : 'NestedLayoutPanel',
36937         region: 'west',
36938         layout: {
36939            center: { },
36940            west: { }   
36941         },
36942         items : [ ... list of content panels or nested layout panels.. ]
36943    }
36944 );
36945 </code></pre>
36946      * @param {Object} cfg Xtype definition of item to add.
36947      */
36948     addxtype : function(cfg) {
36949         return this.layout.addxtype(cfg);
36950     
36951     }
36952 });        /*
36953  * Based on:
36954  * Ext JS Library 1.1.1
36955  * Copyright(c) 2006-2007, Ext JS, LLC.
36956  *
36957  * Originally Released Under LGPL - original licence link has changed is not relivant.
36958  *
36959  * Fork - LGPL
36960  * <script type="text/javascript">
36961  */
36962 /**
36963  * @class Roo.TabPanel
36964  * @extends Roo.util.Observable
36965  * A lightweight tab container.
36966  * <br><br>
36967  * Usage:
36968  * <pre><code>
36969 // basic tabs 1, built from existing content
36970 var tabs = new Roo.TabPanel("tabs1");
36971 tabs.addTab("script", "View Script");
36972 tabs.addTab("markup", "View Markup");
36973 tabs.activate("script");
36974
36975 // more advanced tabs, built from javascript
36976 var jtabs = new Roo.TabPanel("jtabs");
36977 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
36978
36979 // set up the UpdateManager
36980 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
36981 var updater = tab2.getUpdateManager();
36982 updater.setDefaultUrl("ajax1.htm");
36983 tab2.on('activate', updater.refresh, updater, true);
36984
36985 // Use setUrl for Ajax loading
36986 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
36987 tab3.setUrl("ajax2.htm", null, true);
36988
36989 // Disabled tab
36990 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
36991 tab4.disable();
36992
36993 jtabs.activate("jtabs-1");
36994  * </code></pre>
36995  * @constructor
36996  * Create a new TabPanel.
36997  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
36998  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
36999  */
37000 Roo.bootstrap.panel.Tabs = function(config){
37001     /**
37002     * The container element for this TabPanel.
37003     * @type Roo.Element
37004     */
37005     this.el = Roo.get(config.el);
37006     delete config.el;
37007     if(config){
37008         if(typeof config == "boolean"){
37009             this.tabPosition = config ? "bottom" : "top";
37010         }else{
37011             Roo.apply(this, config);
37012         }
37013     }
37014     
37015     if(this.tabPosition == "bottom"){
37016         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37017         this.el.addClass("roo-tabs-bottom");
37018     }
37019     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37020     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37021     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37022     if(Roo.isIE){
37023         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37024     }
37025     if(this.tabPosition != "bottom"){
37026         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37027          * @type Roo.Element
37028          */
37029         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37030         this.el.addClass("roo-tabs-top");
37031     }
37032     this.items = [];
37033
37034     this.bodyEl.setStyle("position", "relative");
37035
37036     this.active = null;
37037     this.activateDelegate = this.activate.createDelegate(this);
37038
37039     this.addEvents({
37040         /**
37041          * @event tabchange
37042          * Fires when the active tab changes
37043          * @param {Roo.TabPanel} this
37044          * @param {Roo.TabPanelItem} activePanel The new active tab
37045          */
37046         "tabchange": true,
37047         /**
37048          * @event beforetabchange
37049          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37050          * @param {Roo.TabPanel} this
37051          * @param {Object} e Set cancel to true on this object to cancel the tab change
37052          * @param {Roo.TabPanelItem} tab The tab being changed to
37053          */
37054         "beforetabchange" : true
37055     });
37056
37057     Roo.EventManager.onWindowResize(this.onResize, this);
37058     this.cpad = this.el.getPadding("lr");
37059     this.hiddenCount = 0;
37060
37061
37062     // toolbar on the tabbar support...
37063     if (this.toolbar) {
37064         alert("no toolbar support yet");
37065         this.toolbar  = false;
37066         /*
37067         var tcfg = this.toolbar;
37068         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37069         this.toolbar = new Roo.Toolbar(tcfg);
37070         if (Roo.isSafari) {
37071             var tbl = tcfg.container.child('table', true);
37072             tbl.setAttribute('width', '100%');
37073         }
37074         */
37075         
37076     }
37077    
37078
37079
37080     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37081 };
37082
37083 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37084     /*
37085      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37086      */
37087     tabPosition : "top",
37088     /*
37089      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37090      */
37091     currentTabWidth : 0,
37092     /*
37093      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37094      */
37095     minTabWidth : 40,
37096     /*
37097      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37098      */
37099     maxTabWidth : 250,
37100     /*
37101      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37102      */
37103     preferredTabWidth : 175,
37104     /*
37105      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37106      */
37107     resizeTabs : false,
37108     /*
37109      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37110      */
37111     monitorResize : true,
37112     /*
37113      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37114      */
37115     toolbar : false,
37116
37117     /**
37118      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37119      * @param {String} id The id of the div to use <b>or create</b>
37120      * @param {String} text The text for the tab
37121      * @param {String} content (optional) Content to put in the TabPanelItem body
37122      * @param {Boolean} closable (optional) True to create a close icon on the tab
37123      * @return {Roo.TabPanelItem} The created TabPanelItem
37124      */
37125     addTab : function(id, text, content, closable, tpl)
37126     {
37127         var item = new Roo.bootstrap.panel.TabItem({
37128             panel: this,
37129             id : id,
37130             text : text,
37131             closable : closable,
37132             tpl : tpl
37133         });
37134         this.addTabItem(item);
37135         if(content){
37136             item.setContent(content);
37137         }
37138         return item;
37139     },
37140
37141     /**
37142      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37143      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37144      * @return {Roo.TabPanelItem}
37145      */
37146     getTab : function(id){
37147         return this.items[id];
37148     },
37149
37150     /**
37151      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37152      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37153      */
37154     hideTab : function(id){
37155         var t = this.items[id];
37156         if(!t.isHidden()){
37157            t.setHidden(true);
37158            this.hiddenCount++;
37159            this.autoSizeTabs();
37160         }
37161     },
37162
37163     /**
37164      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37165      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37166      */
37167     unhideTab : function(id){
37168         var t = this.items[id];
37169         if(t.isHidden()){
37170            t.setHidden(false);
37171            this.hiddenCount--;
37172            this.autoSizeTabs();
37173         }
37174     },
37175
37176     /**
37177      * Adds an existing {@link Roo.TabPanelItem}.
37178      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37179      */
37180     addTabItem : function(item){
37181         this.items[item.id] = item;
37182         this.items.push(item);
37183       //  if(this.resizeTabs){
37184     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37185   //         this.autoSizeTabs();
37186 //        }else{
37187 //            item.autoSize();
37188        // }
37189     },
37190
37191     /**
37192      * Removes a {@link Roo.TabPanelItem}.
37193      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37194      */
37195     removeTab : function(id){
37196         var items = this.items;
37197         var tab = items[id];
37198         if(!tab) { return; }
37199         var index = items.indexOf(tab);
37200         if(this.active == tab && items.length > 1){
37201             var newTab = this.getNextAvailable(index);
37202             if(newTab) {
37203                 newTab.activate();
37204             }
37205         }
37206         this.stripEl.dom.removeChild(tab.pnode.dom);
37207         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37208             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37209         }
37210         items.splice(index, 1);
37211         delete this.items[tab.id];
37212         tab.fireEvent("close", tab);
37213         tab.purgeListeners();
37214         this.autoSizeTabs();
37215     },
37216
37217     getNextAvailable : function(start){
37218         var items = this.items;
37219         var index = start;
37220         // look for a next tab that will slide over to
37221         // replace the one being removed
37222         while(index < items.length){
37223             var item = items[++index];
37224             if(item && !item.isHidden()){
37225                 return item;
37226             }
37227         }
37228         // if one isn't found select the previous tab (on the left)
37229         index = start;
37230         while(index >= 0){
37231             var item = items[--index];
37232             if(item && !item.isHidden()){
37233                 return item;
37234             }
37235         }
37236         return null;
37237     },
37238
37239     /**
37240      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37241      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37242      */
37243     disableTab : function(id){
37244         var tab = this.items[id];
37245         if(tab && this.active != tab){
37246             tab.disable();
37247         }
37248     },
37249
37250     /**
37251      * Enables a {@link Roo.TabPanelItem} that is disabled.
37252      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37253      */
37254     enableTab : function(id){
37255         var tab = this.items[id];
37256         tab.enable();
37257     },
37258
37259     /**
37260      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37261      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37262      * @return {Roo.TabPanelItem} The TabPanelItem.
37263      */
37264     activate : function(id){
37265         var tab = this.items[id];
37266         if(!tab){
37267             return null;
37268         }
37269         if(tab == this.active || tab.disabled){
37270             return tab;
37271         }
37272         var e = {};
37273         this.fireEvent("beforetabchange", this, e, tab);
37274         if(e.cancel !== true && !tab.disabled){
37275             if(this.active){
37276                 this.active.hide();
37277             }
37278             this.active = this.items[id];
37279             this.active.show();
37280             this.fireEvent("tabchange", this, this.active);
37281         }
37282         return tab;
37283     },
37284
37285     /**
37286      * Gets the active {@link Roo.TabPanelItem}.
37287      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37288      */
37289     getActiveTab : function(){
37290         return this.active;
37291     },
37292
37293     /**
37294      * Updates the tab body element to fit the height of the container element
37295      * for overflow scrolling
37296      * @param {Number} targetHeight (optional) Override the starting height from the elements height
37297      */
37298     syncHeight : function(targetHeight){
37299         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37300         var bm = this.bodyEl.getMargins();
37301         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37302         this.bodyEl.setHeight(newHeight);
37303         return newHeight;
37304     },
37305
37306     onResize : function(){
37307         if(this.monitorResize){
37308             this.autoSizeTabs();
37309         }
37310     },
37311
37312     /**
37313      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37314      */
37315     beginUpdate : function(){
37316         this.updating = true;
37317     },
37318
37319     /**
37320      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37321      */
37322     endUpdate : function(){
37323         this.updating = false;
37324         this.autoSizeTabs();
37325     },
37326
37327     /**
37328      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37329      */
37330     autoSizeTabs : function(){
37331         var count = this.items.length;
37332         var vcount = count - this.hiddenCount;
37333         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37334             return;
37335         }
37336         var w = Math.max(this.el.getWidth() - this.cpad, 10);
37337         var availWidth = Math.floor(w / vcount);
37338         var b = this.stripBody;
37339         if(b.getWidth() > w){
37340             var tabs = this.items;
37341             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37342             if(availWidth < this.minTabWidth){
37343                 /*if(!this.sleft){    // incomplete scrolling code
37344                     this.createScrollButtons();
37345                 }
37346                 this.showScroll();
37347                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37348             }
37349         }else{
37350             if(this.currentTabWidth < this.preferredTabWidth){
37351                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37352             }
37353         }
37354     },
37355
37356     /**
37357      * Returns the number of tabs in this TabPanel.
37358      * @return {Number}
37359      */
37360      getCount : function(){
37361          return this.items.length;
37362      },
37363
37364     /**
37365      * Resizes all the tabs to the passed width
37366      * @param {Number} The new width
37367      */
37368     setTabWidth : function(width){
37369         this.currentTabWidth = width;
37370         for(var i = 0, len = this.items.length; i < len; i++) {
37371                 if(!this.items[i].isHidden()) {
37372                 this.items[i].setWidth(width);
37373             }
37374         }
37375     },
37376
37377     /**
37378      * Destroys this TabPanel
37379      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37380      */
37381     destroy : function(removeEl){
37382         Roo.EventManager.removeResizeListener(this.onResize, this);
37383         for(var i = 0, len = this.items.length; i < len; i++){
37384             this.items[i].purgeListeners();
37385         }
37386         if(removeEl === true){
37387             this.el.update("");
37388             this.el.remove();
37389         }
37390     },
37391     
37392     createStrip : function(container)
37393     {
37394         var strip = document.createElement("nav");
37395         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37396         container.appendChild(strip);
37397         return strip;
37398     },
37399     
37400     createStripList : function(strip)
37401     {
37402         // div wrapper for retard IE
37403         // returns the "tr" element.
37404         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
37405         //'<div class="x-tabs-strip-wrap">'+
37406           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
37407           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
37408         return strip.firstChild; //.firstChild.firstChild.firstChild;
37409     },
37410     createBody : function(container)
37411     {
37412         var body = document.createElement("div");
37413         Roo.id(body, "tab-body");
37414         //Roo.fly(body).addClass("x-tabs-body");
37415         Roo.fly(body).addClass("tab-content");
37416         container.appendChild(body);
37417         return body;
37418     },
37419     createItemBody :function(bodyEl, id){
37420         var body = Roo.getDom(id);
37421         if(!body){
37422             body = document.createElement("div");
37423             body.id = id;
37424         }
37425         //Roo.fly(body).addClass("x-tabs-item-body");
37426         Roo.fly(body).addClass("tab-pane");
37427          bodyEl.insertBefore(body, bodyEl.firstChild);
37428         return body;
37429     },
37430     /** @private */
37431     createStripElements :  function(stripEl, text, closable, tpl)
37432     {
37433         var td = document.createElement("li"); // was td..
37434         
37435         
37436         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
37437         
37438         
37439         stripEl.appendChild(td);
37440         /*if(closable){
37441             td.className = "x-tabs-closable";
37442             if(!this.closeTpl){
37443                 this.closeTpl = new Roo.Template(
37444                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37445                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
37446                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
37447                 );
37448             }
37449             var el = this.closeTpl.overwrite(td, {"text": text});
37450             var close = el.getElementsByTagName("div")[0];
37451             var inner = el.getElementsByTagName("em")[0];
37452             return {"el": el, "close": close, "inner": inner};
37453         } else {
37454         */
37455         // not sure what this is..
37456 //            if(!this.tabTpl){
37457                 //this.tabTpl = new Roo.Template(
37458                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37459                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
37460                 //);
37461 //                this.tabTpl = new Roo.Template(
37462 //                   '<a href="#">' +
37463 //                   '<span unselectable="on"' +
37464 //                            (this.disableTooltips ? '' : ' title="{text}"') +
37465 //                            ' >{text}</span></a>'
37466 //                );
37467 //                
37468 //            }
37469
37470
37471             var template = tpl || this.tabTpl || false;
37472             
37473             if(!template){
37474                 
37475                 template = new Roo.Template(
37476                    '<a href="#">' +
37477                    '<span unselectable="on"' +
37478                             (this.disableTooltips ? '' : ' title="{text}"') +
37479                             ' >{text}</span></a>'
37480                 );
37481             }
37482             
37483             switch (typeof(template)) {
37484                 case 'object' :
37485                     break;
37486                 case 'string' :
37487                     template = new Roo.Template(template);
37488                     break;
37489                 default :
37490                     break;
37491             }
37492             
37493             var el = template.overwrite(td, {"text": text});
37494             
37495             var inner = el.getElementsByTagName("span")[0];
37496             
37497             return {"el": el, "inner": inner};
37498             
37499     }
37500         
37501     
37502 });
37503
37504 /**
37505  * @class Roo.TabPanelItem
37506  * @extends Roo.util.Observable
37507  * Represents an individual item (tab plus body) in a TabPanel.
37508  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
37509  * @param {String} id The id of this TabPanelItem
37510  * @param {String} text The text for the tab of this TabPanelItem
37511  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
37512  */
37513 Roo.bootstrap.panel.TabItem = function(config){
37514     /**
37515      * The {@link Roo.TabPanel} this TabPanelItem belongs to
37516      * @type Roo.TabPanel
37517      */
37518     this.tabPanel = config.panel;
37519     /**
37520      * The id for this TabPanelItem
37521      * @type String
37522      */
37523     this.id = config.id;
37524     /** @private */
37525     this.disabled = false;
37526     /** @private */
37527     this.text = config.text;
37528     /** @private */
37529     this.loaded = false;
37530     this.closable = config.closable;
37531
37532     /**
37533      * The body element for this TabPanelItem.
37534      * @type Roo.Element
37535      */
37536     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
37537     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
37538     this.bodyEl.setStyle("display", "block");
37539     this.bodyEl.setStyle("zoom", "1");
37540     //this.hideAction();
37541
37542     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
37543     /** @private */
37544     this.el = Roo.get(els.el);
37545     this.inner = Roo.get(els.inner, true);
37546     this.textEl = Roo.get(this.el.dom.firstChild, true);
37547     this.pnode = Roo.get(els.el.parentNode, true);
37548     this.el.on("mousedown", this.onTabMouseDown, this);
37549     this.el.on("click", this.onTabClick, this);
37550     /** @private */
37551     if(config.closable){
37552         var c = Roo.get(els.close, true);
37553         c.dom.title = this.closeText;
37554         c.addClassOnOver("close-over");
37555         c.on("click", this.closeClick, this);
37556      }
37557
37558     this.addEvents({
37559          /**
37560          * @event activate
37561          * Fires when this tab becomes the active tab.
37562          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37563          * @param {Roo.TabPanelItem} this
37564          */
37565         "activate": true,
37566         /**
37567          * @event beforeclose
37568          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
37569          * @param {Roo.TabPanelItem} this
37570          * @param {Object} e Set cancel to true on this object to cancel the close.
37571          */
37572         "beforeclose": true,
37573         /**
37574          * @event close
37575          * Fires when this tab is closed.
37576          * @param {Roo.TabPanelItem} this
37577          */
37578          "close": true,
37579         /**
37580          * @event deactivate
37581          * Fires when this tab is no longer the active tab.
37582          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37583          * @param {Roo.TabPanelItem} this
37584          */
37585          "deactivate" : true
37586     });
37587     this.hidden = false;
37588
37589     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
37590 };
37591
37592 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
37593            {
37594     purgeListeners : function(){
37595        Roo.util.Observable.prototype.purgeListeners.call(this);
37596        this.el.removeAllListeners();
37597     },
37598     /**
37599      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
37600      */
37601     show : function(){
37602         this.pnode.addClass("active");
37603         this.showAction();
37604         if(Roo.isOpera){
37605             this.tabPanel.stripWrap.repaint();
37606         }
37607         this.fireEvent("activate", this.tabPanel, this);
37608     },
37609
37610     /**
37611      * Returns true if this tab is the active tab.
37612      * @return {Boolean}
37613      */
37614     isActive : function(){
37615         return this.tabPanel.getActiveTab() == this;
37616     },
37617
37618     /**
37619      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
37620      */
37621     hide : function(){
37622         this.pnode.removeClass("active");
37623         this.hideAction();
37624         this.fireEvent("deactivate", this.tabPanel, this);
37625     },
37626
37627     hideAction : function(){
37628         this.bodyEl.hide();
37629         this.bodyEl.setStyle("position", "absolute");
37630         this.bodyEl.setLeft("-20000px");
37631         this.bodyEl.setTop("-20000px");
37632     },
37633
37634     showAction : function(){
37635         this.bodyEl.setStyle("position", "relative");
37636         this.bodyEl.setTop("");
37637         this.bodyEl.setLeft("");
37638         this.bodyEl.show();
37639     },
37640
37641     /**
37642      * Set the tooltip for the tab.
37643      * @param {String} tooltip The tab's tooltip
37644      */
37645     setTooltip : function(text){
37646         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
37647             this.textEl.dom.qtip = text;
37648             this.textEl.dom.removeAttribute('title');
37649         }else{
37650             this.textEl.dom.title = text;
37651         }
37652     },
37653
37654     onTabClick : function(e){
37655         e.preventDefault();
37656         this.tabPanel.activate(this.id);
37657     },
37658
37659     onTabMouseDown : function(e){
37660         e.preventDefault();
37661         this.tabPanel.activate(this.id);
37662     },
37663 /*
37664     getWidth : function(){
37665         return this.inner.getWidth();
37666     },
37667
37668     setWidth : function(width){
37669         var iwidth = width - this.pnode.getPadding("lr");
37670         this.inner.setWidth(iwidth);
37671         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
37672         this.pnode.setWidth(width);
37673     },
37674 */
37675     /**
37676      * Show or hide the tab
37677      * @param {Boolean} hidden True to hide or false to show.
37678      */
37679     setHidden : function(hidden){
37680         this.hidden = hidden;
37681         this.pnode.setStyle("display", hidden ? "none" : "");
37682     },
37683
37684     /**
37685      * Returns true if this tab is "hidden"
37686      * @return {Boolean}
37687      */
37688     isHidden : function(){
37689         return this.hidden;
37690     },
37691
37692     /**
37693      * Returns the text for this tab
37694      * @return {String}
37695      */
37696     getText : function(){
37697         return this.text;
37698     },
37699     /*
37700     autoSize : function(){
37701         //this.el.beginMeasure();
37702         this.textEl.setWidth(1);
37703         /*
37704          *  #2804 [new] Tabs in Roojs
37705          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
37706          */
37707         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
37708         //this.el.endMeasure();
37709     //},
37710
37711     /**
37712      * Sets the text for the tab (Note: this also sets the tooltip text)
37713      * @param {String} text The tab's text and tooltip
37714      */
37715     setText : function(text){
37716         this.text = text;
37717         this.textEl.update(text);
37718         this.setTooltip(text);
37719         //if(!this.tabPanel.resizeTabs){
37720         //    this.autoSize();
37721         //}
37722     },
37723     /**
37724      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
37725      */
37726     activate : function(){
37727         this.tabPanel.activate(this.id);
37728     },
37729
37730     /**
37731      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
37732      */
37733     disable : function(){
37734         if(this.tabPanel.active != this){
37735             this.disabled = true;
37736             this.pnode.addClass("disabled");
37737         }
37738     },
37739
37740     /**
37741      * Enables this TabPanelItem if it was previously disabled.
37742      */
37743     enable : function(){
37744         this.disabled = false;
37745         this.pnode.removeClass("disabled");
37746     },
37747
37748     /**
37749      * Sets the content for this TabPanelItem.
37750      * @param {String} content The content
37751      * @param {Boolean} loadScripts true to look for and load scripts
37752      */
37753     setContent : function(content, loadScripts){
37754         this.bodyEl.update(content, loadScripts);
37755     },
37756
37757     /**
37758      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
37759      * @return {Roo.UpdateManager} The UpdateManager
37760      */
37761     getUpdateManager : function(){
37762         return this.bodyEl.getUpdateManager();
37763     },
37764
37765     /**
37766      * Set a URL to be used to load the content for this TabPanelItem.
37767      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
37768      * @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)
37769      * @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)
37770      * @return {Roo.UpdateManager} The UpdateManager
37771      */
37772     setUrl : function(url, params, loadOnce){
37773         if(this.refreshDelegate){
37774             this.un('activate', this.refreshDelegate);
37775         }
37776         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37777         this.on("activate", this.refreshDelegate);
37778         return this.bodyEl.getUpdateManager();
37779     },
37780
37781     /** @private */
37782     _handleRefresh : function(url, params, loadOnce){
37783         if(!loadOnce || !this.loaded){
37784             var updater = this.bodyEl.getUpdateManager();
37785             updater.update(url, params, this._setLoaded.createDelegate(this));
37786         }
37787     },
37788
37789     /**
37790      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
37791      *   Will fail silently if the setUrl method has not been called.
37792      *   This does not activate the panel, just updates its content.
37793      */
37794     refresh : function(){
37795         if(this.refreshDelegate){
37796            this.loaded = false;
37797            this.refreshDelegate();
37798         }
37799     },
37800
37801     /** @private */
37802     _setLoaded : function(){
37803         this.loaded = true;
37804     },
37805
37806     /** @private */
37807     closeClick : function(e){
37808         var o = {};
37809         e.stopEvent();
37810         this.fireEvent("beforeclose", this, o);
37811         if(o.cancel !== true){
37812             this.tabPanel.removeTab(this.id);
37813         }
37814     },
37815     /**
37816      * The text displayed in the tooltip for the close icon.
37817      * @type String
37818      */
37819     closeText : "Close this tab"
37820 });
37821 /*
37822  * - LGPL
37823  *
37824  * element
37825  * 
37826  */
37827
37828 /**
37829  * @class Roo.bootstrap.PhoneInput
37830  * @extends Roo.bootstrap.TriggerField
37831  * Bootstrap PhoneInput class
37832  * 
37833  * @constructor
37834  * Create a new PhoneInput
37835  * @param {Object} config The config object
37836 */
37837
37838 Roo.bootstrap.PhoneInput = function(config){
37839     
37840     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
37841     
37842     this.addEvents({
37843         'touchviewdisplay' : true
37844     });
37845     
37846     this.country = []; //fetch country JSON
37847 };
37848
37849 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
37850      
37851      list: {},
37852      
37853      listWidth: undefined,
37854      
37855      modalTitle : '', 
37856      
37857      selectedClass: 'active',
37858      
37859      maxHeight: 300,
37860      
37861      minListWidth : 70,
37862      
37863      validClass : "has-success",
37864      
37865      invalidClass: "has-warning",
37866      
37867      //new settings
37868      defaultCountry: 'hk',
37869      
37870      preferedCountries: undefined, //array
37871      
37872      filterCountries: undefined, //array
37873      
37874      displayMode: undefined, //string
37875      
37876      getAutoCreate : function(){
37877          
37878          /*
37879         var countries = Roo.bootstrap.PhoneInput.List;
37880         
37881         for (var i = 0; i < countries.length; i++) {
37882             this.list[countries[i][1]] = {
37883                 name : countries[i][0],
37884                 iso : countries[i][1],
37885                 dial_code : countries[i][2],
37886                 order: countries[i][3] ? countries[i][3] : '',
37887                 area_code: countries[i][4] ? countries[i][4] : ''
37888             };
37889         }
37890         */
37891         
37892         if(this.filterCountries) {
37893             for(var i = 0; i < this.filterCountries.length; i++) {
37894                 delete this.list[this.filterCountries[i]];
37895             }
37896         }
37897         
37898         if (this.preferedCountries) {
37899             //another list??
37900         }
37901         
37902          var align = this.labelAlign || this.parentLabelAlign();
37903          
37904          var id = Roo.id(); //all el??
37905          
37906          var cfg = {
37907              cls: 'form-group'
37908          };
37909          
37910          var input =  {
37911              tag: 'input',
37912              id : id,
37913              type : this.inputType,
37914              cls : 'form-control',
37915              style: 'padding-left: 60px;',
37916              placeholder : this.placeholder || ''
37917          };
37918          
37919          if (this.name) {
37920              input.name = this.name;
37921          }
37922          if (this.size) {
37923              input.cls += ' input-' + this.size;
37924          }
37925          
37926          if (this.disabled) {
37927              input.disabled=true;
37928          }
37929          
37930          var inputblock = input;
37931          
37932          if(this.hasFeedback && !this.allowBlank){
37933              var feedback = {
37934                  tag: 'span',
37935                  cls: 'glyphicon form-control-feedback'
37936              };
37937          }
37938          
37939          inputblock = {
37940              cn :  []
37941          };
37942          
37943          inputblock.cn.push(input);
37944          
37945          if(this.hasFeedback && !this.allowBlank){
37946              inputblock.cls += 'has-feedback';
37947              inputblock.cn.push(feedback);
37948          }
37949          
37950          var box = {
37951              tag: 'div',
37952              cn: [
37953                  {
37954                      tag: 'input',
37955                      type : 'hidden',
37956                      cls: 'form-hidden-field'
37957                  },
37958                  inputblock
37959              ]
37960          };
37961          
37962          var flag = {
37963              tag: 'span',
37964              html: 'flag',
37965              style: 'margin-right:5px',
37966              cls: 'roo-selected-region',
37967              cn: [] //flag position ... (iti-flag-us)
37968          };
37969          
37970          var caret = {
37971              tag: 'span',
37972              cls: 'caret'
37973           };
37974           
37975          if (this.caret != false) {
37976              caret = {
37977                   tag: 'i',
37978                   cls: 'fa fa-' + this.caret
37979              };
37980          }
37981          
37982          var combobox = {
37983              cls: 'roo-select2-container input-group',
37984              cn: []
37985          };
37986          
37987          combobox.cn.push({
37988              tag :'span',
37989              cls : 'input-group-addon btn dropdown-toggle',
37990              style : 'position: absolute; z-index: 4;background: none;border: none; margin-top: 4px; margin-left: 3px; margin-right: 3px;',
37991              cn : [
37992                  flag,
37993                  caret,
37994                  {
37995                      tag: 'span',
37996                      cls: 'combobox-clear',
37997                      cn  : [
37998                          {
37999                              tag : 'i',
38000                              cls: 'icon-remove'
38001                          }
38002                      ]
38003                  }
38004              ]
38005          });
38006          
38007          combobox.cn.push(box);
38008          
38009          if (align ==='left' && this.fieldLabel.length) {
38010              
38011              cfg.cls += ' roo-form-group-label-left';
38012
38013              cfg.cn = [
38014                  {
38015                      tag : 'i',
38016                      cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
38017                      tooltip : 'This field is required'
38018                  },
38019                  {
38020                      tag: 'label',
38021                      'for' :  id,
38022                      cls : 'control-label',
38023                      html : this.fieldLabel
38024
38025                  },
38026                  {
38027                      cls : "", 
38028                      cn: [
38029                          combobox
38030                      ]
38031                  }
38032              ];
38033              
38034              var labelCfg = cfg.cn[1];
38035              var contentCfg = cfg.cn[2];
38036              
38037              if(this.indicatorpos == 'right'){
38038                  cfg.cn = [
38039                      {
38040                          tag: 'label',
38041                          'for' :  id,
38042                          cls : 'control-label',
38043                          cn : [
38044                              {
38045                                  tag : 'span',
38046                                  html : this.fieldLabel
38047                              },
38048                              {
38049                                  tag : 'i',
38050                                  cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
38051                                  tooltip : 'This field is required'
38052                              }
38053                          ]
38054                      },
38055                      {
38056                          cls : "", 
38057                          cn: [
38058                              combobox
38059                          ]
38060                      }
38061
38062                  ];
38063                  
38064                  labelCfg = cfg.cn[0];
38065                  contentCfg = cfg.cn[1];
38066              }
38067              
38068              if(this.labelWidth > 12){
38069                  labelCfg.style = "width: " + this.labelWidth + 'px';
38070              }
38071              
38072              if(this.labelWidth < 13 && this.labelmd == 0){
38073                  this.labelmd = this.labelWidth;
38074              }
38075              
38076              if(this.labellg > 0){
38077                  labelCfg.cls += ' col-lg-' + this.labellg;
38078                  contentCfg.cls += ' col-lg-' + (12 - this.labellg);
38079              }
38080              
38081              if(this.labelmd > 0){
38082                  labelCfg.cls += ' col-md-' + this.labelmd;
38083                  contentCfg.cls += ' col-md-' + (12 - this.labelmd);
38084              }
38085              
38086              if(this.labelsm > 0){
38087                  labelCfg.cls += ' col-sm-' + this.labelsm;
38088                  contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
38089              }
38090              
38091              if(this.labelxs > 0){
38092                  labelCfg.cls += ' col-xs-' + this.labelxs;
38093                  contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
38094              }
38095              
38096          } else if ( this.fieldLabel.length) {
38097  //                Roo.log(" label");
38098              cfg.cn = [
38099                  {
38100                     tag : 'i',
38101                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
38102                     tooltip : 'This field is required'
38103                 },
38104                 {
38105                     tag: 'label',
38106                     //cls : 'input-group-addon',
38107                     html : this.fieldLabel
38108
38109                 },
38110
38111                 combobox
38112
38113              ];
38114              
38115              if(this.indicatorpos == 'right'){
38116                  
38117                  cfg.cn = [
38118                      {
38119                         tag: 'label',
38120                         cn : [
38121                             {
38122                                 tag : 'span',
38123                                 html : this.fieldLabel
38124                             },
38125                             {
38126                                tag : 'i',
38127                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
38128                                tooltip : 'This field is required'
38129                             }
38130                         ]
38131                      },
38132                      combobox
38133                  ];
38134              }
38135          } else {
38136                  cfg = combobox
38137          }
38138          
38139          var settings=this;
38140          ['xs','sm','md','lg'].map(function(size){
38141              if (settings[size]) {
38142                  cfg.cls += ' col-' + size + '-' + settings[size];
38143              }
38144          });
38145          
38146          return cfg;
38147      },
38148      
38149      _initEventsCalled : false,
38150      
38151      initEvents: function()
38152      {   
38153          if (this._initEventsCalled) {
38154              return;
38155          }
38156          
38157          this._initEventsCalled = true;
38158          
38159          this.store =  new Roo.data.Store({
38160              data : Roo.bootstrap.PhoneInput.List,
38161              fields : ['name','iso','dial_code','order','area_code']
38162          });
38163          Roo.log('----------------store----------------');
38164          Roo.log(this.store);
38165          
38166     }
38167      
38168      
38169  });
38170
38171  Roo.apply(Roo.bootstrap.PhoneInput, {
38172      
38173      /**
38174       * iso2 and abbr for all countries
38175       * @type Object
38176       */
38177      List : [
38178          ["Afghanistan (‫افغانستان‬‎)", "af", "93"],
38179          ["Albania (Shqipëri)", "al", "355"],
38180          ["Algeria (‫الجزائر‬‎)", "dz", "213"],
38181          ["American Samoa", "as", "1684"],
38182          ["Andorra", "ad", "376"],
38183          ["Angola", "ao", "244"],
38184          ["Anguilla", "ai", "1264"],
38185          ["Antigua and Barbuda", "ag", "1268"],
38186          ["Argentina", "ar", "54"],
38187          ["Armenia (Հայաստան)", "am", "374"],
38188          ["Aruba", "aw", "297"],
38189          ["Australia", "au", "61", 0],
38190          ["Austria (Österreich)", "at", "43"],
38191          ["Azerbaijan (Azərbaycan)", "az", "994"],
38192          ["Bahamas", "bs", "1242"],
38193          ["Bahrain (‫البحرين‬‎)", "bh", "973"],
38194          ["Bangladesh (বাংলাদেশ)", "bd", "880"],
38195          ["Barbados", "bb", "1246"],
38196          ["Belarus (Беларусь)", "by", "375"],
38197          ["Belgium (België)", "be", "32"],
38198          ["Belize", "bz", "501"],
38199          ["Benin (Bénin)", "bj", "229"],
38200          ["Bermuda", "bm", "1441"],
38201          ["Bhutan (འབྲུག)", "bt", "975"],
38202          ["Bolivia", "bo", "591"],
38203          ["Bosnia and Herzegovina (Босна и Херцеговина)", "ba", "387"],
38204          ["Botswana", "bw", "267"],
38205          ["Brazil (Brasil)", "br", "55"],
38206          ["British Indian Ocean Territory", "io", "246"],
38207          ["British Virgin Islands", "vg", "1284"],
38208          ["Brunei", "bn", "673"],
38209          ["Bulgaria (България)", "bg", "359"],
38210          ["Burkina Faso", "bf", "226"],
38211          ["Burundi (Uburundi)", "bi", "257"],
38212          ["Cambodia (កម្ពុជា)", "kh", "855"],
38213          ["Cameroon (Cameroun)", "cm", "237"],
38214          ["Canada", "ca", "1", 1, ["204", "226", "236", "249", "250", "289", "306", "343", "365", "387", "403", "416", "418", "431", "437", "438", "450", "506", "514", "519", "548", "579", "581", "587", "604", "613", "639", "647", "672", "705", "709", "742", "778", "780", "782", "807", "819", "825", "867", "873", "902", "905"]],
38215          ["Cape Verde (Kabu Verdi)", "cv", "238"],
38216          ["Caribbean Netherlands", "bq", "599", 1],
38217          ["Cayman Islands", "ky", "1345"],
38218          ["Central African Republic (République centrafricaine)", "cf", "236"],
38219          ["Chad (Tchad)", "td", "235"],
38220          ["Chile", "cl", "56"],
38221          ["China (中国)", "cn", "86"],
38222          ["Christmas Island", "cx", "61", 2],
38223          ["Cocos (Keeling) Islands", "cc", "61", 1],
38224          ["Colombia", "co", "57"],
38225          ["Comoros (‫جزر القمر‬‎)", "km", "269"],
38226          ["Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)", "cd", "243"],
38227          ["Congo (Republic) (Congo-Brazzaville)", "cg", "242"],
38228          ["Cook Islands", "ck", "682"],
38229          ["Costa Rica", "cr", "506"],
38230          ["Côte d’Ivoire", "ci", "225"],
38231          ["Croatia (Hrvatska)", "hr", "385"],
38232          ["Cuba", "cu", "53"],
38233          ["Curaçao", "cw", "599", 0],
38234          ["Cyprus (Κύπρος)", "cy", "357"],
38235          ["Czech Republic (Česká republika)", "cz", "420"],
38236          ["Denmark (Danmark)", "dk", "45"],
38237          ["Djibouti", "dj", "253"],
38238          ["Dominica", "dm", "1767"],
38239          ["Dominican Republic (República Dominicana)", "do", "1", 2, ["809", "829", "849"]],
38240          ["Ecuador", "ec", "593"],
38241          ["Egypt (‫مصر‬‎)", "eg", "20"],
38242          ["El Salvador", "sv", "503"],
38243          ["Equatorial Guinea (Guinea Ecuatorial)", "gq", "240"],
38244          ["Eritrea", "er", "291"],
38245          ["Estonia (Eesti)", "ee", "372"],
38246          ["Ethiopia", "et", "251"],
38247          ["Falkland Islands (Islas Malvinas)", "fk", "500"],
38248          ["Faroe Islands (Føroyar)", "fo", "298"],
38249          ["Fiji", "fj", "679"],
38250          ["Finland (Suomi)", "fi", "358", 0],
38251          ["France", "fr", "33"],
38252          ["French Guiana (Guyane française)", "gf", "594"],
38253          ["French Polynesia (Polynésie française)", "pf", "689"],
38254          ["Gabon", "ga", "241"],
38255          ["Gambia", "gm", "220"],
38256          ["Georgia (საქართველო)", "ge", "995"],
38257          ["Germany (Deutschland)", "de", "49"],
38258          ["Ghana (Gaana)", "gh", "233"],
38259          ["Gibraltar", "gi", "350"],
38260          ["Greece (Ελλάδα)", "gr", "30"],
38261          ["Greenland (Kalaallit Nunaat)", "gl", "299"],
38262          ["Grenada", "gd", "1473"],
38263          ["Guadeloupe", "gp", "590", 0],
38264          ["Guam", "gu", "1671"],
38265          ["Guatemala", "gt", "502"],
38266          ["Guernsey", "gg", "44", 1],
38267          ["Guinea (Guinée)", "gn", "224"],
38268          ["Guinea-Bissau (Guiné Bissau)", "gw", "245"],
38269          ["Guyana", "gy", "592"],
38270          ["Haiti", "ht", "509"],
38271          ["Honduras", "hn", "504"],
38272          ["Hong Kong (香港)", "hk", "852"],
38273          ["Hungary (Magyarország)", "hu", "36"],
38274          ["Iceland (Ísland)", "is", "354"],
38275          ["India (भारत)", "in", "91"],
38276          ["Indonesia", "id", "62"],
38277          ["Iran (‫ایران‬‎)", "ir", "98"],
38278          ["Iraq (‫العراق‬‎)", "iq", "964"],
38279          ["Ireland", "ie", "353"],
38280          ["Isle of Man", "im", "44", 2],
38281          ["Israel (‫ישראל‬‎)", "il", "972"],
38282          ["Italy (Italia)", "it", "39", 0],
38283          ["Jamaica", "jm", "1876"],
38284          ["Japan (日本)", "jp", "81"],
38285          ["Jersey", "je", "44", 3],
38286          ["Jordan (‫الأردن‬‎)", "jo", "962"],
38287          ["Kazakhstan (Казахстан)", "kz", "7", 1],
38288          ["Kenya", "ke", "254"],
38289          ["Kiribati", "ki", "686"],
38290          ["Kosovo", "xk", "383"],
38291          ["Kuwait (‫الكويت‬‎)", "kw", "965"],
38292          ["Kyrgyzstan (Кыргызстан)", "kg", "996"],
38293          ["Laos (ລາວ)", "la", "856"],
38294          ["Latvia (Latvija)", "lv", "371"],
38295          ["Lebanon (‫لبنان‬‎)", "lb", "961"],
38296          ["Lesotho", "ls", "266"],
38297          ["Liberia", "lr", "231"],
38298          ["Libya (‫ليبيا‬‎)", "ly", "218"],
38299          ["Liechtenstein", "li", "423"],
38300          ["Lithuania (Lietuva)", "lt", "370"],
38301          ["Luxembourg", "lu", "352"],
38302          ["Macau (澳門)", "mo", "853"],
38303          ["Macedonia (FYROM) (Македонија)", "mk", "389"],
38304          ["Madagascar (Madagasikara)", "mg", "261"],
38305          ["Malawi", "mw", "265"],
38306          ["Malaysia", "my", "60"],
38307          ["Maldives", "mv", "960"],
38308          ["Mali", "ml", "223"],
38309          ["Malta", "mt", "356"],
38310          ["Marshall Islands", "mh", "692"],
38311          ["Martinique", "mq", "596"],
38312          ["Mauritania (‫موريتانيا‬‎)", "mr", "222"],
38313          ["Mauritius (Moris)", "mu", "230"],
38314          ["Mayotte", "yt", "262", 1],
38315          ["Mexico (México)", "mx", "52"],
38316          ["Micronesia", "fm", "691"],
38317          ["Moldova (Republica Moldova)", "md", "373"],
38318          ["Monaco", "mc", "377"],
38319          ["Mongolia (Монгол)", "mn", "976"],
38320          ["Montenegro (Crna Gora)", "me", "382"],
38321          ["Montserrat", "ms", "1664"],
38322          ["Morocco (‫المغرب‬‎)", "ma", "212", 0],
38323          ["Mozambique (Moçambique)", "mz", "258"],
38324          ["Myanmar (Burma) (မြန်မာ)", "mm", "95"],
38325          ["Namibia (Namibië)", "na", "264"],
38326          ["Nauru", "nr", "674"],
38327          ["Nepal (नेपाल)", "np", "977"],
38328          ["Netherlands (Nederland)", "nl", "31"],
38329          ["New Caledonia (Nouvelle-Calédonie)", "nc", "687"],
38330          ["New Zealand", "nz", "64"],
38331          ["Nicaragua", "ni", "505"],
38332          ["Niger (Nijar)", "ne", "227"],
38333          ["Nigeria", "ng", "234"],
38334          ["Niue", "nu", "683"],
38335          ["Norfolk Island", "nf", "672"],
38336          ["North Korea (조선 민주주의 인민 공화국)", "kp", "850"],
38337          ["Northern Mariana Islands", "mp", "1670"],
38338          ["Norway (Norge)", "no", "47", 0],
38339          ["Oman (‫عُمان‬‎)", "om", "968"],
38340          ["Pakistan (‫پاکستان‬‎)", "pk", "92"],
38341          ["Palau", "pw", "680"],
38342          ["Palestine (‫فلسطين‬‎)", "ps", "970"],
38343          ["Panama (Panamá)", "pa", "507"],
38344          ["Papua New Guinea", "pg", "675"],
38345          ["Paraguay", "py", "595"],
38346          ["Peru (Perú)", "pe", "51"],
38347          ["Philippines", "ph", "63"],
38348          ["Poland (Polska)", "pl", "48"],
38349          ["Portugal", "pt", "351"],
38350          ["Puerto Rico", "pr", "1", 3, ["787", "939"]],
38351          ["Qatar (‫قطر‬‎)", "qa", "974"],
38352          ["Réunion (La Réunion)", "re", "262", 0],
38353          ["Romania (România)", "ro", "40"],
38354          ["Russia (Россия)", "ru", "7", 0],
38355          ["Rwanda", "rw", "250"],
38356          ["Saint Barthélemy", "bl", "590", 1],
38357          ["Saint Helena", "sh", "290"],
38358          ["Saint Kitts and Nevis", "kn", "1869"],
38359          ["Saint Lucia", "lc", "1758"],
38360          ["Saint Martin (Saint-Martin (partie française))", "mf", "590", 2],
38361          ["Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)", "pm", "508"],
38362          ["Saint Vincent and the Grenadines", "vc", "1784"],
38363          ["Samoa", "ws", "685"],
38364          ["San Marino", "sm", "378"],
38365          ["São Tomé and Príncipe (São Tomé e Príncipe)", "st", "239"],
38366          ["Saudi Arabia (‫المملكة العربية السعودية‬‎)", "sa", "966"],
38367          ["Senegal (Sénégal)", "sn", "221"],
38368          ["Serbia (Србија)", "rs", "381"],
38369          ["Seychelles", "sc", "248"],
38370          ["Sierra Leone", "sl", "232"],
38371          ["Singapore", "sg", "65"],
38372          ["Sint Maarten", "sx", "1721"],
38373          ["Slovakia (Slovensko)", "sk", "421"],
38374          ["Slovenia (Slovenija)", "si", "386"],
38375          ["Solomon Islands", "sb", "677"],
38376          ["Somalia (Soomaaliya)", "so", "252"],
38377          ["South Africa", "za", "27"],
38378          ["South Korea (대한민국)", "kr", "82"],
38379          ["South Sudan (‫جنوب السودان‬‎)", "ss", "211"],
38380          ["Spain (España)", "es", "34"],
38381          ["Sri Lanka (ශ්‍රී ලංකාව)", "lk", "94"],
38382          ["Sudan (‫السودان‬‎)", "sd", "249"],
38383          ["Suriname", "sr", "597"],
38384          ["Svalbard and Jan Mayen", "sj", "47", 1],
38385          ["Swaziland", "sz", "268"],
38386          ["Sweden (Sverige)", "se", "46"],
38387          ["Switzerland (Schweiz)", "ch", "41"],
38388          ["Syria (‫سوريا‬‎)", "sy", "963"],
38389          ["Taiwan (台灣)", "tw", "886"],
38390          ["Tajikistan", "tj", "992"],
38391          ["Tanzania", "tz", "255"],
38392          ["Thailand (ไทย)", "th", "66"],
38393          ["Timor-Leste", "tl", "670"],
38394          ["Togo", "tg", "228"],
38395          ["Tokelau", "tk", "690"],
38396          ["Tonga", "to", "676"],
38397          ["Trinidad and Tobago", "tt", "1868"],
38398          ["Tunisia (‫تونس‬‎)", "tn", "216"],
38399          ["Turkey (Türkiye)", "tr", "90"],
38400          ["Turkmenistan", "tm", "993"],
38401          ["Turks and Caicos Islands", "tc", "1649"],
38402          ["Tuvalu", "tv", "688"],
38403          ["U.S. Virgin Islands", "vi", "1340"],
38404          ["Uganda", "ug", "256"],
38405          ["Ukraine (Україна)", "ua", "380"],
38406          ["United Arab Emirates (‫الإمارات العربية المتحدة‬‎)", "ae", "971"],
38407          ["United Kingdom", "gb", "44", 0],
38408          ["United States", "us", "1", 0],
38409          ["Uruguay", "uy", "598"],
38410          ["Uzbekistan (Oʻzbekiston)", "uz", "998"],
38411          ["Vanuatu", "vu", "678"],
38412          ["Vatican City (Città del Vaticano)", "va", "39", 1],
38413          ["Venezuela", "ve", "58"],
38414          ["Vietnam (Việt Nam)", "vn", "84"],
38415          ["Wallis and Futuna (Wallis-et-Futuna)", "wf", "681"],
38416          ["Western Sahara (‫الصحراء الغربية‬‎)", "eh", "212", 1],
38417          ["Yemen (‫اليمن‬‎)", "ye", "967"],
38418          ["Zambia", "zm", "260"],
38419          ["Zimbabwe", "zw", "263"],
38420          ["Åland Islands", "ax", "358", 1]
38421      ]
38422  });