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
8119             var zIndex = Roo.bootstrap.Modal.zIndex++;
8120
8121             this.maskEl.top.setStyle('position', 'fixed');
8122             this.maskEl.top.setStyle('z-index', zIndex);
8123             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8124             this.maskEl.top.setXY([0, 0]);
8125             this.maskEl.top.show();
8126
8127             this.maskEl.left.setStyle('position', 'fixed');
8128             this.maskEl.left.setStyle('z-index', zIndex);
8129             this.maskEl.left.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8130             this.maskEl.left.setXY([box.right + this.padding, box.y - this.padding]);
8131             this.maskEl.left.show();
8132
8133             this.maskEl.bottom.setStyle('position', 'fixed');
8134             this.maskEl.bottom.setStyle('z-index', zIndex);
8135             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8136             this.maskEl.bottom.setXY([0, box.bottom + this.padding]);
8137             this.maskEl.bottom.show();
8138
8139             this.maskEl.right.setStyle('position', 'fixed');
8140             this.maskEl.right.setStyle('z-index', zIndex);
8141             this.maskEl.right.setSize(box.x - this.padding, box.height + this.padding * 2);
8142             this.maskEl.right.setXY([0, box.y - this.padding]);
8143             this.maskEl.right.show();
8144
8145
8146             this.toolTip.bindEl = this.target.el;
8147
8148             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8149
8150             var tip = this.target.blankText;
8151
8152             if(this.target.getValue() !== '' && this.target.regexText.length){
8153                 tip = this.target.regexText;
8154             }
8155
8156             this.toolTip.show(tip);
8157
8158             this.intervalID = window.setInterval(function() {
8159                 Roo.bootstrap.Form.popover.unmask();
8160             }, 10000);
8161
8162             window.onwheel = function(){ return false;};
8163             
8164             (function(){ this.isMasked = true; }).defer(500, this);
8165                 
8166             
8167             
8168         },
8169         
8170         unmask : function()
8171         {
8172             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8173                 return;
8174             }
8175             
8176             this.maskEl.top.setStyle('position', 'absolute');
8177             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8178             this.maskEl.top.hide();
8179
8180             this.maskEl.left.setStyle('position', 'absolute');
8181             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8182             this.maskEl.left.hide();
8183
8184             this.maskEl.bottom.setStyle('position', 'absolute');
8185             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8186             this.maskEl.bottom.hide();
8187
8188             this.maskEl.right.setStyle('position', 'absolute');
8189             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8190             this.maskEl.right.hide();
8191             
8192             this.toolTip.hide();
8193             
8194             this.toolTip.el.hide();
8195             
8196             window.onwheel = function(){ return true;};
8197             
8198             if(this.intervalID){
8199                 window.clearInterval(this.intervalID);
8200                 this.intervalID = false;
8201             }
8202             
8203             this.isMasked = false;
8204             
8205         }
8206         
8207     }
8208     
8209 });
8210
8211 /*
8212  * Based on:
8213  * Ext JS Library 1.1.1
8214  * Copyright(c) 2006-2007, Ext JS, LLC.
8215  *
8216  * Originally Released Under LGPL - original licence link has changed is not relivant.
8217  *
8218  * Fork - LGPL
8219  * <script type="text/javascript">
8220  */
8221 /**
8222  * @class Roo.form.VTypes
8223  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8224  * @singleton
8225  */
8226 Roo.form.VTypes = function(){
8227     // closure these in so they are only created once.
8228     var alpha = /^[a-zA-Z_]+$/;
8229     var alphanum = /^[a-zA-Z0-9_]+$/;
8230     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8231     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8232
8233     // All these messages and functions are configurable
8234     return {
8235         /**
8236          * The function used to validate email addresses
8237          * @param {String} value The email address
8238          */
8239         'email' : function(v){
8240             return email.test(v);
8241         },
8242         /**
8243          * The error text to display when the email validation function returns false
8244          * @type String
8245          */
8246         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8247         /**
8248          * The keystroke filter mask to be applied on email input
8249          * @type RegExp
8250          */
8251         'emailMask' : /[a-z0-9_\.\-@]/i,
8252
8253         /**
8254          * The function used to validate URLs
8255          * @param {String} value The URL
8256          */
8257         'url' : function(v){
8258             return url.test(v);
8259         },
8260         /**
8261          * The error text to display when the url validation function returns false
8262          * @type String
8263          */
8264         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8265         
8266         /**
8267          * The function used to validate alpha values
8268          * @param {String} value The value
8269          */
8270         'alpha' : function(v){
8271             return alpha.test(v);
8272         },
8273         /**
8274          * The error text to display when the alpha validation function returns false
8275          * @type String
8276          */
8277         'alphaText' : 'This field should only contain letters and _',
8278         /**
8279          * The keystroke filter mask to be applied on alpha input
8280          * @type RegExp
8281          */
8282         'alphaMask' : /[a-z_]/i,
8283
8284         /**
8285          * The function used to validate alphanumeric values
8286          * @param {String} value The value
8287          */
8288         'alphanum' : function(v){
8289             return alphanum.test(v);
8290         },
8291         /**
8292          * The error text to display when the alphanumeric validation function returns false
8293          * @type String
8294          */
8295         'alphanumText' : 'This field should only contain letters, numbers and _',
8296         /**
8297          * The keystroke filter mask to be applied on alphanumeric input
8298          * @type RegExp
8299          */
8300         'alphanumMask' : /[a-z0-9_]/i
8301     };
8302 }();/*
8303  * - LGPL
8304  *
8305  * Input
8306  * 
8307  */
8308
8309 /**
8310  * @class Roo.bootstrap.Input
8311  * @extends Roo.bootstrap.Component
8312  * Bootstrap Input class
8313  * @cfg {Boolean} disabled is it disabled
8314  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8315  * @cfg {String} name name of the input
8316  * @cfg {string} fieldLabel - the label associated
8317  * @cfg {string} placeholder - placeholder to put in text.
8318  * @cfg {string}  before - input group add on before
8319  * @cfg {string} after - input group add on after
8320  * @cfg {string} size - (lg|sm) or leave empty..
8321  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8322  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8323  * @cfg {Number} md colspan out of 12 for computer-sized screens
8324  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8325  * @cfg {string} value default value of the input
8326  * @cfg {Number} labelWidth set the width of label 
8327  * @cfg {Number} labellg set the width of label (1-12)
8328  * @cfg {Number} labelmd set the width of label (1-12)
8329  * @cfg {Number} labelsm set the width of label (1-12)
8330  * @cfg {Number} labelxs set the width of label (1-12)
8331  * @cfg {String} labelAlign (top|left)
8332  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8333  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8334  * @cfg {String} indicatorpos (left|right) default left
8335
8336  * @cfg {String} align (left|center|right) Default left
8337  * @cfg {Boolean} forceFeedback (true|false) Default false
8338  * 
8339  * 
8340  * 
8341  * 
8342  * @constructor
8343  * Create a new Input
8344  * @param {Object} config The config object
8345  */
8346
8347 Roo.bootstrap.Input = function(config){
8348     
8349     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8350     
8351     this.addEvents({
8352         /**
8353          * @event focus
8354          * Fires when this field receives input focus.
8355          * @param {Roo.form.Field} this
8356          */
8357         focus : true,
8358         /**
8359          * @event blur
8360          * Fires when this field loses input focus.
8361          * @param {Roo.form.Field} this
8362          */
8363         blur : true,
8364         /**
8365          * @event specialkey
8366          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8367          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8368          * @param {Roo.form.Field} this
8369          * @param {Roo.EventObject} e The event object
8370          */
8371         specialkey : true,
8372         /**
8373          * @event change
8374          * Fires just before the field blurs if the field value has changed.
8375          * @param {Roo.form.Field} this
8376          * @param {Mixed} newValue The new value
8377          * @param {Mixed} oldValue The original value
8378          */
8379         change : true,
8380         /**
8381          * @event invalid
8382          * Fires after the field has been marked as invalid.
8383          * @param {Roo.form.Field} this
8384          * @param {String} msg The validation message
8385          */
8386         invalid : true,
8387         /**
8388          * @event valid
8389          * Fires after the field has been validated with no errors.
8390          * @param {Roo.form.Field} this
8391          */
8392         valid : true,
8393          /**
8394          * @event keyup
8395          * Fires after the key up
8396          * @param {Roo.form.Field} this
8397          * @param {Roo.EventObject}  e The event Object
8398          */
8399         keyup : true
8400     });
8401 };
8402
8403 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8404      /**
8405      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8406       automatic validation (defaults to "keyup").
8407      */
8408     validationEvent : "keyup",
8409      /**
8410      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8411      */
8412     validateOnBlur : true,
8413     /**
8414      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8415      */
8416     validationDelay : 250,
8417      /**
8418      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8419      */
8420     focusClass : "x-form-focus",  // not needed???
8421     
8422        
8423     /**
8424      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8425      */
8426     invalidClass : "has-warning",
8427     
8428     /**
8429      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8430      */
8431     validClass : "has-success",
8432     
8433     /**
8434      * @cfg {Boolean} hasFeedback (true|false) default true
8435      */
8436     hasFeedback : true,
8437     
8438     /**
8439      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8440      */
8441     invalidFeedbackClass : "glyphicon-warning-sign",
8442     
8443     /**
8444      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8445      */
8446     validFeedbackClass : "glyphicon-ok",
8447     
8448     /**
8449      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8450      */
8451     selectOnFocus : false,
8452     
8453      /**
8454      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8455      */
8456     maskRe : null,
8457        /**
8458      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8459      */
8460     vtype : null,
8461     
8462       /**
8463      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8464      */
8465     disableKeyFilter : false,
8466     
8467        /**
8468      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8469      */
8470     disabled : false,
8471      /**
8472      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8473      */
8474     allowBlank : true,
8475     /**
8476      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8477      */
8478     blankText : "Please complete this mandatory field",
8479     
8480      /**
8481      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8482      */
8483     minLength : 0,
8484     /**
8485      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8486      */
8487     maxLength : Number.MAX_VALUE,
8488     /**
8489      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8490      */
8491     minLengthText : "The minimum length for this field is {0}",
8492     /**
8493      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8494      */
8495     maxLengthText : "The maximum length for this field is {0}",
8496   
8497     
8498     /**
8499      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8500      * If available, this function will be called only after the basic validators all return true, and will be passed the
8501      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8502      */
8503     validator : null,
8504     /**
8505      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8506      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8507      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8508      */
8509     regex : null,
8510     /**
8511      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
8512      */
8513     regexText : "",
8514     
8515     autocomplete: false,
8516     
8517     
8518     fieldLabel : '',
8519     inputType : 'text',
8520     
8521     name : false,
8522     placeholder: false,
8523     before : false,
8524     after : false,
8525     size : false,
8526     hasFocus : false,
8527     preventMark: false,
8528     isFormField : true,
8529     value : '',
8530     labelWidth : 2,
8531     labelAlign : false,
8532     readOnly : false,
8533     align : false,
8534     formatedValue : false,
8535     forceFeedback : false,
8536     
8537     indicatorpos : 'left',
8538     
8539     labellg : 0,
8540     labelmd : 0,
8541     labelsm : 0,
8542     labelxs : 0,
8543     
8544     parentLabelAlign : function()
8545     {
8546         var parent = this;
8547         while (parent.parent()) {
8548             parent = parent.parent();
8549             if (typeof(parent.labelAlign) !='undefined') {
8550                 return parent.labelAlign;
8551             }
8552         }
8553         return 'left';
8554         
8555     },
8556     
8557     getAutoCreate : function()
8558     {
8559         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8560         
8561         var id = Roo.id();
8562         
8563         var cfg = {};
8564         
8565         if(this.inputType != 'hidden'){
8566             cfg.cls = 'form-group' //input-group
8567         }
8568         
8569         var input =  {
8570             tag: 'input',
8571             id : id,
8572             type : this.inputType,
8573             value : this.value,
8574             cls : 'form-control',
8575             placeholder : this.placeholder || '',
8576             autocomplete : this.autocomplete || 'new-password'
8577         };
8578         
8579         if(this.align){
8580             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8581         }
8582         
8583         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8584             input.maxLength = this.maxLength;
8585         }
8586         
8587         if (this.disabled) {
8588             input.disabled=true;
8589         }
8590         
8591         if (this.readOnly) {
8592             input.readonly=true;
8593         }
8594         
8595         if (this.name) {
8596             input.name = this.name;
8597         }
8598         
8599         if (this.size) {
8600             input.cls += ' input-' + this.size;
8601         }
8602         
8603         var settings=this;
8604         ['xs','sm','md','lg'].map(function(size){
8605             if (settings[size]) {
8606                 cfg.cls += ' col-' + size + '-' + settings[size];
8607             }
8608         });
8609         
8610         var inputblock = input;
8611         
8612         var feedback = {
8613             tag: 'span',
8614             cls: 'glyphicon form-control-feedback'
8615         };
8616             
8617         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8618             
8619             inputblock = {
8620                 cls : 'has-feedback',
8621                 cn :  [
8622                     input,
8623                     feedback
8624                 ] 
8625             };  
8626         }
8627         
8628         if (this.before || this.after) {
8629             
8630             inputblock = {
8631                 cls : 'input-group',
8632                 cn :  [] 
8633             };
8634             
8635             if (this.before && typeof(this.before) == 'string') {
8636                 
8637                 inputblock.cn.push({
8638                     tag :'span',
8639                     cls : 'roo-input-before input-group-addon',
8640                     html : this.before
8641                 });
8642             }
8643             if (this.before && typeof(this.before) == 'object') {
8644                 this.before = Roo.factory(this.before);
8645                 
8646                 inputblock.cn.push({
8647                     tag :'span',
8648                     cls : 'roo-input-before input-group-' +
8649                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8650                 });
8651             }
8652             
8653             inputblock.cn.push(input);
8654             
8655             if (this.after && typeof(this.after) == 'string') {
8656                 inputblock.cn.push({
8657                     tag :'span',
8658                     cls : 'roo-input-after input-group-addon',
8659                     html : this.after
8660                 });
8661             }
8662             if (this.after && typeof(this.after) == 'object') {
8663                 this.after = Roo.factory(this.after);
8664                 
8665                 inputblock.cn.push({
8666                     tag :'span',
8667                     cls : 'roo-input-after input-group-' +
8668                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8669                 });
8670             }
8671             
8672             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8673                 inputblock.cls += ' has-feedback';
8674                 inputblock.cn.push(feedback);
8675             }
8676         };
8677         
8678         if (align ==='left' && this.fieldLabel.length) {
8679             
8680             cfg.cls += ' roo-form-group-label-left';
8681             
8682             cfg.cn = [
8683                 {
8684                     tag : 'i',
8685                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8686                     tooltip : 'This field is required'
8687                 },
8688                 {
8689                     tag: 'label',
8690                     'for' :  id,
8691                     cls : 'control-label',
8692                     html : this.fieldLabel
8693
8694                 },
8695                 {
8696                     cls : "", 
8697                     cn: [
8698                         inputblock
8699                     ]
8700                 }
8701             ];
8702             
8703             var labelCfg = cfg.cn[1];
8704             var contentCfg = cfg.cn[2];
8705             
8706             if(this.indicatorpos == 'right'){
8707                 cfg.cn = [
8708                     {
8709                         tag: 'label',
8710                         'for' :  id,
8711                         cls : 'control-label',
8712                         html : this.fieldLabel
8713
8714                     },
8715                     {
8716                         tag : 'i',
8717                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8718                         tooltip : 'This field is required'
8719                     },
8720                     {
8721                         cls : "",
8722                         cn: [
8723                             inputblock
8724                         ]
8725                     }
8726
8727                 ];
8728                 
8729                 labelCfg = cfg.cn[0];
8730                 contentCfg = cfg.cn[2];
8731             
8732             }
8733             
8734             if(this.labelWidth > 12){
8735                 labelCfg.style = "width: " + this.labelWidth + 'px';
8736             }
8737             
8738             if(this.labelWidth < 13 && this.labelmd == 0){
8739                 this.labelmd = this.labelWidth;
8740             }
8741             
8742             if(this.labellg > 0){
8743                 labelCfg.cls += ' col-lg-' + this.labellg;
8744                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
8745             }
8746             
8747             if(this.labelmd > 0){
8748                 labelCfg.cls += ' col-md-' + this.labelmd;
8749                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
8750             }
8751             
8752             if(this.labelsm > 0){
8753                 labelCfg.cls += ' col-sm-' + this.labelsm;
8754                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
8755             }
8756             
8757             if(this.labelxs > 0){
8758                 labelCfg.cls += ' col-xs-' + this.labelxs;
8759                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
8760             }
8761             
8762             
8763         } else if ( this.fieldLabel.length) {
8764                 
8765             cfg.cn = [
8766                 {
8767                     tag : 'i',
8768                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8769                     tooltip : 'This field is required'
8770                 },
8771                 {
8772                     tag: 'label',
8773                    //cls : 'input-group-addon',
8774                     html : this.fieldLabel
8775
8776                 },
8777
8778                inputblock
8779
8780            ];
8781            
8782            if(this.indicatorpos == 'right'){
8783                 
8784                 cfg.cn = [
8785                     {
8786                         tag: 'label',
8787                        //cls : 'input-group-addon',
8788                         html : this.fieldLabel
8789
8790                     },
8791                     {
8792                         tag : 'i',
8793                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8794                         tooltip : 'This field is required'
8795                     },
8796
8797                    inputblock
8798
8799                ];
8800
8801             }
8802
8803         } else {
8804             
8805             cfg.cn = [
8806
8807                     inputblock
8808
8809             ];
8810                 
8811                 
8812         };
8813         
8814         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8815            cfg.cls += ' navbar-form';
8816         }
8817         
8818         if (this.parentType === 'NavGroup') {
8819            cfg.cls += ' navbar-form';
8820            cfg.tag = 'li';
8821         }
8822         
8823         return cfg;
8824         
8825     },
8826     /**
8827      * return the real input element.
8828      */
8829     inputEl: function ()
8830     {
8831         return this.el.select('input.form-control',true).first();
8832     },
8833     
8834     tooltipEl : function()
8835     {
8836         return this.inputEl();
8837     },
8838     
8839     indicatorEl : function()
8840     {
8841         var indicator = this.el.select('i.roo-required-indicator',true).first();
8842         
8843         if(!indicator){
8844             return false;
8845         }
8846         
8847         return indicator;
8848         
8849     },
8850     
8851     setDisabled : function(v)
8852     {
8853         var i  = this.inputEl().dom;
8854         if (!v) {
8855             i.removeAttribute('disabled');
8856             return;
8857             
8858         }
8859         i.setAttribute('disabled','true');
8860     },
8861     initEvents : function()
8862     {
8863           
8864         this.inputEl().on("keydown" , this.fireKey,  this);
8865         this.inputEl().on("focus", this.onFocus,  this);
8866         this.inputEl().on("blur", this.onBlur,  this);
8867         
8868         this.inputEl().relayEvent('keyup', this);
8869         
8870         this.indicator = this.indicatorEl();
8871         
8872         if(this.indicator){
8873             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
8874             this.indicator.hide();
8875         }
8876  
8877         // reference to original value for reset
8878         this.originalValue = this.getValue();
8879         //Roo.form.TextField.superclass.initEvents.call(this);
8880         if(this.validationEvent == 'keyup'){
8881             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8882             this.inputEl().on('keyup', this.filterValidation, this);
8883         }
8884         else if(this.validationEvent !== false){
8885             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8886         }
8887         
8888         if(this.selectOnFocus){
8889             this.on("focus", this.preFocus, this);
8890             
8891         }
8892         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8893             this.inputEl().on("keypress", this.filterKeys, this);
8894         } else {
8895             this.inputEl().relayEvent('keypress', this);
8896         }
8897        /* if(this.grow){
8898             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8899             this.el.on("click", this.autoSize,  this);
8900         }
8901         */
8902         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8903             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8904         }
8905         
8906         if (typeof(this.before) == 'object') {
8907             this.before.render(this.el.select('.roo-input-before',true).first());
8908         }
8909         if (typeof(this.after) == 'object') {
8910             this.after.render(this.el.select('.roo-input-after',true).first());
8911         }
8912         
8913         
8914     },
8915     filterValidation : function(e){
8916         if(!e.isNavKeyPress()){
8917             this.validationTask.delay(this.validationDelay);
8918         }
8919     },
8920      /**
8921      * Validates the field value
8922      * @return {Boolean} True if the value is valid, else false
8923      */
8924     validate : function(){
8925         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
8926         if(this.disabled || this.validateValue(this.getRawValue())){
8927             this.markValid();
8928             return true;
8929         }
8930         
8931         this.markInvalid();
8932         return false;
8933     },
8934     
8935     
8936     /**
8937      * Validates a value according to the field's validation rules and marks the field as invalid
8938      * if the validation fails
8939      * @param {Mixed} value The value to validate
8940      * @return {Boolean} True if the value is valid, else false
8941      */
8942     validateValue : function(value){
8943         if(value.length < 1)  { // if it's blank
8944             if(this.allowBlank){
8945                 return true;
8946             }            
8947             return this.inputEl().hasClass('hide') ? true : false;
8948         }
8949         
8950         if(value.length < this.minLength){
8951             return false;
8952         }
8953         if(value.length > this.maxLength){
8954             return false;
8955         }
8956         if(this.vtype){
8957             var vt = Roo.form.VTypes;
8958             if(!vt[this.vtype](value, this)){
8959                 return false;
8960             }
8961         }
8962         if(typeof this.validator == "function"){
8963             var msg = this.validator(value);
8964             if(msg !== true){
8965                 return false;
8966             }
8967         }
8968         
8969         if(this.regex && !this.regex.test(value)){
8970             return false;
8971         }
8972         
8973         return true;
8974     },
8975
8976     
8977     
8978      // private
8979     fireKey : function(e){
8980         //Roo.log('field ' + e.getKey());
8981         if(e.isNavKeyPress()){
8982             this.fireEvent("specialkey", this, e);
8983         }
8984     },
8985     focus : function (selectText){
8986         if(this.rendered){
8987             this.inputEl().focus();
8988             if(selectText === true){
8989                 this.inputEl().dom.select();
8990             }
8991         }
8992         return this;
8993     } ,
8994     
8995     onFocus : function(){
8996         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
8997            // this.el.addClass(this.focusClass);
8998         }
8999         if(!this.hasFocus){
9000             this.hasFocus = true;
9001             this.startValue = this.getValue();
9002             this.fireEvent("focus", this);
9003         }
9004     },
9005     
9006     beforeBlur : Roo.emptyFn,
9007
9008     
9009     // private
9010     onBlur : function(){
9011         this.beforeBlur();
9012         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9013             //this.el.removeClass(this.focusClass);
9014         }
9015         this.hasFocus = false;
9016         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9017             this.validate();
9018         }
9019         var v = this.getValue();
9020         if(String(v) !== String(this.startValue)){
9021             this.fireEvent('change', this, v, this.startValue);
9022         }
9023         this.fireEvent("blur", this);
9024     },
9025     
9026     /**
9027      * Resets the current field value to the originally loaded value and clears any validation messages
9028      */
9029     reset : function(){
9030         this.setValue(this.originalValue);
9031         this.validate();
9032     },
9033      /**
9034      * Returns the name of the field
9035      * @return {Mixed} name The name field
9036      */
9037     getName: function(){
9038         return this.name;
9039     },
9040      /**
9041      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9042      * @return {Mixed} value The field value
9043      */
9044     getValue : function(){
9045         
9046         var v = this.inputEl().getValue();
9047         
9048         return v;
9049     },
9050     /**
9051      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9052      * @return {Mixed} value The field value
9053      */
9054     getRawValue : function(){
9055         var v = this.inputEl().getValue();
9056         
9057         return v;
9058     },
9059     
9060     /**
9061      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9062      * @param {Mixed} value The value to set
9063      */
9064     setRawValue : function(v){
9065         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9066     },
9067     
9068     selectText : function(start, end){
9069         var v = this.getRawValue();
9070         if(v.length > 0){
9071             start = start === undefined ? 0 : start;
9072             end = end === undefined ? v.length : end;
9073             var d = this.inputEl().dom;
9074             if(d.setSelectionRange){
9075                 d.setSelectionRange(start, end);
9076             }else if(d.createTextRange){
9077                 var range = d.createTextRange();
9078                 range.moveStart("character", start);
9079                 range.moveEnd("character", v.length-end);
9080                 range.select();
9081             }
9082         }
9083     },
9084     
9085     /**
9086      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9087      * @param {Mixed} value The value to set
9088      */
9089     setValue : function(v){
9090         this.value = v;
9091         if(this.rendered){
9092             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9093             this.validate();
9094         }
9095     },
9096     
9097     /*
9098     processValue : function(value){
9099         if(this.stripCharsRe){
9100             var newValue = value.replace(this.stripCharsRe, '');
9101             if(newValue !== value){
9102                 this.setRawValue(newValue);
9103                 return newValue;
9104             }
9105         }
9106         return value;
9107     },
9108   */
9109     preFocus : function(){
9110         
9111         if(this.selectOnFocus){
9112             this.inputEl().dom.select();
9113         }
9114     },
9115     filterKeys : function(e){
9116         var k = e.getKey();
9117         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9118             return;
9119         }
9120         var c = e.getCharCode(), cc = String.fromCharCode(c);
9121         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9122             return;
9123         }
9124         if(!this.maskRe.test(cc)){
9125             e.stopEvent();
9126         }
9127     },
9128      /**
9129      * Clear any invalid styles/messages for this field
9130      */
9131     clearInvalid : function(){
9132         
9133         if(!this.el || this.preventMark){ // not rendered
9134             return;
9135         }
9136         
9137      
9138         this.el.removeClass(this.invalidClass);
9139         
9140         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9141             
9142             var feedback = this.el.select('.form-control-feedback', true).first();
9143             
9144             if(feedback){
9145                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9146             }
9147             
9148         }
9149         
9150         this.fireEvent('valid', this);
9151     },
9152     
9153      /**
9154      * Mark this field as valid
9155      */
9156     markValid : function()
9157     {
9158         if(!this.el  || this.preventMark){ // not rendered...
9159             return;
9160         }
9161         
9162         this.el.removeClass([this.invalidClass, this.validClass]);
9163         
9164         var feedback = this.el.select('.form-control-feedback', true).first();
9165             
9166         if(feedback){
9167             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9168         }
9169
9170         if(this.disabled){
9171             return;
9172         }
9173         
9174         if(this.allowBlank && !this.getRawValue().length){
9175             return;
9176         }
9177         
9178         if(this.indicator){
9179             this.indicator.hide();
9180         }
9181         
9182         this.el.addClass(this.validClass);
9183         
9184         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9185             
9186             var feedback = this.el.select('.form-control-feedback', true).first();
9187             
9188             if(feedback){
9189                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9190                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9191             }
9192             
9193         }
9194         
9195         this.fireEvent('valid', this);
9196     },
9197     
9198      /**
9199      * Mark this field as invalid
9200      * @param {String} msg The validation message
9201      */
9202     markInvalid : function(msg)
9203     {
9204         if(!this.el  || this.preventMark){ // not rendered
9205             return;
9206         }
9207         
9208         this.el.removeClass([this.invalidClass, this.validClass]);
9209         
9210         var feedback = this.el.select('.form-control-feedback', true).first();
9211             
9212         if(feedback){
9213             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9214         }
9215
9216         if(this.disabled){
9217             return;
9218         }
9219         
9220         if(this.allowBlank && !this.getRawValue().length){
9221             return;
9222         }
9223         
9224         if(this.indicator){
9225             this.indicator.show();
9226         }
9227         
9228         this.el.addClass(this.invalidClass);
9229         
9230         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9231             
9232             var feedback = this.el.select('.form-control-feedback', true).first();
9233             
9234             if(feedback){
9235                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9236                 
9237                 if(this.getValue().length || this.forceFeedback){
9238                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9239                 }
9240                 
9241             }
9242             
9243         }
9244         
9245         this.fireEvent('invalid', this, msg);
9246     },
9247     // private
9248     SafariOnKeyDown : function(event)
9249     {
9250         // this is a workaround for a password hang bug on chrome/ webkit.
9251         if (this.inputEl().dom.type != 'password') {
9252             return;
9253         }
9254         
9255         var isSelectAll = false;
9256         
9257         if(this.inputEl().dom.selectionEnd > 0){
9258             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9259         }
9260         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9261             event.preventDefault();
9262             this.setValue('');
9263             return;
9264         }
9265         
9266         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9267             
9268             event.preventDefault();
9269             // this is very hacky as keydown always get's upper case.
9270             //
9271             var cc = String.fromCharCode(event.getCharCode());
9272             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9273             
9274         }
9275     },
9276     adjustWidth : function(tag, w){
9277         tag = tag.toLowerCase();
9278         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9279             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9280                 if(tag == 'input'){
9281                     return w + 2;
9282                 }
9283                 if(tag == 'textarea'){
9284                     return w-2;
9285                 }
9286             }else if(Roo.isOpera){
9287                 if(tag == 'input'){
9288                     return w + 2;
9289                 }
9290                 if(tag == 'textarea'){
9291                     return w-2;
9292                 }
9293             }
9294         }
9295         return w;
9296     }
9297     
9298 });
9299
9300  
9301 /*
9302  * - LGPL
9303  *
9304  * Input
9305  * 
9306  */
9307
9308 /**
9309  * @class Roo.bootstrap.TextArea
9310  * @extends Roo.bootstrap.Input
9311  * Bootstrap TextArea class
9312  * @cfg {Number} cols Specifies the visible width of a text area
9313  * @cfg {Number} rows Specifies the visible number of lines in a text area
9314  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9315  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9316  * @cfg {string} html text
9317  * 
9318  * @constructor
9319  * Create a new TextArea
9320  * @param {Object} config The config object
9321  */
9322
9323 Roo.bootstrap.TextArea = function(config){
9324     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9325    
9326 };
9327
9328 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9329      
9330     cols : false,
9331     rows : 5,
9332     readOnly : false,
9333     warp : 'soft',
9334     resize : false,
9335     value: false,
9336     html: false,
9337     
9338     getAutoCreate : function(){
9339         
9340         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9341         
9342         var id = Roo.id();
9343         
9344         var cfg = {};
9345         
9346         var input =  {
9347             tag: 'textarea',
9348             id : id,
9349             warp : this.warp,
9350             rows : this.rows,
9351             value : this.value || '',
9352             html: this.html || '',
9353             cls : 'form-control',
9354             placeholder : this.placeholder || '' 
9355             
9356         };
9357         
9358         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9359             input.maxLength = this.maxLength;
9360         }
9361         
9362         if(this.resize){
9363             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9364         }
9365         
9366         if(this.cols){
9367             input.cols = this.cols;
9368         }
9369         
9370         if (this.readOnly) {
9371             input.readonly = true;
9372         }
9373         
9374         if (this.name) {
9375             input.name = this.name;
9376         }
9377         
9378         if (this.size) {
9379             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9380         }
9381         
9382         var settings=this;
9383         ['xs','sm','md','lg'].map(function(size){
9384             if (settings[size]) {
9385                 cfg.cls += ' col-' + size + '-' + settings[size];
9386             }
9387         });
9388         
9389         var inputblock = input;
9390         
9391         if(this.hasFeedback && !this.allowBlank){
9392             
9393             var feedback = {
9394                 tag: 'span',
9395                 cls: 'glyphicon form-control-feedback'
9396             };
9397
9398             inputblock = {
9399                 cls : 'has-feedback',
9400                 cn :  [
9401                     input,
9402                     feedback
9403                 ] 
9404             };  
9405         }
9406         
9407         
9408         if (this.before || this.after) {
9409             
9410             inputblock = {
9411                 cls : 'input-group',
9412                 cn :  [] 
9413             };
9414             if (this.before) {
9415                 inputblock.cn.push({
9416                     tag :'span',
9417                     cls : 'input-group-addon',
9418                     html : this.before
9419                 });
9420             }
9421             
9422             inputblock.cn.push(input);
9423             
9424             if(this.hasFeedback && !this.allowBlank){
9425                 inputblock.cls += ' has-feedback';
9426                 inputblock.cn.push(feedback);
9427             }
9428             
9429             if (this.after) {
9430                 inputblock.cn.push({
9431                     tag :'span',
9432                     cls : 'input-group-addon',
9433                     html : this.after
9434                 });
9435             }
9436             
9437         }
9438         
9439         if (align ==='left' && this.fieldLabel.length) {
9440             cfg.cn = [
9441                 {
9442                     tag: 'label',
9443                     'for' :  id,
9444                     cls : 'control-label',
9445                     html : this.fieldLabel
9446                 },
9447                 {
9448                     cls : "",
9449                     cn: [
9450                         inputblock
9451                     ]
9452                 }
9453
9454             ];
9455             
9456             if(this.labelWidth > 12){
9457                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9458             }
9459
9460             if(this.labelWidth < 13 && this.labelmd == 0){
9461                 this.labelmd = this.labelWidth;
9462             }
9463
9464             if(this.labellg > 0){
9465                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9466                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9467             }
9468
9469             if(this.labelmd > 0){
9470                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9471                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9472             }
9473
9474             if(this.labelsm > 0){
9475                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9476                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9477             }
9478
9479             if(this.labelxs > 0){
9480                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9481                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9482             }
9483             
9484         } else if ( this.fieldLabel.length) {
9485             cfg.cn = [
9486
9487                {
9488                    tag: 'label',
9489                    //cls : 'input-group-addon',
9490                    html : this.fieldLabel
9491
9492                },
9493
9494                inputblock
9495
9496            ];
9497
9498         } else {
9499
9500             cfg.cn = [
9501
9502                 inputblock
9503
9504             ];
9505                 
9506         }
9507         
9508         if (this.disabled) {
9509             input.disabled=true;
9510         }
9511         
9512         return cfg;
9513         
9514     },
9515     /**
9516      * return the real textarea element.
9517      */
9518     inputEl: function ()
9519     {
9520         return this.el.select('textarea.form-control',true).first();
9521     },
9522     
9523     /**
9524      * Clear any invalid styles/messages for this field
9525      */
9526     clearInvalid : function()
9527     {
9528         
9529         if(!this.el || this.preventMark){ // not rendered
9530             return;
9531         }
9532         
9533         var label = this.el.select('label', true).first();
9534         var icon = this.el.select('i.fa-star', true).first();
9535         
9536         if(label && icon){
9537             icon.remove();
9538         }
9539         
9540         this.el.removeClass(this.invalidClass);
9541         
9542         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9543             
9544             var feedback = this.el.select('.form-control-feedback', true).first();
9545             
9546             if(feedback){
9547                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9548             }
9549             
9550         }
9551         
9552         this.fireEvent('valid', this);
9553     },
9554     
9555      /**
9556      * Mark this field as valid
9557      */
9558     markValid : function()
9559     {
9560         if(!this.el  || this.preventMark){ // not rendered
9561             return;
9562         }
9563         
9564         this.el.removeClass([this.invalidClass, this.validClass]);
9565         
9566         var feedback = this.el.select('.form-control-feedback', true).first();
9567             
9568         if(feedback){
9569             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9570         }
9571
9572         if(this.disabled || this.allowBlank){
9573             return;
9574         }
9575         
9576         var label = this.el.select('label', true).first();
9577         var icon = this.el.select('i.fa-star', true).first();
9578         
9579         if(label && icon){
9580             icon.remove();
9581         }
9582         
9583         this.el.addClass(this.validClass);
9584         
9585         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9586             
9587             var feedback = this.el.select('.form-control-feedback', true).first();
9588             
9589             if(feedback){
9590                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9591                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9592             }
9593             
9594         }
9595         
9596         this.fireEvent('valid', this);
9597     },
9598     
9599      /**
9600      * Mark this field as invalid
9601      * @param {String} msg The validation message
9602      */
9603     markInvalid : function(msg)
9604     {
9605         if(!this.el  || this.preventMark){ // not rendered
9606             return;
9607         }
9608         
9609         this.el.removeClass([this.invalidClass, this.validClass]);
9610         
9611         var feedback = this.el.select('.form-control-feedback', true).first();
9612             
9613         if(feedback){
9614             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9615         }
9616
9617         if(this.disabled || this.allowBlank){
9618             return;
9619         }
9620         
9621         var label = this.el.select('label', true).first();
9622         var icon = this.el.select('i.fa-star', true).first();
9623         
9624         if(!this.getValue().length && label && !icon){
9625             this.el.createChild({
9626                 tag : 'i',
9627                 cls : 'text-danger fa fa-lg fa-star',
9628                 tooltip : 'This field is required',
9629                 style : 'margin-right:5px;'
9630             }, label, true);
9631         }
9632
9633         this.el.addClass(this.invalidClass);
9634         
9635         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9636             
9637             var feedback = this.el.select('.form-control-feedback', true).first();
9638             
9639             if(feedback){
9640                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9641                 
9642                 if(this.getValue().length || this.forceFeedback){
9643                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9644                 }
9645                 
9646             }
9647             
9648         }
9649         
9650         this.fireEvent('invalid', this, msg);
9651     }
9652 });
9653
9654  
9655 /*
9656  * - LGPL
9657  *
9658  * trigger field - base class for combo..
9659  * 
9660  */
9661  
9662 /**
9663  * @class Roo.bootstrap.TriggerField
9664  * @extends Roo.bootstrap.Input
9665  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9666  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9667  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9668  * for which you can provide a custom implementation.  For example:
9669  * <pre><code>
9670 var trigger = new Roo.bootstrap.TriggerField();
9671 trigger.onTriggerClick = myTriggerFn;
9672 trigger.applyTo('my-field');
9673 </code></pre>
9674  *
9675  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9676  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9677  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9678  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9679  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9680
9681  * @constructor
9682  * Create a new TriggerField.
9683  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9684  * to the base TextField)
9685  */
9686 Roo.bootstrap.TriggerField = function(config){
9687     this.mimicing = false;
9688     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9689 };
9690
9691 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9692     /**
9693      * @cfg {String} triggerClass A CSS class to apply to the trigger
9694      */
9695      /**
9696      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9697      */
9698     hideTrigger:false,
9699
9700     /**
9701      * @cfg {Boolean} removable (true|false) special filter default false
9702      */
9703     removable : false,
9704     
9705     /** @cfg {Boolean} grow @hide */
9706     /** @cfg {Number} growMin @hide */
9707     /** @cfg {Number} growMax @hide */
9708
9709     /**
9710      * @hide 
9711      * @method
9712      */
9713     autoSize: Roo.emptyFn,
9714     // private
9715     monitorTab : true,
9716     // private
9717     deferHeight : true,
9718
9719     
9720     actionMode : 'wrap',
9721     
9722     caret : false,
9723     
9724     
9725     getAutoCreate : function(){
9726        
9727         var align = this.labelAlign || this.parentLabelAlign();
9728         
9729         var id = Roo.id();
9730         
9731         var cfg = {
9732             cls: 'form-group' //input-group
9733         };
9734         
9735         
9736         var input =  {
9737             tag: 'input',
9738             id : id,
9739             type : this.inputType,
9740             cls : 'form-control',
9741             autocomplete: 'new-password',
9742             placeholder : this.placeholder || '' 
9743             
9744         };
9745         if (this.name) {
9746             input.name = this.name;
9747         }
9748         if (this.size) {
9749             input.cls += ' input-' + this.size;
9750         }
9751         
9752         if (this.disabled) {
9753             input.disabled=true;
9754         }
9755         
9756         var inputblock = input;
9757         
9758         if(this.hasFeedback && !this.allowBlank){
9759             
9760             var feedback = {
9761                 tag: 'span',
9762                 cls: 'glyphicon form-control-feedback'
9763             };
9764             
9765             if(this.removable && !this.editable && !this.tickable){
9766                 inputblock = {
9767                     cls : 'has-feedback',
9768                     cn :  [
9769                         inputblock,
9770                         {
9771                             tag: 'button',
9772                             html : 'x',
9773                             cls : 'roo-combo-removable-btn close'
9774                         },
9775                         feedback
9776                     ] 
9777                 };
9778             } else {
9779                 inputblock = {
9780                     cls : 'has-feedback',
9781                     cn :  [
9782                         inputblock,
9783                         feedback
9784                     ] 
9785                 };
9786             }
9787
9788         } else {
9789             if(this.removable && !this.editable && !this.tickable){
9790                 inputblock = {
9791                     cls : 'roo-removable',
9792                     cn :  [
9793                         inputblock,
9794                         {
9795                             tag: 'button',
9796                             html : 'x',
9797                             cls : 'roo-combo-removable-btn close'
9798                         }
9799                     ] 
9800                 };
9801             }
9802         }
9803         
9804         if (this.before || this.after) {
9805             
9806             inputblock = {
9807                 cls : 'input-group',
9808                 cn :  [] 
9809             };
9810             if (this.before) {
9811                 inputblock.cn.push({
9812                     tag :'span',
9813                     cls : 'input-group-addon',
9814                     html : this.before
9815                 });
9816             }
9817             
9818             inputblock.cn.push(input);
9819             
9820             if(this.hasFeedback && !this.allowBlank){
9821                 inputblock.cls += ' has-feedback';
9822                 inputblock.cn.push(feedback);
9823             }
9824             
9825             if (this.after) {
9826                 inputblock.cn.push({
9827                     tag :'span',
9828                     cls : 'input-group-addon',
9829                     html : this.after
9830                 });
9831             }
9832             
9833         };
9834         
9835         var box = {
9836             tag: 'div',
9837             cn: [
9838                 {
9839                     tag: 'input',
9840                     type : 'hidden',
9841                     cls: 'form-hidden-field'
9842                 },
9843                 inputblock
9844             ]
9845             
9846         };
9847         
9848         if(this.multiple){
9849             box = {
9850                 tag: 'div',
9851                 cn: [
9852                     {
9853                         tag: 'input',
9854                         type : 'hidden',
9855                         cls: 'form-hidden-field'
9856                     },
9857                     {
9858                         tag: 'ul',
9859                         cls: 'roo-select2-choices',
9860                         cn:[
9861                             {
9862                                 tag: 'li',
9863                                 cls: 'roo-select2-search-field',
9864                                 cn: [
9865
9866                                     inputblock
9867                                 ]
9868                             }
9869                         ]
9870                     }
9871                 ]
9872             }
9873         };
9874         
9875         var combobox = {
9876             cls: 'roo-select2-container input-group',
9877             cn: [
9878                 box
9879 //                {
9880 //                    tag: 'ul',
9881 //                    cls: 'typeahead typeahead-long dropdown-menu',
9882 //                    style: 'display:none'
9883 //                }
9884             ]
9885         };
9886         
9887         if(!this.multiple && this.showToggleBtn){
9888             
9889             var caret = {
9890                         tag: 'span',
9891                         cls: 'caret'
9892              };
9893             if (this.caret != false) {
9894                 caret = {
9895                      tag: 'i',
9896                      cls: 'fa fa-' + this.caret
9897                 };
9898                 
9899             }
9900             
9901             combobox.cn.push({
9902                 tag :'span',
9903                 cls : 'input-group-addon btn dropdown-toggle',
9904                 cn : [
9905                     caret,
9906                     {
9907                         tag: 'span',
9908                         cls: 'combobox-clear',
9909                         cn  : [
9910                             {
9911                                 tag : 'i',
9912                                 cls: 'icon-remove'
9913                             }
9914                         ]
9915                     }
9916                 ]
9917
9918             })
9919         }
9920         
9921         if(this.multiple){
9922             combobox.cls += ' roo-select2-container-multi';
9923         }
9924         
9925         if (align ==='left' && this.fieldLabel.length) {
9926             
9927             cfg.cls += ' roo-form-group-label-left';
9928
9929             cfg.cn = [
9930                 {
9931                     tag : 'i',
9932                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
9933                     tooltip : 'This field is required'
9934                 },
9935                 {
9936                     tag: 'label',
9937                     'for' :  id,
9938                     cls : 'control-label',
9939                     html : this.fieldLabel
9940
9941                 },
9942                 {
9943                     cls : "", 
9944                     cn: [
9945                         combobox
9946                     ]
9947                 }
9948
9949             ];
9950             
9951             var labelCfg = cfg.cn[1];
9952             var contentCfg = cfg.cn[2];
9953             
9954             if(this.indicatorpos == 'right'){
9955                 cfg.cn = [
9956                     {
9957                         tag: 'label',
9958                         'for' :  id,
9959                         cls : 'control-label',
9960                         cn : [
9961                             {
9962                                 tag : 'span',
9963                                 html : this.fieldLabel
9964                             },
9965                             {
9966                                 tag : 'i',
9967                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
9968                                 tooltip : 'This field is required'
9969                             }
9970                         ]
9971                     },
9972                     {
9973                         cls : "", 
9974                         cn: [
9975                             combobox
9976                         ]
9977                     }
9978
9979                 ];
9980                 
9981                 labelCfg = cfg.cn[0];
9982                 contentCfg = cfg.cn[1];
9983             }
9984             
9985             if(this.labelWidth > 12){
9986                 labelCfg.style = "width: " + this.labelWidth + 'px';
9987             }
9988             
9989             if(this.labelWidth < 13 && this.labelmd == 0){
9990                 this.labelmd = this.labelWidth;
9991             }
9992             
9993             if(this.labellg > 0){
9994                 labelCfg.cls += ' col-lg-' + this.labellg;
9995                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
9996             }
9997             
9998             if(this.labelmd > 0){
9999                 labelCfg.cls += ' col-md-' + this.labelmd;
10000                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10001             }
10002             
10003             if(this.labelsm > 0){
10004                 labelCfg.cls += ' col-sm-' + this.labelsm;
10005                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10006             }
10007             
10008             if(this.labelxs > 0){
10009                 labelCfg.cls += ' col-xs-' + this.labelxs;
10010                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10011             }
10012             
10013         } else if ( this.fieldLabel.length) {
10014 //                Roo.log(" label");
10015             cfg.cn = [
10016                 {
10017                    tag : 'i',
10018                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10019                    tooltip : 'This field is required'
10020                },
10021                {
10022                    tag: 'label',
10023                    //cls : 'input-group-addon',
10024                    html : this.fieldLabel
10025
10026                },
10027
10028                combobox
10029
10030             ];
10031             
10032             if(this.indicatorpos == 'right'){
10033                 
10034                 cfg.cn = [
10035                     {
10036                        tag: 'label',
10037                        cn : [
10038                            {
10039                                tag : 'span',
10040                                html : this.fieldLabel
10041                            },
10042                            {
10043                               tag : 'i',
10044                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10045                               tooltip : 'This field is required'
10046                            }
10047                        ]
10048
10049                     },
10050                     combobox
10051
10052                 ];
10053
10054             }
10055
10056         } else {
10057             
10058 //                Roo.log(" no label && no align");
10059                 cfg = combobox
10060                      
10061                 
10062         }
10063         
10064         var settings=this;
10065         ['xs','sm','md','lg'].map(function(size){
10066             if (settings[size]) {
10067                 cfg.cls += ' col-' + size + '-' + settings[size];
10068             }
10069         });
10070         
10071         return cfg;
10072         
10073     },
10074     
10075     
10076     
10077     // private
10078     onResize : function(w, h){
10079 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10080 //        if(typeof w == 'number'){
10081 //            var x = w - this.trigger.getWidth();
10082 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10083 //            this.trigger.setStyle('left', x+'px');
10084 //        }
10085     },
10086
10087     // private
10088     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10089
10090     // private
10091     getResizeEl : function(){
10092         return this.inputEl();
10093     },
10094
10095     // private
10096     getPositionEl : function(){
10097         return this.inputEl();
10098     },
10099
10100     // private
10101     alignErrorIcon : function(){
10102         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10103     },
10104
10105     // private
10106     initEvents : function(){
10107         
10108         this.createList();
10109         
10110         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10111         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10112         if(!this.multiple && this.showToggleBtn){
10113             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10114             if(this.hideTrigger){
10115                 this.trigger.setDisplayed(false);
10116             }
10117             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10118         }
10119         
10120         if(this.multiple){
10121             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10122         }
10123         
10124         if(this.removable && !this.editable && !this.tickable){
10125             var close = this.closeTriggerEl();
10126             
10127             if(close){
10128                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10129                 close.on('click', this.removeBtnClick, this, close);
10130             }
10131         }
10132         
10133         //this.trigger.addClassOnOver('x-form-trigger-over');
10134         //this.trigger.addClassOnClick('x-form-trigger-click');
10135         
10136         //if(!this.width){
10137         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10138         //}
10139     },
10140     
10141     closeTriggerEl : function()
10142     {
10143         var close = this.el.select('.roo-combo-removable-btn', true).first();
10144         return close ? close : false;
10145     },
10146     
10147     removeBtnClick : function(e, h, el)
10148     {
10149         e.preventDefault();
10150         
10151         if(this.fireEvent("remove", this) !== false){
10152             this.reset();
10153             this.fireEvent("afterremove", this)
10154         }
10155     },
10156     
10157     createList : function()
10158     {
10159         this.list = Roo.get(document.body).createChild({
10160             tag: 'ul',
10161             cls: 'typeahead typeahead-long dropdown-menu',
10162             style: 'display:none'
10163         });
10164         
10165         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10166         
10167     },
10168
10169     // private
10170     initTrigger : function(){
10171        
10172     },
10173
10174     // private
10175     onDestroy : function(){
10176         if(this.trigger){
10177             this.trigger.removeAllListeners();
10178           //  this.trigger.remove();
10179         }
10180         //if(this.wrap){
10181         //    this.wrap.remove();
10182         //}
10183         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10184     },
10185
10186     // private
10187     onFocus : function(){
10188         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10189         /*
10190         if(!this.mimicing){
10191             this.wrap.addClass('x-trigger-wrap-focus');
10192             this.mimicing = true;
10193             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10194             if(this.monitorTab){
10195                 this.el.on("keydown", this.checkTab, this);
10196             }
10197         }
10198         */
10199     },
10200
10201     // private
10202     checkTab : function(e){
10203         if(e.getKey() == e.TAB){
10204             this.triggerBlur();
10205         }
10206     },
10207
10208     // private
10209     onBlur : function(){
10210         // do nothing
10211     },
10212
10213     // private
10214     mimicBlur : function(e, t){
10215         /*
10216         if(!this.wrap.contains(t) && this.validateBlur()){
10217             this.triggerBlur();
10218         }
10219         */
10220     },
10221
10222     // private
10223     triggerBlur : function(){
10224         this.mimicing = false;
10225         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10226         if(this.monitorTab){
10227             this.el.un("keydown", this.checkTab, this);
10228         }
10229         //this.wrap.removeClass('x-trigger-wrap-focus');
10230         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10231     },
10232
10233     // private
10234     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10235     validateBlur : function(e, t){
10236         return true;
10237     },
10238
10239     // private
10240     onDisable : function(){
10241         this.inputEl().dom.disabled = true;
10242         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10243         //if(this.wrap){
10244         //    this.wrap.addClass('x-item-disabled');
10245         //}
10246     },
10247
10248     // private
10249     onEnable : function(){
10250         this.inputEl().dom.disabled = false;
10251         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10252         //if(this.wrap){
10253         //    this.el.removeClass('x-item-disabled');
10254         //}
10255     },
10256
10257     // private
10258     onShow : function(){
10259         var ae = this.getActionEl();
10260         
10261         if(ae){
10262             ae.dom.style.display = '';
10263             ae.dom.style.visibility = 'visible';
10264         }
10265     },
10266
10267     // private
10268     
10269     onHide : function(){
10270         var ae = this.getActionEl();
10271         ae.dom.style.display = 'none';
10272     },
10273
10274     /**
10275      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10276      * by an implementing function.
10277      * @method
10278      * @param {EventObject} e
10279      */
10280     onTriggerClick : Roo.emptyFn
10281 });
10282  /*
10283  * Based on:
10284  * Ext JS Library 1.1.1
10285  * Copyright(c) 2006-2007, Ext JS, LLC.
10286  *
10287  * Originally Released Under LGPL - original licence link has changed is not relivant.
10288  *
10289  * Fork - LGPL
10290  * <script type="text/javascript">
10291  */
10292
10293
10294 /**
10295  * @class Roo.data.SortTypes
10296  * @singleton
10297  * Defines the default sorting (casting?) comparison functions used when sorting data.
10298  */
10299 Roo.data.SortTypes = {
10300     /**
10301      * Default sort that does nothing
10302      * @param {Mixed} s The value being converted
10303      * @return {Mixed} The comparison value
10304      */
10305     none : function(s){
10306         return s;
10307     },
10308     
10309     /**
10310      * The regular expression used to strip tags
10311      * @type {RegExp}
10312      * @property
10313      */
10314     stripTagsRE : /<\/?[^>]+>/gi,
10315     
10316     /**
10317      * Strips all HTML tags to sort on text only
10318      * @param {Mixed} s The value being converted
10319      * @return {String} The comparison value
10320      */
10321     asText : function(s){
10322         return String(s).replace(this.stripTagsRE, "");
10323     },
10324     
10325     /**
10326      * Strips all HTML tags to sort on text only - Case insensitive
10327      * @param {Mixed} s The value being converted
10328      * @return {String} The comparison value
10329      */
10330     asUCText : function(s){
10331         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10332     },
10333     
10334     /**
10335      * Case insensitive string
10336      * @param {Mixed} s The value being converted
10337      * @return {String} The comparison value
10338      */
10339     asUCString : function(s) {
10340         return String(s).toUpperCase();
10341     },
10342     
10343     /**
10344      * Date sorting
10345      * @param {Mixed} s The value being converted
10346      * @return {Number} The comparison value
10347      */
10348     asDate : function(s) {
10349         if(!s){
10350             return 0;
10351         }
10352         if(s instanceof Date){
10353             return s.getTime();
10354         }
10355         return Date.parse(String(s));
10356     },
10357     
10358     /**
10359      * Float sorting
10360      * @param {Mixed} s The value being converted
10361      * @return {Float} The comparison value
10362      */
10363     asFloat : function(s) {
10364         var val = parseFloat(String(s).replace(/,/g, ""));
10365         if(isNaN(val)) {
10366             val = 0;
10367         }
10368         return val;
10369     },
10370     
10371     /**
10372      * Integer sorting
10373      * @param {Mixed} s The value being converted
10374      * @return {Number} The comparison value
10375      */
10376     asInt : function(s) {
10377         var val = parseInt(String(s).replace(/,/g, ""));
10378         if(isNaN(val)) {
10379             val = 0;
10380         }
10381         return val;
10382     }
10383 };/*
10384  * Based on:
10385  * Ext JS Library 1.1.1
10386  * Copyright(c) 2006-2007, Ext JS, LLC.
10387  *
10388  * Originally Released Under LGPL - original licence link has changed is not relivant.
10389  *
10390  * Fork - LGPL
10391  * <script type="text/javascript">
10392  */
10393
10394 /**
10395 * @class Roo.data.Record
10396  * Instances of this class encapsulate both record <em>definition</em> information, and record
10397  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10398  * to access Records cached in an {@link Roo.data.Store} object.<br>
10399  * <p>
10400  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10401  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10402  * objects.<br>
10403  * <p>
10404  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10405  * @constructor
10406  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10407  * {@link #create}. The parameters are the same.
10408  * @param {Array} data An associative Array of data values keyed by the field name.
10409  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10410  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10411  * not specified an integer id is generated.
10412  */
10413 Roo.data.Record = function(data, id){
10414     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10415     this.data = data;
10416 };
10417
10418 /**
10419  * Generate a constructor for a specific record layout.
10420  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10421  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10422  * Each field definition object may contain the following properties: <ul>
10423  * <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,
10424  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10425  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10426  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10427  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10428  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10429  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10430  * this may be omitted.</p></li>
10431  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10432  * <ul><li>auto (Default, implies no conversion)</li>
10433  * <li>string</li>
10434  * <li>int</li>
10435  * <li>float</li>
10436  * <li>boolean</li>
10437  * <li>date</li></ul></p></li>
10438  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10439  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10440  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10441  * by the Reader into an object that will be stored in the Record. It is passed the
10442  * following parameters:<ul>
10443  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10444  * </ul></p></li>
10445  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10446  * </ul>
10447  * <br>usage:<br><pre><code>
10448 var TopicRecord = Roo.data.Record.create(
10449     {name: 'title', mapping: 'topic_title'},
10450     {name: 'author', mapping: 'username'},
10451     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10452     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10453     {name: 'lastPoster', mapping: 'user2'},
10454     {name: 'excerpt', mapping: 'post_text'}
10455 );
10456
10457 var myNewRecord = new TopicRecord({
10458     title: 'Do my job please',
10459     author: 'noobie',
10460     totalPosts: 1,
10461     lastPost: new Date(),
10462     lastPoster: 'Animal',
10463     excerpt: 'No way dude!'
10464 });
10465 myStore.add(myNewRecord);
10466 </code></pre>
10467  * @method create
10468  * @static
10469  */
10470 Roo.data.Record.create = function(o){
10471     var f = function(){
10472         f.superclass.constructor.apply(this, arguments);
10473     };
10474     Roo.extend(f, Roo.data.Record);
10475     var p = f.prototype;
10476     p.fields = new Roo.util.MixedCollection(false, function(field){
10477         return field.name;
10478     });
10479     for(var i = 0, len = o.length; i < len; i++){
10480         p.fields.add(new Roo.data.Field(o[i]));
10481     }
10482     f.getField = function(name){
10483         return p.fields.get(name);  
10484     };
10485     return f;
10486 };
10487
10488 Roo.data.Record.AUTO_ID = 1000;
10489 Roo.data.Record.EDIT = 'edit';
10490 Roo.data.Record.REJECT = 'reject';
10491 Roo.data.Record.COMMIT = 'commit';
10492
10493 Roo.data.Record.prototype = {
10494     /**
10495      * Readonly flag - true if this record has been modified.
10496      * @type Boolean
10497      */
10498     dirty : false,
10499     editing : false,
10500     error: null,
10501     modified: null,
10502
10503     // private
10504     join : function(store){
10505         this.store = store;
10506     },
10507
10508     /**
10509      * Set the named field to the specified value.
10510      * @param {String} name The name of the field to set.
10511      * @param {Object} value The value to set the field to.
10512      */
10513     set : function(name, value){
10514         if(this.data[name] == value){
10515             return;
10516         }
10517         this.dirty = true;
10518         if(!this.modified){
10519             this.modified = {};
10520         }
10521         if(typeof this.modified[name] == 'undefined'){
10522             this.modified[name] = this.data[name];
10523         }
10524         this.data[name] = value;
10525         if(!this.editing && this.store){
10526             this.store.afterEdit(this);
10527         }       
10528     },
10529
10530     /**
10531      * Get the value of the named field.
10532      * @param {String} name The name of the field to get the value of.
10533      * @return {Object} The value of the field.
10534      */
10535     get : function(name){
10536         return this.data[name]; 
10537     },
10538
10539     // private
10540     beginEdit : function(){
10541         this.editing = true;
10542         this.modified = {}; 
10543     },
10544
10545     // private
10546     cancelEdit : function(){
10547         this.editing = false;
10548         delete this.modified;
10549     },
10550
10551     // private
10552     endEdit : function(){
10553         this.editing = false;
10554         if(this.dirty && this.store){
10555             this.store.afterEdit(this);
10556         }
10557     },
10558
10559     /**
10560      * Usually called by the {@link Roo.data.Store} which owns the Record.
10561      * Rejects all changes made to the Record since either creation, or the last commit operation.
10562      * Modified fields are reverted to their original values.
10563      * <p>
10564      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10565      * of reject operations.
10566      */
10567     reject : function(){
10568         var m = this.modified;
10569         for(var n in m){
10570             if(typeof m[n] != "function"){
10571                 this.data[n] = m[n];
10572             }
10573         }
10574         this.dirty = false;
10575         delete this.modified;
10576         this.editing = false;
10577         if(this.store){
10578             this.store.afterReject(this);
10579         }
10580     },
10581
10582     /**
10583      * Usually called by the {@link Roo.data.Store} which owns the Record.
10584      * Commits all changes made to the Record since either creation, or the last commit operation.
10585      * <p>
10586      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10587      * of commit operations.
10588      */
10589     commit : function(){
10590         this.dirty = false;
10591         delete this.modified;
10592         this.editing = false;
10593         if(this.store){
10594             this.store.afterCommit(this);
10595         }
10596     },
10597
10598     // private
10599     hasError : function(){
10600         return this.error != null;
10601     },
10602
10603     // private
10604     clearError : function(){
10605         this.error = null;
10606     },
10607
10608     /**
10609      * Creates a copy of this record.
10610      * @param {String} id (optional) A new record id if you don't want to use this record's id
10611      * @return {Record}
10612      */
10613     copy : function(newId) {
10614         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10615     }
10616 };/*
10617  * Based on:
10618  * Ext JS Library 1.1.1
10619  * Copyright(c) 2006-2007, Ext JS, LLC.
10620  *
10621  * Originally Released Under LGPL - original licence link has changed is not relivant.
10622  *
10623  * Fork - LGPL
10624  * <script type="text/javascript">
10625  */
10626
10627
10628
10629 /**
10630  * @class Roo.data.Store
10631  * @extends Roo.util.Observable
10632  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10633  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10634  * <p>
10635  * 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
10636  * has no knowledge of the format of the data returned by the Proxy.<br>
10637  * <p>
10638  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10639  * instances from the data object. These records are cached and made available through accessor functions.
10640  * @constructor
10641  * Creates a new Store.
10642  * @param {Object} config A config object containing the objects needed for the Store to access data,
10643  * and read the data into Records.
10644  */
10645 Roo.data.Store = function(config){
10646     this.data = new Roo.util.MixedCollection(false);
10647     this.data.getKey = function(o){
10648         return o.id;
10649     };
10650     this.baseParams = {};
10651     // private
10652     this.paramNames = {
10653         "start" : "start",
10654         "limit" : "limit",
10655         "sort" : "sort",
10656         "dir" : "dir",
10657         "multisort" : "_multisort"
10658     };
10659
10660     if(config && config.data){
10661         this.inlineData = config.data;
10662         delete config.data;
10663     }
10664
10665     Roo.apply(this, config);
10666     
10667     if(this.reader){ // reader passed
10668         this.reader = Roo.factory(this.reader, Roo.data);
10669         this.reader.xmodule = this.xmodule || false;
10670         if(!this.recordType){
10671             this.recordType = this.reader.recordType;
10672         }
10673         if(this.reader.onMetaChange){
10674             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10675         }
10676     }
10677
10678     if(this.recordType){
10679         this.fields = this.recordType.prototype.fields;
10680     }
10681     this.modified = [];
10682
10683     this.addEvents({
10684         /**
10685          * @event datachanged
10686          * Fires when the data cache has changed, and a widget which is using this Store
10687          * as a Record cache should refresh its view.
10688          * @param {Store} this
10689          */
10690         datachanged : true,
10691         /**
10692          * @event metachange
10693          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10694          * @param {Store} this
10695          * @param {Object} meta The JSON metadata
10696          */
10697         metachange : true,
10698         /**
10699          * @event add
10700          * Fires when Records have been added to the Store
10701          * @param {Store} this
10702          * @param {Roo.data.Record[]} records The array of Records added
10703          * @param {Number} index The index at which the record(s) were added
10704          */
10705         add : true,
10706         /**
10707          * @event remove
10708          * Fires when a Record has been removed from the Store
10709          * @param {Store} this
10710          * @param {Roo.data.Record} record The Record that was removed
10711          * @param {Number} index The index at which the record was removed
10712          */
10713         remove : true,
10714         /**
10715          * @event update
10716          * Fires when a Record has been updated
10717          * @param {Store} this
10718          * @param {Roo.data.Record} record The Record that was updated
10719          * @param {String} operation The update operation being performed.  Value may be one of:
10720          * <pre><code>
10721  Roo.data.Record.EDIT
10722  Roo.data.Record.REJECT
10723  Roo.data.Record.COMMIT
10724          * </code></pre>
10725          */
10726         update : true,
10727         /**
10728          * @event clear
10729          * Fires when the data cache has been cleared.
10730          * @param {Store} this
10731          */
10732         clear : true,
10733         /**
10734          * @event beforeload
10735          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10736          * the load action will be canceled.
10737          * @param {Store} this
10738          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10739          */
10740         beforeload : true,
10741         /**
10742          * @event beforeloadadd
10743          * Fires after a new set of Records has been loaded.
10744          * @param {Store} this
10745          * @param {Roo.data.Record[]} records The Records that were loaded
10746          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10747          */
10748         beforeloadadd : true,
10749         /**
10750          * @event load
10751          * Fires after a new set of Records has been loaded, before they are added to the store.
10752          * @param {Store} this
10753          * @param {Roo.data.Record[]} records The Records that were loaded
10754          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10755          * @params {Object} return from reader
10756          */
10757         load : true,
10758         /**
10759          * @event loadexception
10760          * Fires if an exception occurs in the Proxy during loading.
10761          * Called with the signature of the Proxy's "loadexception" event.
10762          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10763          * 
10764          * @param {Proxy} 
10765          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10766          * @param {Object} load options 
10767          * @param {Object} jsonData from your request (normally this contains the Exception)
10768          */
10769         loadexception : true
10770     });
10771     
10772     if(this.proxy){
10773         this.proxy = Roo.factory(this.proxy, Roo.data);
10774         this.proxy.xmodule = this.xmodule || false;
10775         this.relayEvents(this.proxy,  ["loadexception"]);
10776     }
10777     this.sortToggle = {};
10778     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10779
10780     Roo.data.Store.superclass.constructor.call(this);
10781
10782     if(this.inlineData){
10783         this.loadData(this.inlineData);
10784         delete this.inlineData;
10785     }
10786 };
10787
10788 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10789      /**
10790     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10791     * without a remote query - used by combo/forms at present.
10792     */
10793     
10794     /**
10795     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10796     */
10797     /**
10798     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10799     */
10800     /**
10801     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10802     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10803     */
10804     /**
10805     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10806     * on any HTTP request
10807     */
10808     /**
10809     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10810     */
10811     /**
10812     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10813     */
10814     multiSort: false,
10815     /**
10816     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10817     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10818     */
10819     remoteSort : false,
10820
10821     /**
10822     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10823      * loaded or when a record is removed. (defaults to false).
10824     */
10825     pruneModifiedRecords : false,
10826
10827     // private
10828     lastOptions : null,
10829
10830     /**
10831      * Add Records to the Store and fires the add event.
10832      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10833      */
10834     add : function(records){
10835         records = [].concat(records);
10836         for(var i = 0, len = records.length; i < len; i++){
10837             records[i].join(this);
10838         }
10839         var index = this.data.length;
10840         this.data.addAll(records);
10841         this.fireEvent("add", this, records, index);
10842     },
10843
10844     /**
10845      * Remove a Record from the Store and fires the remove event.
10846      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10847      */
10848     remove : function(record){
10849         var index = this.data.indexOf(record);
10850         this.data.removeAt(index);
10851         if(this.pruneModifiedRecords){
10852             this.modified.remove(record);
10853         }
10854         this.fireEvent("remove", this, record, index);
10855     },
10856
10857     /**
10858      * Remove all Records from the Store and fires the clear event.
10859      */
10860     removeAll : function(){
10861         this.data.clear();
10862         if(this.pruneModifiedRecords){
10863             this.modified = [];
10864         }
10865         this.fireEvent("clear", this);
10866     },
10867
10868     /**
10869      * Inserts Records to the Store at the given index and fires the add event.
10870      * @param {Number} index The start index at which to insert the passed Records.
10871      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10872      */
10873     insert : function(index, records){
10874         records = [].concat(records);
10875         for(var i = 0, len = records.length; i < len; i++){
10876             this.data.insert(index, records[i]);
10877             records[i].join(this);
10878         }
10879         this.fireEvent("add", this, records, index);
10880     },
10881
10882     /**
10883      * Get the index within the cache of the passed Record.
10884      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
10885      * @return {Number} The index of the passed Record. Returns -1 if not found.
10886      */
10887     indexOf : function(record){
10888         return this.data.indexOf(record);
10889     },
10890
10891     /**
10892      * Get the index within the cache of the Record with the passed id.
10893      * @param {String} id The id of the Record to find.
10894      * @return {Number} The index of the Record. Returns -1 if not found.
10895      */
10896     indexOfId : function(id){
10897         return this.data.indexOfKey(id);
10898     },
10899
10900     /**
10901      * Get the Record with the specified id.
10902      * @param {String} id The id of the Record to find.
10903      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
10904      */
10905     getById : function(id){
10906         return this.data.key(id);
10907     },
10908
10909     /**
10910      * Get the Record at the specified index.
10911      * @param {Number} index The index of the Record to find.
10912      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
10913      */
10914     getAt : function(index){
10915         return this.data.itemAt(index);
10916     },
10917
10918     /**
10919      * Returns a range of Records between specified indices.
10920      * @param {Number} startIndex (optional) The starting index (defaults to 0)
10921      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
10922      * @return {Roo.data.Record[]} An array of Records
10923      */
10924     getRange : function(start, end){
10925         return this.data.getRange(start, end);
10926     },
10927
10928     // private
10929     storeOptions : function(o){
10930         o = Roo.apply({}, o);
10931         delete o.callback;
10932         delete o.scope;
10933         this.lastOptions = o;
10934     },
10935
10936     /**
10937      * Loads the Record cache from the configured Proxy using the configured Reader.
10938      * <p>
10939      * If using remote paging, then the first load call must specify the <em>start</em>
10940      * and <em>limit</em> properties in the options.params property to establish the initial
10941      * position within the dataset, and the number of Records to cache on each read from the Proxy.
10942      * <p>
10943      * <strong>It is important to note that for remote data sources, loading is asynchronous,
10944      * and this call will return before the new data has been loaded. Perform any post-processing
10945      * in a callback function, or in a "load" event handler.</strong>
10946      * <p>
10947      * @param {Object} options An object containing properties which control loading options:<ul>
10948      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
10949      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
10950      * passed the following arguments:<ul>
10951      * <li>r : Roo.data.Record[]</li>
10952      * <li>options: Options object from the load call</li>
10953      * <li>success: Boolean success indicator</li></ul></li>
10954      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
10955      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
10956      * </ul>
10957      */
10958     load : function(options){
10959         options = options || {};
10960         if(this.fireEvent("beforeload", this, options) !== false){
10961             this.storeOptions(options);
10962             var p = Roo.apply(options.params || {}, this.baseParams);
10963             // if meta was not loaded from remote source.. try requesting it.
10964             if (!this.reader.metaFromRemote) {
10965                 p._requestMeta = 1;
10966             }
10967             if(this.sortInfo && this.remoteSort){
10968                 var pn = this.paramNames;
10969                 p[pn["sort"]] = this.sortInfo.field;
10970                 p[pn["dir"]] = this.sortInfo.direction;
10971             }
10972             if (this.multiSort) {
10973                 var pn = this.paramNames;
10974                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
10975             }
10976             
10977             this.proxy.load(p, this.reader, this.loadRecords, this, options);
10978         }
10979     },
10980
10981     /**
10982      * Reloads the Record cache from the configured Proxy using the configured Reader and
10983      * the options from the last load operation performed.
10984      * @param {Object} options (optional) An object containing properties which may override the options
10985      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
10986      * the most recently used options are reused).
10987      */
10988     reload : function(options){
10989         this.load(Roo.applyIf(options||{}, this.lastOptions));
10990     },
10991
10992     // private
10993     // Called as a callback by the Reader during a load operation.
10994     loadRecords : function(o, options, success){
10995         if(!o || success === false){
10996             if(success !== false){
10997                 this.fireEvent("load", this, [], options, o);
10998             }
10999             if(options.callback){
11000                 options.callback.call(options.scope || this, [], options, false);
11001             }
11002             return;
11003         }
11004         // if data returned failure - throw an exception.
11005         if (o.success === false) {
11006             // show a message if no listener is registered.
11007             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11008                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11009             }
11010             // loadmask wil be hooked into this..
11011             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11012             return;
11013         }
11014         var r = o.records, t = o.totalRecords || r.length;
11015         
11016         this.fireEvent("beforeloadadd", this, r, options, o);
11017         
11018         if(!options || options.add !== true){
11019             if(this.pruneModifiedRecords){
11020                 this.modified = [];
11021             }
11022             for(var i = 0, len = r.length; i < len; i++){
11023                 r[i].join(this);
11024             }
11025             if(this.snapshot){
11026                 this.data = this.snapshot;
11027                 delete this.snapshot;
11028             }
11029             this.data.clear();
11030             this.data.addAll(r);
11031             this.totalLength = t;
11032             this.applySort();
11033             this.fireEvent("datachanged", this);
11034         }else{
11035             this.totalLength = Math.max(t, this.data.length+r.length);
11036             this.add(r);
11037         }
11038         
11039         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11040                 
11041             var e = new Roo.data.Record({});
11042
11043             e.set(this.parent.displayField, this.parent.emptyTitle);
11044             e.set(this.parent.valueField, '');
11045
11046             this.insert(0, e);
11047         }
11048             
11049         this.fireEvent("load", this, r, options, o);
11050         if(options.callback){
11051             options.callback.call(options.scope || this, r, options, true);
11052         }
11053     },
11054
11055
11056     /**
11057      * Loads data from a passed data block. A Reader which understands the format of the data
11058      * must have been configured in the constructor.
11059      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11060      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11061      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11062      */
11063     loadData : function(o, append){
11064         var r = this.reader.readRecords(o);
11065         this.loadRecords(r, {add: append}, true);
11066     },
11067
11068     /**
11069      * Gets the number of cached records.
11070      * <p>
11071      * <em>If using paging, this may not be the total size of the dataset. If the data object
11072      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11073      * the data set size</em>
11074      */
11075     getCount : function(){
11076         return this.data.length || 0;
11077     },
11078
11079     /**
11080      * Gets the total number of records in the dataset as returned by the server.
11081      * <p>
11082      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11083      * the dataset size</em>
11084      */
11085     getTotalCount : function(){
11086         return this.totalLength || 0;
11087     },
11088
11089     /**
11090      * Returns the sort state of the Store as an object with two properties:
11091      * <pre><code>
11092  field {String} The name of the field by which the Records are sorted
11093  direction {String} The sort order, "ASC" or "DESC"
11094      * </code></pre>
11095      */
11096     getSortState : function(){
11097         return this.sortInfo;
11098     },
11099
11100     // private
11101     applySort : function(){
11102         if(this.sortInfo && !this.remoteSort){
11103             var s = this.sortInfo, f = s.field;
11104             var st = this.fields.get(f).sortType;
11105             var fn = function(r1, r2){
11106                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11107                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11108             };
11109             this.data.sort(s.direction, fn);
11110             if(this.snapshot && this.snapshot != this.data){
11111                 this.snapshot.sort(s.direction, fn);
11112             }
11113         }
11114     },
11115
11116     /**
11117      * Sets the default sort column and order to be used by the next load operation.
11118      * @param {String} fieldName The name of the field to sort by.
11119      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11120      */
11121     setDefaultSort : function(field, dir){
11122         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11123     },
11124
11125     /**
11126      * Sort the Records.
11127      * If remote sorting is used, the sort is performed on the server, and the cache is
11128      * reloaded. If local sorting is used, the cache is sorted internally.
11129      * @param {String} fieldName The name of the field to sort by.
11130      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11131      */
11132     sort : function(fieldName, dir){
11133         var f = this.fields.get(fieldName);
11134         if(!dir){
11135             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11136             
11137             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11138                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11139             }else{
11140                 dir = f.sortDir;
11141             }
11142         }
11143         this.sortToggle[f.name] = dir;
11144         this.sortInfo = {field: f.name, direction: dir};
11145         if(!this.remoteSort){
11146             this.applySort();
11147             this.fireEvent("datachanged", this);
11148         }else{
11149             this.load(this.lastOptions);
11150         }
11151     },
11152
11153     /**
11154      * Calls the specified function for each of the Records in the cache.
11155      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11156      * Returning <em>false</em> aborts and exits the iteration.
11157      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11158      */
11159     each : function(fn, scope){
11160         this.data.each(fn, scope);
11161     },
11162
11163     /**
11164      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11165      * (e.g., during paging).
11166      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11167      */
11168     getModifiedRecords : function(){
11169         return this.modified;
11170     },
11171
11172     // private
11173     createFilterFn : function(property, value, anyMatch){
11174         if(!value.exec){ // not a regex
11175             value = String(value);
11176             if(value.length == 0){
11177                 return false;
11178             }
11179             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11180         }
11181         return function(r){
11182             return value.test(r.data[property]);
11183         };
11184     },
11185
11186     /**
11187      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11188      * @param {String} property A field on your records
11189      * @param {Number} start The record index to start at (defaults to 0)
11190      * @param {Number} end The last record index to include (defaults to length - 1)
11191      * @return {Number} The sum
11192      */
11193     sum : function(property, start, end){
11194         var rs = this.data.items, v = 0;
11195         start = start || 0;
11196         end = (end || end === 0) ? end : rs.length-1;
11197
11198         for(var i = start; i <= end; i++){
11199             v += (rs[i].data[property] || 0);
11200         }
11201         return v;
11202     },
11203
11204     /**
11205      * Filter the records by a specified property.
11206      * @param {String} field A field on your records
11207      * @param {String/RegExp} value Either a string that the field
11208      * should start with or a RegExp to test against the field
11209      * @param {Boolean} anyMatch True to match any part not just the beginning
11210      */
11211     filter : function(property, value, anyMatch){
11212         var fn = this.createFilterFn(property, value, anyMatch);
11213         return fn ? this.filterBy(fn) : this.clearFilter();
11214     },
11215
11216     /**
11217      * Filter by a function. The specified function will be called with each
11218      * record in this data source. If the function returns true the record is included,
11219      * otherwise it is filtered.
11220      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11221      * @param {Object} scope (optional) The scope of the function (defaults to this)
11222      */
11223     filterBy : function(fn, scope){
11224         this.snapshot = this.snapshot || this.data;
11225         this.data = this.queryBy(fn, scope||this);
11226         this.fireEvent("datachanged", this);
11227     },
11228
11229     /**
11230      * Query the records by a specified property.
11231      * @param {String} field A field on your records
11232      * @param {String/RegExp} value Either a string that the field
11233      * should start with or a RegExp to test against the field
11234      * @param {Boolean} anyMatch True to match any part not just the beginning
11235      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11236      */
11237     query : function(property, value, anyMatch){
11238         var fn = this.createFilterFn(property, value, anyMatch);
11239         return fn ? this.queryBy(fn) : this.data.clone();
11240     },
11241
11242     /**
11243      * Query by a function. The specified function will be called with each
11244      * record in this data source. If the function returns true the record is included
11245      * in the results.
11246      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11247      * @param {Object} scope (optional) The scope of the function (defaults to this)
11248       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11249      **/
11250     queryBy : function(fn, scope){
11251         var data = this.snapshot || this.data;
11252         return data.filterBy(fn, scope||this);
11253     },
11254
11255     /**
11256      * Collects unique values for a particular dataIndex from this store.
11257      * @param {String} dataIndex The property to collect
11258      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11259      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11260      * @return {Array} An array of the unique values
11261      **/
11262     collect : function(dataIndex, allowNull, bypassFilter){
11263         var d = (bypassFilter === true && this.snapshot) ?
11264                 this.snapshot.items : this.data.items;
11265         var v, sv, r = [], l = {};
11266         for(var i = 0, len = d.length; i < len; i++){
11267             v = d[i].data[dataIndex];
11268             sv = String(v);
11269             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11270                 l[sv] = true;
11271                 r[r.length] = v;
11272             }
11273         }
11274         return r;
11275     },
11276
11277     /**
11278      * Revert to a view of the Record cache with no filtering applied.
11279      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11280      */
11281     clearFilter : function(suppressEvent){
11282         if(this.snapshot && this.snapshot != this.data){
11283             this.data = this.snapshot;
11284             delete this.snapshot;
11285             if(suppressEvent !== true){
11286                 this.fireEvent("datachanged", this);
11287             }
11288         }
11289     },
11290
11291     // private
11292     afterEdit : function(record){
11293         if(this.modified.indexOf(record) == -1){
11294             this.modified.push(record);
11295         }
11296         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11297     },
11298     
11299     // private
11300     afterReject : function(record){
11301         this.modified.remove(record);
11302         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11303     },
11304
11305     // private
11306     afterCommit : function(record){
11307         this.modified.remove(record);
11308         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11309     },
11310
11311     /**
11312      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11313      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11314      */
11315     commitChanges : function(){
11316         var m = this.modified.slice(0);
11317         this.modified = [];
11318         for(var i = 0, len = m.length; i < len; i++){
11319             m[i].commit();
11320         }
11321     },
11322
11323     /**
11324      * Cancel outstanding changes on all changed records.
11325      */
11326     rejectChanges : function(){
11327         var m = this.modified.slice(0);
11328         this.modified = [];
11329         for(var i = 0, len = m.length; i < len; i++){
11330             m[i].reject();
11331         }
11332     },
11333
11334     onMetaChange : function(meta, rtype, o){
11335         this.recordType = rtype;
11336         this.fields = rtype.prototype.fields;
11337         delete this.snapshot;
11338         this.sortInfo = meta.sortInfo || this.sortInfo;
11339         this.modified = [];
11340         this.fireEvent('metachange', this, this.reader.meta);
11341     },
11342     
11343     moveIndex : function(data, type)
11344     {
11345         var index = this.indexOf(data);
11346         
11347         var newIndex = index + type;
11348         
11349         this.remove(data);
11350         
11351         this.insert(newIndex, data);
11352         
11353     }
11354 });/*
11355  * Based on:
11356  * Ext JS Library 1.1.1
11357  * Copyright(c) 2006-2007, Ext JS, LLC.
11358  *
11359  * Originally Released Under LGPL - original licence link has changed is not relivant.
11360  *
11361  * Fork - LGPL
11362  * <script type="text/javascript">
11363  */
11364
11365 /**
11366  * @class Roo.data.SimpleStore
11367  * @extends Roo.data.Store
11368  * Small helper class to make creating Stores from Array data easier.
11369  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11370  * @cfg {Array} fields An array of field definition objects, or field name strings.
11371  * @cfg {Array} data The multi-dimensional array of data
11372  * @constructor
11373  * @param {Object} config
11374  */
11375 Roo.data.SimpleStore = function(config){
11376     Roo.data.SimpleStore.superclass.constructor.call(this, {
11377         isLocal : true,
11378         reader: new Roo.data.ArrayReader({
11379                 id: config.id
11380             },
11381             Roo.data.Record.create(config.fields)
11382         ),
11383         proxy : new Roo.data.MemoryProxy(config.data)
11384     });
11385     this.load();
11386 };
11387 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11388  * Based on:
11389  * Ext JS Library 1.1.1
11390  * Copyright(c) 2006-2007, Ext JS, LLC.
11391  *
11392  * Originally Released Under LGPL - original licence link has changed is not relivant.
11393  *
11394  * Fork - LGPL
11395  * <script type="text/javascript">
11396  */
11397
11398 /**
11399 /**
11400  * @extends Roo.data.Store
11401  * @class Roo.data.JsonStore
11402  * Small helper class to make creating Stores for JSON data easier. <br/>
11403 <pre><code>
11404 var store = new Roo.data.JsonStore({
11405     url: 'get-images.php',
11406     root: 'images',
11407     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11408 });
11409 </code></pre>
11410  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11411  * JsonReader and HttpProxy (unless inline data is provided).</b>
11412  * @cfg {Array} fields An array of field definition objects, or field name strings.
11413  * @constructor
11414  * @param {Object} config
11415  */
11416 Roo.data.JsonStore = function(c){
11417     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11418         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11419         reader: new Roo.data.JsonReader(c, c.fields)
11420     }));
11421 };
11422 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11423  * Based on:
11424  * Ext JS Library 1.1.1
11425  * Copyright(c) 2006-2007, Ext JS, LLC.
11426  *
11427  * Originally Released Under LGPL - original licence link has changed is not relivant.
11428  *
11429  * Fork - LGPL
11430  * <script type="text/javascript">
11431  */
11432
11433  
11434 Roo.data.Field = function(config){
11435     if(typeof config == "string"){
11436         config = {name: config};
11437     }
11438     Roo.apply(this, config);
11439     
11440     if(!this.type){
11441         this.type = "auto";
11442     }
11443     
11444     var st = Roo.data.SortTypes;
11445     // named sortTypes are supported, here we look them up
11446     if(typeof this.sortType == "string"){
11447         this.sortType = st[this.sortType];
11448     }
11449     
11450     // set default sortType for strings and dates
11451     if(!this.sortType){
11452         switch(this.type){
11453             case "string":
11454                 this.sortType = st.asUCString;
11455                 break;
11456             case "date":
11457                 this.sortType = st.asDate;
11458                 break;
11459             default:
11460                 this.sortType = st.none;
11461         }
11462     }
11463
11464     // define once
11465     var stripRe = /[\$,%]/g;
11466
11467     // prebuilt conversion function for this field, instead of
11468     // switching every time we're reading a value
11469     if(!this.convert){
11470         var cv, dateFormat = this.dateFormat;
11471         switch(this.type){
11472             case "":
11473             case "auto":
11474             case undefined:
11475                 cv = function(v){ return v; };
11476                 break;
11477             case "string":
11478                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11479                 break;
11480             case "int":
11481                 cv = function(v){
11482                     return v !== undefined && v !== null && v !== '' ?
11483                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11484                     };
11485                 break;
11486             case "float":
11487                 cv = function(v){
11488                     return v !== undefined && v !== null && v !== '' ?
11489                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11490                     };
11491                 break;
11492             case "bool":
11493             case "boolean":
11494                 cv = function(v){ return v === true || v === "true" || v == 1; };
11495                 break;
11496             case "date":
11497                 cv = function(v){
11498                     if(!v){
11499                         return '';
11500                     }
11501                     if(v instanceof Date){
11502                         return v;
11503                     }
11504                     if(dateFormat){
11505                         if(dateFormat == "timestamp"){
11506                             return new Date(v*1000);
11507                         }
11508                         return Date.parseDate(v, dateFormat);
11509                     }
11510                     var parsed = Date.parse(v);
11511                     return parsed ? new Date(parsed) : null;
11512                 };
11513              break;
11514             
11515         }
11516         this.convert = cv;
11517     }
11518 };
11519
11520 Roo.data.Field.prototype = {
11521     dateFormat: null,
11522     defaultValue: "",
11523     mapping: null,
11524     sortType : null,
11525     sortDir : "ASC"
11526 };/*
11527  * Based on:
11528  * Ext JS Library 1.1.1
11529  * Copyright(c) 2006-2007, Ext JS, LLC.
11530  *
11531  * Originally Released Under LGPL - original licence link has changed is not relivant.
11532  *
11533  * Fork - LGPL
11534  * <script type="text/javascript">
11535  */
11536  
11537 // Base class for reading structured data from a data source.  This class is intended to be
11538 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11539
11540 /**
11541  * @class Roo.data.DataReader
11542  * Base class for reading structured data from a data source.  This class is intended to be
11543  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11544  */
11545
11546 Roo.data.DataReader = function(meta, recordType){
11547     
11548     this.meta = meta;
11549     
11550     this.recordType = recordType instanceof Array ? 
11551         Roo.data.Record.create(recordType) : recordType;
11552 };
11553
11554 Roo.data.DataReader.prototype = {
11555      /**
11556      * Create an empty record
11557      * @param {Object} data (optional) - overlay some values
11558      * @return {Roo.data.Record} record created.
11559      */
11560     newRow :  function(d) {
11561         var da =  {};
11562         this.recordType.prototype.fields.each(function(c) {
11563             switch( c.type) {
11564                 case 'int' : da[c.name] = 0; break;
11565                 case 'date' : da[c.name] = new Date(); break;
11566                 case 'float' : da[c.name] = 0.0; break;
11567                 case 'boolean' : da[c.name] = false; break;
11568                 default : da[c.name] = ""; break;
11569             }
11570             
11571         });
11572         return new this.recordType(Roo.apply(da, d));
11573     }
11574     
11575 };/*
11576  * Based on:
11577  * Ext JS Library 1.1.1
11578  * Copyright(c) 2006-2007, Ext JS, LLC.
11579  *
11580  * Originally Released Under LGPL - original licence link has changed is not relivant.
11581  *
11582  * Fork - LGPL
11583  * <script type="text/javascript">
11584  */
11585
11586 /**
11587  * @class Roo.data.DataProxy
11588  * @extends Roo.data.Observable
11589  * This class is an abstract base class for implementations which provide retrieval of
11590  * unformatted data objects.<br>
11591  * <p>
11592  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11593  * (of the appropriate type which knows how to parse the data object) to provide a block of
11594  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11595  * <p>
11596  * Custom implementations must implement the load method as described in
11597  * {@link Roo.data.HttpProxy#load}.
11598  */
11599 Roo.data.DataProxy = function(){
11600     this.addEvents({
11601         /**
11602          * @event beforeload
11603          * Fires before a network request is made to retrieve a data object.
11604          * @param {Object} This DataProxy object.
11605          * @param {Object} params The params parameter to the load function.
11606          */
11607         beforeload : true,
11608         /**
11609          * @event load
11610          * Fires before the load method's callback is called.
11611          * @param {Object} This DataProxy object.
11612          * @param {Object} o The data object.
11613          * @param {Object} arg The callback argument object passed to the load function.
11614          */
11615         load : true,
11616         /**
11617          * @event loadexception
11618          * Fires if an Exception occurs during data retrieval.
11619          * @param {Object} This DataProxy object.
11620          * @param {Object} o The data object.
11621          * @param {Object} arg The callback argument object passed to the load function.
11622          * @param {Object} e The Exception.
11623          */
11624         loadexception : true
11625     });
11626     Roo.data.DataProxy.superclass.constructor.call(this);
11627 };
11628
11629 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11630
11631     /**
11632      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11633      */
11634 /*
11635  * Based on:
11636  * Ext JS Library 1.1.1
11637  * Copyright(c) 2006-2007, Ext JS, LLC.
11638  *
11639  * Originally Released Under LGPL - original licence link has changed is not relivant.
11640  *
11641  * Fork - LGPL
11642  * <script type="text/javascript">
11643  */
11644 /**
11645  * @class Roo.data.MemoryProxy
11646  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11647  * to the Reader when its load method is called.
11648  * @constructor
11649  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11650  */
11651 Roo.data.MemoryProxy = function(data){
11652     if (data.data) {
11653         data = data.data;
11654     }
11655     Roo.data.MemoryProxy.superclass.constructor.call(this);
11656     this.data = data;
11657 };
11658
11659 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11660     
11661     /**
11662      * Load data from the requested source (in this case an in-memory
11663      * data object passed to the constructor), read the data object into
11664      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11665      * process that block using the passed callback.
11666      * @param {Object} params This parameter is not used by the MemoryProxy class.
11667      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11668      * object into a block of Roo.data.Records.
11669      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11670      * The function must be passed <ul>
11671      * <li>The Record block object</li>
11672      * <li>The "arg" argument from the load function</li>
11673      * <li>A boolean success indicator</li>
11674      * </ul>
11675      * @param {Object} scope The scope in which to call the callback
11676      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11677      */
11678     load : function(params, reader, callback, scope, arg){
11679         params = params || {};
11680         var result;
11681         try {
11682             result = reader.readRecords(this.data);
11683         }catch(e){
11684             this.fireEvent("loadexception", this, arg, null, e);
11685             callback.call(scope, null, arg, false);
11686             return;
11687         }
11688         callback.call(scope, result, arg, true);
11689     },
11690     
11691     // private
11692     update : function(params, records){
11693         
11694     }
11695 });/*
11696  * Based on:
11697  * Ext JS Library 1.1.1
11698  * Copyright(c) 2006-2007, Ext JS, LLC.
11699  *
11700  * Originally Released Under LGPL - original licence link has changed is not relivant.
11701  *
11702  * Fork - LGPL
11703  * <script type="text/javascript">
11704  */
11705 /**
11706  * @class Roo.data.HttpProxy
11707  * @extends Roo.data.DataProxy
11708  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11709  * configured to reference a certain URL.<br><br>
11710  * <p>
11711  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11712  * from which the running page was served.<br><br>
11713  * <p>
11714  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11715  * <p>
11716  * Be aware that to enable the browser to parse an XML document, the server must set
11717  * the Content-Type header in the HTTP response to "text/xml".
11718  * @constructor
11719  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11720  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11721  * will be used to make the request.
11722  */
11723 Roo.data.HttpProxy = function(conn){
11724     Roo.data.HttpProxy.superclass.constructor.call(this);
11725     // is conn a conn config or a real conn?
11726     this.conn = conn;
11727     this.useAjax = !conn || !conn.events;
11728   
11729 };
11730
11731 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11732     // thse are take from connection...
11733     
11734     /**
11735      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11736      */
11737     /**
11738      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11739      * extra parameters to each request made by this object. (defaults to undefined)
11740      */
11741     /**
11742      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11743      *  to each request made by this object. (defaults to undefined)
11744      */
11745     /**
11746      * @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)
11747      */
11748     /**
11749      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11750      */
11751      /**
11752      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11753      * @type Boolean
11754      */
11755   
11756
11757     /**
11758      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11759      * @type Boolean
11760      */
11761     /**
11762      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11763      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11764      * a finer-grained basis than the DataProxy events.
11765      */
11766     getConnection : function(){
11767         return this.useAjax ? Roo.Ajax : this.conn;
11768     },
11769
11770     /**
11771      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11772      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11773      * process that block using the passed callback.
11774      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11775      * for the request to the remote server.
11776      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11777      * object into a block of Roo.data.Records.
11778      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11779      * The function must be passed <ul>
11780      * <li>The Record block object</li>
11781      * <li>The "arg" argument from the load function</li>
11782      * <li>A boolean success indicator</li>
11783      * </ul>
11784      * @param {Object} scope The scope in which to call the callback
11785      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11786      */
11787     load : function(params, reader, callback, scope, arg){
11788         if(this.fireEvent("beforeload", this, params) !== false){
11789             var  o = {
11790                 params : params || {},
11791                 request: {
11792                     callback : callback,
11793                     scope : scope,
11794                     arg : arg
11795                 },
11796                 reader: reader,
11797                 callback : this.loadResponse,
11798                 scope: this
11799             };
11800             if(this.useAjax){
11801                 Roo.applyIf(o, this.conn);
11802                 if(this.activeRequest){
11803                     Roo.Ajax.abort(this.activeRequest);
11804                 }
11805                 this.activeRequest = Roo.Ajax.request(o);
11806             }else{
11807                 this.conn.request(o);
11808             }
11809         }else{
11810             callback.call(scope||this, null, arg, false);
11811         }
11812     },
11813
11814     // private
11815     loadResponse : function(o, success, response){
11816         delete this.activeRequest;
11817         if(!success){
11818             this.fireEvent("loadexception", this, o, response);
11819             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11820             return;
11821         }
11822         var result;
11823         try {
11824             result = o.reader.read(response);
11825         }catch(e){
11826             this.fireEvent("loadexception", this, o, response, e);
11827             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11828             return;
11829         }
11830         
11831         this.fireEvent("load", this, o, o.request.arg);
11832         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11833     },
11834
11835     // private
11836     update : function(dataSet){
11837
11838     },
11839
11840     // private
11841     updateResponse : function(dataSet){
11842
11843     }
11844 });/*
11845  * Based on:
11846  * Ext JS Library 1.1.1
11847  * Copyright(c) 2006-2007, Ext JS, LLC.
11848  *
11849  * Originally Released Under LGPL - original licence link has changed is not relivant.
11850  *
11851  * Fork - LGPL
11852  * <script type="text/javascript">
11853  */
11854
11855 /**
11856  * @class Roo.data.ScriptTagProxy
11857  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11858  * other than the originating domain of the running page.<br><br>
11859  * <p>
11860  * <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
11861  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11862  * <p>
11863  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11864  * source code that is used as the source inside a &lt;script> tag.<br><br>
11865  * <p>
11866  * In order for the browser to process the returned data, the server must wrap the data object
11867  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11868  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11869  * depending on whether the callback name was passed:
11870  * <p>
11871  * <pre><code>
11872 boolean scriptTag = false;
11873 String cb = request.getParameter("callback");
11874 if (cb != null) {
11875     scriptTag = true;
11876     response.setContentType("text/javascript");
11877 } else {
11878     response.setContentType("application/x-json");
11879 }
11880 Writer out = response.getWriter();
11881 if (scriptTag) {
11882     out.write(cb + "(");
11883 }
11884 out.print(dataBlock.toJsonString());
11885 if (scriptTag) {
11886     out.write(");");
11887 }
11888 </pre></code>
11889  *
11890  * @constructor
11891  * @param {Object} config A configuration object.
11892  */
11893 Roo.data.ScriptTagProxy = function(config){
11894     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
11895     Roo.apply(this, config);
11896     this.head = document.getElementsByTagName("head")[0];
11897 };
11898
11899 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
11900
11901 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
11902     /**
11903      * @cfg {String} url The URL from which to request the data object.
11904      */
11905     /**
11906      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
11907      */
11908     timeout : 30000,
11909     /**
11910      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
11911      * the server the name of the callback function set up by the load call to process the returned data object.
11912      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
11913      * javascript output which calls this named function passing the data object as its only parameter.
11914      */
11915     callbackParam : "callback",
11916     /**
11917      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
11918      * name to the request.
11919      */
11920     nocache : true,
11921
11922     /**
11923      * Load data from the configured URL, read the data object into
11924      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11925      * process that block using the passed callback.
11926      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11927      * for the request to the remote server.
11928      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11929      * object into a block of Roo.data.Records.
11930      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11931      * The function must be passed <ul>
11932      * <li>The Record block object</li>
11933      * <li>The "arg" argument from the load function</li>
11934      * <li>A boolean success indicator</li>
11935      * </ul>
11936      * @param {Object} scope The scope in which to call the callback
11937      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11938      */
11939     load : function(params, reader, callback, scope, arg){
11940         if(this.fireEvent("beforeload", this, params) !== false){
11941
11942             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
11943
11944             var url = this.url;
11945             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
11946             if(this.nocache){
11947                 url += "&_dc=" + (new Date().getTime());
11948             }
11949             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
11950             var trans = {
11951                 id : transId,
11952                 cb : "stcCallback"+transId,
11953                 scriptId : "stcScript"+transId,
11954                 params : params,
11955                 arg : arg,
11956                 url : url,
11957                 callback : callback,
11958                 scope : scope,
11959                 reader : reader
11960             };
11961             var conn = this;
11962
11963             window[trans.cb] = function(o){
11964                 conn.handleResponse(o, trans);
11965             };
11966
11967             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
11968
11969             if(this.autoAbort !== false){
11970                 this.abort();
11971             }
11972
11973             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
11974
11975             var script = document.createElement("script");
11976             script.setAttribute("src", url);
11977             script.setAttribute("type", "text/javascript");
11978             script.setAttribute("id", trans.scriptId);
11979             this.head.appendChild(script);
11980
11981             this.trans = trans;
11982         }else{
11983             callback.call(scope||this, null, arg, false);
11984         }
11985     },
11986
11987     // private
11988     isLoading : function(){
11989         return this.trans ? true : false;
11990     },
11991
11992     /**
11993      * Abort the current server request.
11994      */
11995     abort : function(){
11996         if(this.isLoading()){
11997             this.destroyTrans(this.trans);
11998         }
11999     },
12000
12001     // private
12002     destroyTrans : function(trans, isLoaded){
12003         this.head.removeChild(document.getElementById(trans.scriptId));
12004         clearTimeout(trans.timeoutId);
12005         if(isLoaded){
12006             window[trans.cb] = undefined;
12007             try{
12008                 delete window[trans.cb];
12009             }catch(e){}
12010         }else{
12011             // if hasn't been loaded, wait for load to remove it to prevent script error
12012             window[trans.cb] = function(){
12013                 window[trans.cb] = undefined;
12014                 try{
12015                     delete window[trans.cb];
12016                 }catch(e){}
12017             };
12018         }
12019     },
12020
12021     // private
12022     handleResponse : function(o, trans){
12023         this.trans = false;
12024         this.destroyTrans(trans, true);
12025         var result;
12026         try {
12027             result = trans.reader.readRecords(o);
12028         }catch(e){
12029             this.fireEvent("loadexception", this, o, trans.arg, e);
12030             trans.callback.call(trans.scope||window, null, trans.arg, false);
12031             return;
12032         }
12033         this.fireEvent("load", this, o, trans.arg);
12034         trans.callback.call(trans.scope||window, result, trans.arg, true);
12035     },
12036
12037     // private
12038     handleFailure : function(trans){
12039         this.trans = false;
12040         this.destroyTrans(trans, false);
12041         this.fireEvent("loadexception", this, null, trans.arg);
12042         trans.callback.call(trans.scope||window, null, trans.arg, false);
12043     }
12044 });/*
12045  * Based on:
12046  * Ext JS Library 1.1.1
12047  * Copyright(c) 2006-2007, Ext JS, LLC.
12048  *
12049  * Originally Released Under LGPL - original licence link has changed is not relivant.
12050  *
12051  * Fork - LGPL
12052  * <script type="text/javascript">
12053  */
12054
12055 /**
12056  * @class Roo.data.JsonReader
12057  * @extends Roo.data.DataReader
12058  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12059  * based on mappings in a provided Roo.data.Record constructor.
12060  * 
12061  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12062  * in the reply previously. 
12063  * 
12064  * <p>
12065  * Example code:
12066  * <pre><code>
12067 var RecordDef = Roo.data.Record.create([
12068     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12069     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12070 ]);
12071 var myReader = new Roo.data.JsonReader({
12072     totalProperty: "results",    // The property which contains the total dataset size (optional)
12073     root: "rows",                // The property which contains an Array of row objects
12074     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12075 }, RecordDef);
12076 </code></pre>
12077  * <p>
12078  * This would consume a JSON file like this:
12079  * <pre><code>
12080 { 'results': 2, 'rows': [
12081     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12082     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12083 }
12084 </code></pre>
12085  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12086  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12087  * paged from the remote server.
12088  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12089  * @cfg {String} root name of the property which contains the Array of row objects.
12090  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12091  * @cfg {Array} fields Array of field definition objects
12092  * @constructor
12093  * Create a new JsonReader
12094  * @param {Object} meta Metadata configuration options
12095  * @param {Object} recordType Either an Array of field definition objects,
12096  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12097  */
12098 Roo.data.JsonReader = function(meta, recordType){
12099     
12100     meta = meta || {};
12101     // set some defaults:
12102     Roo.applyIf(meta, {
12103         totalProperty: 'total',
12104         successProperty : 'success',
12105         root : 'data',
12106         id : 'id'
12107     });
12108     
12109     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12110 };
12111 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12112     
12113     /**
12114      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12115      * Used by Store query builder to append _requestMeta to params.
12116      * 
12117      */
12118     metaFromRemote : false,
12119     /**
12120      * This method is only used by a DataProxy which has retrieved data from a remote server.
12121      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12122      * @return {Object} data A data block which is used by an Roo.data.Store object as
12123      * a cache of Roo.data.Records.
12124      */
12125     read : function(response){
12126         var json = response.responseText;
12127        
12128         var o = /* eval:var:o */ eval("("+json+")");
12129         if(!o) {
12130             throw {message: "JsonReader.read: Json object not found"};
12131         }
12132         
12133         if(o.metaData){
12134             
12135             delete this.ef;
12136             this.metaFromRemote = true;
12137             this.meta = o.metaData;
12138             this.recordType = Roo.data.Record.create(o.metaData.fields);
12139             this.onMetaChange(this.meta, this.recordType, o);
12140         }
12141         return this.readRecords(o);
12142     },
12143
12144     // private function a store will implement
12145     onMetaChange : function(meta, recordType, o){
12146
12147     },
12148
12149     /**
12150          * @ignore
12151          */
12152     simpleAccess: function(obj, subsc) {
12153         return obj[subsc];
12154     },
12155
12156         /**
12157          * @ignore
12158          */
12159     getJsonAccessor: function(){
12160         var re = /[\[\.]/;
12161         return function(expr) {
12162             try {
12163                 return(re.test(expr))
12164                     ? new Function("obj", "return obj." + expr)
12165                     : function(obj){
12166                         return obj[expr];
12167                     };
12168             } catch(e){}
12169             return Roo.emptyFn;
12170         };
12171     }(),
12172
12173     /**
12174      * Create a data block containing Roo.data.Records from an XML document.
12175      * @param {Object} o An object which contains an Array of row objects in the property specified
12176      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12177      * which contains the total size of the dataset.
12178      * @return {Object} data A data block which is used by an Roo.data.Store object as
12179      * a cache of Roo.data.Records.
12180      */
12181     readRecords : function(o){
12182         /**
12183          * After any data loads, the raw JSON data is available for further custom processing.
12184          * @type Object
12185          */
12186         this.o = o;
12187         var s = this.meta, Record = this.recordType,
12188             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12189
12190 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12191         if (!this.ef) {
12192             if(s.totalProperty) {
12193                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12194                 }
12195                 if(s.successProperty) {
12196                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12197                 }
12198                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12199                 if (s.id) {
12200                         var g = this.getJsonAccessor(s.id);
12201                         this.getId = function(rec) {
12202                                 var r = g(rec);  
12203                                 return (r === undefined || r === "") ? null : r;
12204                         };
12205                 } else {
12206                         this.getId = function(){return null;};
12207                 }
12208             this.ef = [];
12209             for(var jj = 0; jj < fl; jj++){
12210                 f = fi[jj];
12211                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12212                 this.ef[jj] = this.getJsonAccessor(map);
12213             }
12214         }
12215
12216         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12217         if(s.totalProperty){
12218             var vt = parseInt(this.getTotal(o), 10);
12219             if(!isNaN(vt)){
12220                 totalRecords = vt;
12221             }
12222         }
12223         if(s.successProperty){
12224             var vs = this.getSuccess(o);
12225             if(vs === false || vs === 'false'){
12226                 success = false;
12227             }
12228         }
12229         var records = [];
12230         for(var i = 0; i < c; i++){
12231                 var n = root[i];
12232             var values = {};
12233             var id = this.getId(n);
12234             for(var j = 0; j < fl; j++){
12235                 f = fi[j];
12236             var v = this.ef[j](n);
12237             if (!f.convert) {
12238                 Roo.log('missing convert for ' + f.name);
12239                 Roo.log(f);
12240                 continue;
12241             }
12242             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12243             }
12244             var record = new Record(values, id);
12245             record.json = n;
12246             records[i] = record;
12247         }
12248         return {
12249             raw : o,
12250             success : success,
12251             records : records,
12252             totalRecords : totalRecords
12253         };
12254     }
12255 });/*
12256  * Based on:
12257  * Ext JS Library 1.1.1
12258  * Copyright(c) 2006-2007, Ext JS, LLC.
12259  *
12260  * Originally Released Under LGPL - original licence link has changed is not relivant.
12261  *
12262  * Fork - LGPL
12263  * <script type="text/javascript">
12264  */
12265
12266 /**
12267  * @class Roo.data.ArrayReader
12268  * @extends Roo.data.DataReader
12269  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12270  * Each element of that Array represents a row of data fields. The
12271  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12272  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12273  * <p>
12274  * Example code:.
12275  * <pre><code>
12276 var RecordDef = Roo.data.Record.create([
12277     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12278     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12279 ]);
12280 var myReader = new Roo.data.ArrayReader({
12281     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12282 }, RecordDef);
12283 </code></pre>
12284  * <p>
12285  * This would consume an Array like this:
12286  * <pre><code>
12287 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12288   </code></pre>
12289  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12290  * @constructor
12291  * Create a new JsonReader
12292  * @param {Object} meta Metadata configuration options.
12293  * @param {Object} recordType Either an Array of field definition objects
12294  * as specified to {@link Roo.data.Record#create},
12295  * or an {@link Roo.data.Record} object
12296  * created using {@link Roo.data.Record#create}.
12297  */
12298 Roo.data.ArrayReader = function(meta, recordType){
12299     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12300 };
12301
12302 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12303     /**
12304      * Create a data block containing Roo.data.Records from an XML document.
12305      * @param {Object} o An Array of row objects which represents the dataset.
12306      * @return {Object} data A data block which is used by an Roo.data.Store object as
12307      * a cache of Roo.data.Records.
12308      */
12309     readRecords : function(o){
12310         var sid = this.meta ? this.meta.id : null;
12311         var recordType = this.recordType, fields = recordType.prototype.fields;
12312         var records = [];
12313         var root = o;
12314             for(var i = 0; i < root.length; i++){
12315                     var n = root[i];
12316                 var values = {};
12317                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12318                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12319                 var f = fields.items[j];
12320                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12321                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12322                 v = f.convert(v);
12323                 values[f.name] = v;
12324             }
12325                 var record = new recordType(values, id);
12326                 record.json = n;
12327                 records[records.length] = record;
12328             }
12329             return {
12330                 records : records,
12331                 totalRecords : records.length
12332             };
12333     }
12334 });/*
12335  * - LGPL
12336  * * 
12337  */
12338
12339 /**
12340  * @class Roo.bootstrap.ComboBox
12341  * @extends Roo.bootstrap.TriggerField
12342  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12343  * @cfg {Boolean} append (true|false) default false
12344  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12345  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12346  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12347  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12348  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12349  * @cfg {Boolean} animate default true
12350  * @cfg {Boolean} emptyResultText only for touch device
12351  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12352  * @cfg {String} emptyTitle default ''
12353  * @constructor
12354  * Create a new ComboBox.
12355  * @param {Object} config Configuration options
12356  */
12357 Roo.bootstrap.ComboBox = function(config){
12358     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12359     this.addEvents({
12360         /**
12361          * @event expand
12362          * Fires when the dropdown list is expanded
12363              * @param {Roo.bootstrap.ComboBox} combo This combo box
12364              */
12365         'expand' : true,
12366         /**
12367          * @event collapse
12368          * Fires when the dropdown list is collapsed
12369              * @param {Roo.bootstrap.ComboBox} combo This combo box
12370              */
12371         'collapse' : true,
12372         /**
12373          * @event beforeselect
12374          * Fires before a list item is selected. Return false to cancel the selection.
12375              * @param {Roo.bootstrap.ComboBox} combo This combo box
12376              * @param {Roo.data.Record} record The data record returned from the underlying store
12377              * @param {Number} index The index of the selected item in the dropdown list
12378              */
12379         'beforeselect' : true,
12380         /**
12381          * @event select
12382          * Fires when a list item is selected
12383              * @param {Roo.bootstrap.ComboBox} combo This combo box
12384              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12385              * @param {Number} index The index of the selected item in the dropdown list
12386              */
12387         'select' : true,
12388         /**
12389          * @event beforequery
12390          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12391          * The event object passed has these properties:
12392              * @param {Roo.bootstrap.ComboBox} combo This combo box
12393              * @param {String} query The query
12394              * @param {Boolean} forceAll true to force "all" query
12395              * @param {Boolean} cancel true to cancel the query
12396              * @param {Object} e The query event object
12397              */
12398         'beforequery': true,
12399          /**
12400          * @event add
12401          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12402              * @param {Roo.bootstrap.ComboBox} combo This combo box
12403              */
12404         'add' : true,
12405         /**
12406          * @event edit
12407          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12408              * @param {Roo.bootstrap.ComboBox} combo This combo box
12409              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12410              */
12411         'edit' : true,
12412         /**
12413          * @event remove
12414          * Fires when the remove value from the combobox array
12415              * @param {Roo.bootstrap.ComboBox} combo This combo box
12416              */
12417         'remove' : true,
12418         /**
12419          * @event afterremove
12420          * Fires when the remove value from the combobox array
12421              * @param {Roo.bootstrap.ComboBox} combo This combo box
12422              */
12423         'afterremove' : true,
12424         /**
12425          * @event specialfilter
12426          * Fires when specialfilter
12427             * @param {Roo.bootstrap.ComboBox} combo This combo box
12428             */
12429         'specialfilter' : true,
12430         /**
12431          * @event tick
12432          * Fires when tick the element
12433             * @param {Roo.bootstrap.ComboBox} combo This combo box
12434             */
12435         'tick' : true,
12436         /**
12437          * @event touchviewdisplay
12438          * Fires when touch view require special display (default is using displayField)
12439             * @param {Roo.bootstrap.ComboBox} combo This combo box
12440             * @param {Object} cfg set html .
12441             */
12442         'touchviewdisplay' : true
12443         
12444     });
12445     
12446     this.item = [];
12447     this.tickItems = [];
12448     
12449     this.selectedIndex = -1;
12450     if(this.mode == 'local'){
12451         if(config.queryDelay === undefined){
12452             this.queryDelay = 10;
12453         }
12454         if(config.minChars === undefined){
12455             this.minChars = 0;
12456         }
12457     }
12458 };
12459
12460 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12461      
12462     /**
12463      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12464      * rendering into an Roo.Editor, defaults to false)
12465      */
12466     /**
12467      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12468      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12469      */
12470     /**
12471      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12472      */
12473     /**
12474      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12475      * the dropdown list (defaults to undefined, with no header element)
12476      */
12477
12478      /**
12479      * @cfg {String/Roo.Template} tpl The template to use to render the output
12480      */
12481      
12482      /**
12483      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12484      */
12485     listWidth: undefined,
12486     /**
12487      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12488      * mode = 'remote' or 'text' if mode = 'local')
12489      */
12490     displayField: undefined,
12491     
12492     /**
12493      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12494      * mode = 'remote' or 'value' if mode = 'local'). 
12495      * Note: use of a valueField requires the user make a selection
12496      * in order for a value to be mapped.
12497      */
12498     valueField: undefined,
12499     /**
12500      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12501      */
12502     modalTitle : '',
12503     
12504     /**
12505      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12506      * field's data value (defaults to the underlying DOM element's name)
12507      */
12508     hiddenName: undefined,
12509     /**
12510      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12511      */
12512     listClass: '',
12513     /**
12514      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12515      */
12516     selectedClass: 'active',
12517     
12518     /**
12519      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12520      */
12521     shadow:'sides',
12522     /**
12523      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12524      * anchor positions (defaults to 'tl-bl')
12525      */
12526     listAlign: 'tl-bl?',
12527     /**
12528      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12529      */
12530     maxHeight: 300,
12531     /**
12532      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12533      * query specified by the allQuery config option (defaults to 'query')
12534      */
12535     triggerAction: 'query',
12536     /**
12537      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12538      * (defaults to 4, does not apply if editable = false)
12539      */
12540     minChars : 4,
12541     /**
12542      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12543      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12544      */
12545     typeAhead: false,
12546     /**
12547      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12548      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12549      */
12550     queryDelay: 500,
12551     /**
12552      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12553      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12554      */
12555     pageSize: 0,
12556     /**
12557      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12558      * when editable = true (defaults to false)
12559      */
12560     selectOnFocus:false,
12561     /**
12562      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12563      */
12564     queryParam: 'query',
12565     /**
12566      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12567      * when mode = 'remote' (defaults to 'Loading...')
12568      */
12569     loadingText: 'Loading...',
12570     /**
12571      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12572      */
12573     resizable: false,
12574     /**
12575      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12576      */
12577     handleHeight : 8,
12578     /**
12579      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12580      * traditional select (defaults to true)
12581      */
12582     editable: true,
12583     /**
12584      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12585      */
12586     allQuery: '',
12587     /**
12588      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12589      */
12590     mode: 'remote',
12591     /**
12592      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12593      * listWidth has a higher value)
12594      */
12595     minListWidth : 70,
12596     /**
12597      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12598      * allow the user to set arbitrary text into the field (defaults to false)
12599      */
12600     forceSelection:false,
12601     /**
12602      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12603      * if typeAhead = true (defaults to 250)
12604      */
12605     typeAheadDelay : 250,
12606     /**
12607      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12608      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12609      */
12610     valueNotFoundText : undefined,
12611     /**
12612      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12613      */
12614     blockFocus : false,
12615     
12616     /**
12617      * @cfg {Boolean} disableClear Disable showing of clear button.
12618      */
12619     disableClear : false,
12620     /**
12621      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12622      */
12623     alwaysQuery : false,
12624     
12625     /**
12626      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12627      */
12628     multiple : false,
12629     
12630     /**
12631      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12632      */
12633     invalidClass : "has-warning",
12634     
12635     /**
12636      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12637      */
12638     validClass : "has-success",
12639     
12640     /**
12641      * @cfg {Boolean} specialFilter (true|false) special filter default false
12642      */
12643     specialFilter : false,
12644     
12645     /**
12646      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12647      */
12648     mobileTouchView : true,
12649     
12650     /**
12651      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12652      */
12653     useNativeIOS : false,
12654     
12655     ios_options : false,
12656     
12657     //private
12658     addicon : false,
12659     editicon: false,
12660     
12661     page: 0,
12662     hasQuery: false,
12663     append: false,
12664     loadNext: false,
12665     autoFocus : true,
12666     tickable : false,
12667     btnPosition : 'right',
12668     triggerList : true,
12669     showToggleBtn : true,
12670     animate : true,
12671     emptyResultText: 'Empty',
12672     triggerText : 'Select',
12673     emptyTitle : '',
12674     
12675     // element that contains real text value.. (when hidden is used..)
12676     
12677     getAutoCreate : function()
12678     {   
12679         var cfg = false;
12680         //render
12681         /*
12682          * Render classic select for iso
12683          */
12684         
12685         if(Roo.isIOS && this.useNativeIOS){
12686             cfg = this.getAutoCreateNativeIOS();
12687             return cfg;
12688         }
12689         
12690         /*
12691          * Touch Devices
12692          */
12693         
12694         if(Roo.isTouch && this.mobileTouchView){
12695             cfg = this.getAutoCreateTouchView();
12696             return cfg;;
12697         }
12698         
12699         /*
12700          *  Normal ComboBox
12701          */
12702         if(!this.tickable){
12703             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12704             if(this.name == 'info_year_invest_id_display_name'){
12705                 Roo.log('cfg.................................................');
12706                 Roo.log(cfg);
12707             }
12708             return cfg;
12709         }
12710         
12711         /*
12712          *  ComboBox with tickable selections
12713          */
12714              
12715         var align = this.labelAlign || this.parentLabelAlign();
12716         
12717         cfg = {
12718             cls : 'form-group roo-combobox-tickable' //input-group
12719         };
12720         
12721         var btn_text_select = '';
12722         var btn_text_done = '';
12723         var btn_text_cancel = '';
12724         
12725         if (this.btn_text_show) {
12726             btn_text_select = 'Select';
12727             btn_text_done = 'Done';
12728             btn_text_cancel = 'Cancel'; 
12729         }
12730         
12731         var buttons = {
12732             tag : 'div',
12733             cls : 'tickable-buttons',
12734             cn : [
12735                 {
12736                     tag : 'button',
12737                     type : 'button',
12738                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12739                     //html : this.triggerText
12740                     html: btn_text_select
12741                 },
12742                 {
12743                     tag : 'button',
12744                     type : 'button',
12745                     name : 'ok',
12746                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12747                     //html : 'Done'
12748                     html: btn_text_done
12749                 },
12750                 {
12751                     tag : 'button',
12752                     type : 'button',
12753                     name : 'cancel',
12754                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12755                     //html : 'Cancel'
12756                     html: btn_text_cancel
12757                 }
12758             ]
12759         };
12760         
12761         if(this.editable){
12762             buttons.cn.unshift({
12763                 tag: 'input',
12764                 cls: 'roo-select2-search-field-input'
12765             });
12766         }
12767         
12768         var _this = this;
12769         
12770         Roo.each(buttons.cn, function(c){
12771             if (_this.size) {
12772                 c.cls += ' btn-' + _this.size;
12773             }
12774
12775             if (_this.disabled) {
12776                 c.disabled = true;
12777             }
12778         });
12779         
12780         var box = {
12781             tag: 'div',
12782             cn: [
12783                 {
12784                     tag: 'input',
12785                     type : 'hidden',
12786                     cls: 'form-hidden-field'
12787                 },
12788                 {
12789                     tag: 'ul',
12790                     cls: 'roo-select2-choices',
12791                     cn:[
12792                         {
12793                             tag: 'li',
12794                             cls: 'roo-select2-search-field',
12795                             cn: [
12796                                 buttons
12797                             ]
12798                         }
12799                     ]
12800                 }
12801             ]
12802         };
12803         
12804         var combobox = {
12805             cls: 'roo-select2-container input-group roo-select2-container-multi',
12806             cn: [
12807                 box
12808 //                {
12809 //                    tag: 'ul',
12810 //                    cls: 'typeahead typeahead-long dropdown-menu',
12811 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12812 //                }
12813             ]
12814         };
12815         
12816         if(this.hasFeedback && !this.allowBlank){
12817             
12818             var feedback = {
12819                 tag: 'span',
12820                 cls: 'glyphicon form-control-feedback'
12821             };
12822
12823             combobox.cn.push(feedback);
12824         }
12825         
12826         
12827         if (align ==='left' && this.fieldLabel.length) {
12828             
12829             cfg.cls += ' roo-form-group-label-left';
12830             
12831             cfg.cn = [
12832                 {
12833                     tag : 'i',
12834                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12835                     tooltip : 'This field is required'
12836                 },
12837                 {
12838                     tag: 'label',
12839                     'for' :  id,
12840                     cls : 'control-label',
12841                     html : this.fieldLabel
12842
12843                 },
12844                 {
12845                     cls : "", 
12846                     cn: [
12847                         combobox
12848                     ]
12849                 }
12850
12851             ];
12852             
12853             var labelCfg = cfg.cn[1];
12854             var contentCfg = cfg.cn[2];
12855             
12856
12857             if(this.indicatorpos == 'right'){
12858                 
12859                 cfg.cn = [
12860                     {
12861                         tag: 'label',
12862                         'for' :  id,
12863                         cls : 'control-label',
12864                         cn : [
12865                             {
12866                                 tag : 'span',
12867                                 html : this.fieldLabel
12868                             },
12869                             {
12870                                 tag : 'i',
12871                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12872                                 tooltip : 'This field is required'
12873                             }
12874                         ]
12875                     },
12876                     {
12877                         cls : "",
12878                         cn: [
12879                             combobox
12880                         ]
12881                     }
12882
12883                 ];
12884                 
12885                 
12886                 
12887                 labelCfg = cfg.cn[0];
12888                 contentCfg = cfg.cn[1];
12889             
12890             }
12891             
12892             if(this.labelWidth > 12){
12893                 labelCfg.style = "width: " + this.labelWidth + 'px';
12894             }
12895             
12896             if(this.labelWidth < 13 && this.labelmd == 0){
12897                 this.labelmd = this.labelWidth;
12898             }
12899             
12900             if(this.labellg > 0){
12901                 labelCfg.cls += ' col-lg-' + this.labellg;
12902                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12903             }
12904             
12905             if(this.labelmd > 0){
12906                 labelCfg.cls += ' col-md-' + this.labelmd;
12907                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12908             }
12909             
12910             if(this.labelsm > 0){
12911                 labelCfg.cls += ' col-sm-' + this.labelsm;
12912                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12913             }
12914             
12915             if(this.labelxs > 0){
12916                 labelCfg.cls += ' col-xs-' + this.labelxs;
12917                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12918             }
12919                 
12920                 
12921         } else if ( this.fieldLabel.length) {
12922 //                Roo.log(" label");
12923                  cfg.cn = [
12924                     {
12925                         tag : 'i',
12926                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12927                         tooltip : 'This field is required'
12928                     },
12929                     {
12930                         tag: 'label',
12931                         //cls : 'input-group-addon',
12932                         html : this.fieldLabel
12933                     },
12934                     combobox
12935                 ];
12936                 
12937                 if(this.indicatorpos == 'right'){
12938                     Roo.log('hidden name:'+this.hiddenName);
12939                     cfg.cn = [
12940                         {
12941                             tag: 'label',
12942                             //cls : 'input-group-addon',
12943                             html : this.fieldLabel
12944                         },
12945                         {
12946                             tag : 'i',
12947                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12948                             tooltip : 'This field is required'
12949                         },
12950                         combobox
12951                     ];
12952                     
12953                 }
12954
12955         } else {
12956             
12957 //                Roo.log(" no label && no align");
12958                 cfg = combobox
12959                      
12960                 
12961         }
12962          
12963         var settings=this;
12964         ['xs','sm','md','lg'].map(function(size){
12965             if (settings[size]) {
12966                 cfg.cls += ' col-' + size + '-' + settings[size];
12967             }
12968         });
12969         
12970         return cfg;
12971         
12972     },
12973     
12974     _initEventsCalled : false,
12975     
12976     // private
12977     initEvents: function()
12978     {   
12979         if (this._initEventsCalled) { // as we call render... prevent looping...
12980             return;
12981         }
12982         this._initEventsCalled = true;
12983         
12984         if (!this.store) {
12985             throw "can not find store for combo";
12986         }
12987         
12988         this.store = Roo.factory(this.store, Roo.data);
12989         this.store.parent = this;
12990         
12991         // if we are building from html. then this element is so complex, that we can not really
12992         // use the rendered HTML.
12993         // so we have to trash and replace the previous code.
12994         if (Roo.XComponent.build_from_html) {
12995             
12996             // remove this element....
12997             var e = this.el.dom, k=0;
12998             while (e ) { e = e.previousSibling;  ++k;}
12999
13000             this.el.remove();
13001             
13002             this.el=false;
13003             this.rendered = false;
13004             
13005             this.render(this.parent().getChildContainer(true), k);
13006             
13007             
13008             
13009         }
13010         
13011         if(Roo.isIOS && this.useNativeIOS){
13012             this.initIOSView();
13013             return;
13014         }
13015         
13016         /*
13017          * Touch Devices
13018          */
13019         
13020         if(Roo.isTouch && this.mobileTouchView){
13021             this.initTouchView();
13022             return;
13023         }
13024         
13025         if(this.tickable){
13026             this.initTickableEvents();
13027             return;
13028         }
13029         
13030         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13031         
13032         if(this.hiddenName){
13033             
13034             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13035             
13036             this.hiddenField.dom.value =
13037                 this.hiddenValue !== undefined ? this.hiddenValue :
13038                 this.value !== undefined ? this.value : '';
13039
13040             // prevent input submission
13041             this.el.dom.removeAttribute('name');
13042             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13043              
13044              
13045         }
13046         //if(Roo.isGecko){
13047         //    this.el.dom.setAttribute('autocomplete', 'off');
13048         //}
13049         
13050         var cls = 'x-combo-list';
13051         
13052         //this.list = new Roo.Layer({
13053         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13054         //});
13055         
13056         var _this = this;
13057         
13058         (function(){
13059             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13060             _this.list.setWidth(lw);
13061         }).defer(100);
13062         
13063         this.list.on('mouseover', this.onViewOver, this);
13064         this.list.on('mousemove', this.onViewMove, this);
13065         
13066         this.list.on('scroll', this.onViewScroll, this);
13067         
13068         /*
13069         this.list.swallowEvent('mousewheel');
13070         this.assetHeight = 0;
13071
13072         if(this.title){
13073             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13074             this.assetHeight += this.header.getHeight();
13075         }
13076
13077         this.innerList = this.list.createChild({cls:cls+'-inner'});
13078         this.innerList.on('mouseover', this.onViewOver, this);
13079         this.innerList.on('mousemove', this.onViewMove, this);
13080         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13081         
13082         if(this.allowBlank && !this.pageSize && !this.disableClear){
13083             this.footer = this.list.createChild({cls:cls+'-ft'});
13084             this.pageTb = new Roo.Toolbar(this.footer);
13085            
13086         }
13087         if(this.pageSize){
13088             this.footer = this.list.createChild({cls:cls+'-ft'});
13089             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13090                     {pageSize: this.pageSize});
13091             
13092         }
13093         
13094         if (this.pageTb && this.allowBlank && !this.disableClear) {
13095             var _this = this;
13096             this.pageTb.add(new Roo.Toolbar.Fill(), {
13097                 cls: 'x-btn-icon x-btn-clear',
13098                 text: '&#160;',
13099                 handler: function()
13100                 {
13101                     _this.collapse();
13102                     _this.clearValue();
13103                     _this.onSelect(false, -1);
13104                 }
13105             });
13106         }
13107         if (this.footer) {
13108             this.assetHeight += this.footer.getHeight();
13109         }
13110         */
13111             
13112         if(!this.tpl){
13113             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13114         }
13115
13116         this.view = new Roo.View(this.list, this.tpl, {
13117             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13118         });
13119         //this.view.wrapEl.setDisplayed(false);
13120         this.view.on('click', this.onViewClick, this);
13121         
13122         
13123         this.store.on('beforeload', this.onBeforeLoad, this);
13124         this.store.on('load', this.onLoad, this);
13125         this.store.on('loadexception', this.onLoadException, this);
13126         /*
13127         if(this.resizable){
13128             this.resizer = new Roo.Resizable(this.list,  {
13129                pinned:true, handles:'se'
13130             });
13131             this.resizer.on('resize', function(r, w, h){
13132                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13133                 this.listWidth = w;
13134                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13135                 this.restrictHeight();
13136             }, this);
13137             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13138         }
13139         */
13140         if(!this.editable){
13141             this.editable = true;
13142             this.setEditable(false);
13143         }
13144         
13145         /*
13146         
13147         if (typeof(this.events.add.listeners) != 'undefined') {
13148             
13149             this.addicon = this.wrap.createChild(
13150                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13151        
13152             this.addicon.on('click', function(e) {
13153                 this.fireEvent('add', this);
13154             }, this);
13155         }
13156         if (typeof(this.events.edit.listeners) != 'undefined') {
13157             
13158             this.editicon = this.wrap.createChild(
13159                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13160             if (this.addicon) {
13161                 this.editicon.setStyle('margin-left', '40px');
13162             }
13163             this.editicon.on('click', function(e) {
13164                 
13165                 // we fire even  if inothing is selected..
13166                 this.fireEvent('edit', this, this.lastData );
13167                 
13168             }, this);
13169         }
13170         */
13171         
13172         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13173             "up" : function(e){
13174                 this.inKeyMode = true;
13175                 this.selectPrev();
13176             },
13177
13178             "down" : function(e){
13179                 if(!this.isExpanded()){
13180                     this.onTriggerClick();
13181                 }else{
13182                     this.inKeyMode = true;
13183                     this.selectNext();
13184                 }
13185             },
13186
13187             "enter" : function(e){
13188 //                this.onViewClick();
13189                 //return true;
13190                 this.collapse();
13191                 
13192                 if(this.fireEvent("specialkey", this, e)){
13193                     this.onViewClick(false);
13194                 }
13195                 
13196                 return true;
13197             },
13198
13199             "esc" : function(e){
13200                 this.collapse();
13201             },
13202
13203             "tab" : function(e){
13204                 this.collapse();
13205                 
13206                 if(this.fireEvent("specialkey", this, e)){
13207                     this.onViewClick(false);
13208                 }
13209                 
13210                 return true;
13211             },
13212
13213             scope : this,
13214
13215             doRelay : function(foo, bar, hname){
13216                 if(hname == 'down' || this.scope.isExpanded()){
13217                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13218                 }
13219                 return true;
13220             },
13221
13222             forceKeyDown: true
13223         });
13224         
13225         
13226         this.queryDelay = Math.max(this.queryDelay || 10,
13227                 this.mode == 'local' ? 10 : 250);
13228         
13229         
13230         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13231         
13232         if(this.typeAhead){
13233             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13234         }
13235         if(this.editable !== false){
13236             this.inputEl().on("keyup", this.onKeyUp, this);
13237         }
13238         if(this.forceSelection){
13239             this.inputEl().on('blur', this.doForce, this);
13240         }
13241         
13242         if(this.multiple){
13243             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13244             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13245         }
13246     },
13247     
13248     initTickableEvents: function()
13249     {   
13250         this.createList();
13251         
13252         if(this.hiddenName){
13253             
13254             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13255             
13256             this.hiddenField.dom.value =
13257                 this.hiddenValue !== undefined ? this.hiddenValue :
13258                 this.value !== undefined ? this.value : '';
13259
13260             // prevent input submission
13261             this.el.dom.removeAttribute('name');
13262             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13263              
13264              
13265         }
13266         
13267 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13268         
13269         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13270         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13271         if(this.triggerList){
13272             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13273         }
13274          
13275         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13276         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13277         
13278         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13279         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13280         
13281         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13282         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13283         
13284         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13285         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13286         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13287         
13288         this.okBtn.hide();
13289         this.cancelBtn.hide();
13290         
13291         var _this = this;
13292         
13293         (function(){
13294             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13295             _this.list.setWidth(lw);
13296         }).defer(100);
13297         
13298         this.list.on('mouseover', this.onViewOver, this);
13299         this.list.on('mousemove', this.onViewMove, this);
13300         
13301         this.list.on('scroll', this.onViewScroll, this);
13302         
13303         if(!this.tpl){
13304             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>';
13305         }
13306
13307         this.view = new Roo.View(this.list, this.tpl, {
13308             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13309         });
13310         
13311         //this.view.wrapEl.setDisplayed(false);
13312         this.view.on('click', this.onViewClick, this);
13313         
13314         
13315         
13316         this.store.on('beforeload', this.onBeforeLoad, this);
13317         this.store.on('load', this.onLoad, this);
13318         this.store.on('loadexception', this.onLoadException, this);
13319         
13320         if(this.editable){
13321             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13322                 "up" : function(e){
13323                     this.inKeyMode = true;
13324                     this.selectPrev();
13325                 },
13326
13327                 "down" : function(e){
13328                     this.inKeyMode = true;
13329                     this.selectNext();
13330                 },
13331
13332                 "enter" : function(e){
13333                     if(this.fireEvent("specialkey", this, e)){
13334                         this.onViewClick(false);
13335                     }
13336                     
13337                     return true;
13338                 },
13339
13340                 "esc" : function(e){
13341                     this.onTickableFooterButtonClick(e, false, false);
13342                 },
13343
13344                 "tab" : function(e){
13345                     this.fireEvent("specialkey", this, e);
13346                     
13347                     this.onTickableFooterButtonClick(e, false, false);
13348                     
13349                     return true;
13350                 },
13351
13352                 scope : this,
13353
13354                 doRelay : function(e, fn, key){
13355                     if(this.scope.isExpanded()){
13356                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13357                     }
13358                     return true;
13359                 },
13360
13361                 forceKeyDown: true
13362             });
13363         }
13364         
13365         this.queryDelay = Math.max(this.queryDelay || 10,
13366                 this.mode == 'local' ? 10 : 250);
13367         
13368         
13369         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13370         
13371         if(this.typeAhead){
13372             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13373         }
13374         
13375         if(this.editable !== false){
13376             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13377         }
13378         
13379         this.indicator = this.indicatorEl();
13380         
13381         if(this.indicator){
13382             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13383             this.indicator.hide();
13384         }
13385         
13386     },
13387
13388     onDestroy : function(){
13389         if(this.view){
13390             this.view.setStore(null);
13391             this.view.el.removeAllListeners();
13392             this.view.el.remove();
13393             this.view.purgeListeners();
13394         }
13395         if(this.list){
13396             this.list.dom.innerHTML  = '';
13397         }
13398         
13399         if(this.store){
13400             this.store.un('beforeload', this.onBeforeLoad, this);
13401             this.store.un('load', this.onLoad, this);
13402             this.store.un('loadexception', this.onLoadException, this);
13403         }
13404         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13405     },
13406
13407     // private
13408     fireKey : function(e){
13409         if(e.isNavKeyPress() && !this.list.isVisible()){
13410             this.fireEvent("specialkey", this, e);
13411         }
13412     },
13413
13414     // private
13415     onResize: function(w, h){
13416 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13417 //        
13418 //        if(typeof w != 'number'){
13419 //            // we do not handle it!?!?
13420 //            return;
13421 //        }
13422 //        var tw = this.trigger.getWidth();
13423 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13424 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13425 //        var x = w - tw;
13426 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13427 //            
13428 //        //this.trigger.setStyle('left', x+'px');
13429 //        
13430 //        if(this.list && this.listWidth === undefined){
13431 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13432 //            this.list.setWidth(lw);
13433 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13434 //        }
13435         
13436     
13437         
13438     },
13439
13440     /**
13441      * Allow or prevent the user from directly editing the field text.  If false is passed,
13442      * the user will only be able to select from the items defined in the dropdown list.  This method
13443      * is the runtime equivalent of setting the 'editable' config option at config time.
13444      * @param {Boolean} value True to allow the user to directly edit the field text
13445      */
13446     setEditable : function(value){
13447         if(value == this.editable){
13448             return;
13449         }
13450         this.editable = value;
13451         if(!value){
13452             this.inputEl().dom.setAttribute('readOnly', true);
13453             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13454             this.inputEl().addClass('x-combo-noedit');
13455         }else{
13456             this.inputEl().dom.setAttribute('readOnly', false);
13457             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13458             this.inputEl().removeClass('x-combo-noedit');
13459         }
13460     },
13461
13462     // private
13463     
13464     onBeforeLoad : function(combo,opts){
13465         if(!this.hasFocus){
13466             return;
13467         }
13468          if (!opts.add) {
13469             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13470          }
13471         this.restrictHeight();
13472         this.selectedIndex = -1;
13473     },
13474
13475     // private
13476     onLoad : function(){
13477         
13478         this.hasQuery = false;
13479         
13480         if(!this.hasFocus){
13481             return;
13482         }
13483         
13484         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13485             this.loading.hide();
13486         }
13487         
13488         if(this.store.getCount() > 0){
13489             
13490             this.expand();
13491             this.restrictHeight();
13492             if(this.lastQuery == this.allQuery){
13493                 if(this.editable && !this.tickable){
13494                     this.inputEl().dom.select();
13495                 }
13496                 
13497                 if(
13498                     !this.selectByValue(this.value, true) &&
13499                     this.autoFocus && 
13500                     (
13501                         !this.store.lastOptions ||
13502                         typeof(this.store.lastOptions.add) == 'undefined' || 
13503                         this.store.lastOptions.add != true
13504                     )
13505                 ){
13506                     this.select(0, true);
13507                 }
13508             }else{
13509                 if(this.autoFocus){
13510                     this.selectNext();
13511                 }
13512                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13513                     this.taTask.delay(this.typeAheadDelay);
13514                 }
13515             }
13516         }else{
13517             this.onEmptyResults();
13518         }
13519         
13520         //this.el.focus();
13521     },
13522     // private
13523     onLoadException : function()
13524     {
13525         this.hasQuery = false;
13526         
13527         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13528             this.loading.hide();
13529         }
13530         
13531         if(this.tickable && this.editable){
13532             return;
13533         }
13534         
13535         this.collapse();
13536         // only causes errors at present
13537         //Roo.log(this.store.reader.jsonData);
13538         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13539             // fixme
13540             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13541         //}
13542         
13543         
13544     },
13545     // private
13546     onTypeAhead : function(){
13547         if(this.store.getCount() > 0){
13548             var r = this.store.getAt(0);
13549             var newValue = r.data[this.displayField];
13550             var len = newValue.length;
13551             var selStart = this.getRawValue().length;
13552             
13553             if(selStart != len){
13554                 this.setRawValue(newValue);
13555                 this.selectText(selStart, newValue.length);
13556             }
13557         }
13558     },
13559
13560     // private
13561     onSelect : function(record, index){
13562         
13563         if(this.fireEvent('beforeselect', this, record, index) !== false){
13564         
13565             this.setFromData(index > -1 ? record.data : false);
13566             
13567             this.collapse();
13568             this.fireEvent('select', this, record, index);
13569         }
13570     },
13571
13572     /**
13573      * Returns the currently selected field value or empty string if no value is set.
13574      * @return {String} value The selected value
13575      */
13576     getValue : function()
13577     {
13578         if(Roo.isIOS && this.useNativeIOS){
13579             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13580         }
13581         
13582         if(this.multiple){
13583             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13584         }
13585         
13586         if(this.valueField){
13587             return typeof this.value != 'undefined' ? this.value : '';
13588         }else{
13589             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13590         }
13591     },
13592     
13593     getRawValue : function()
13594     {
13595         if(Roo.isIOS && this.useNativeIOS){
13596             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13597         }
13598         
13599         var v = this.inputEl().getValue();
13600         
13601         return v;
13602     },
13603
13604     /**
13605      * Clears any text/value currently set in the field
13606      */
13607     clearValue : function(){
13608         
13609         if(this.hiddenField){
13610             this.hiddenField.dom.value = '';
13611         }
13612         this.value = '';
13613         this.setRawValue('');
13614         this.lastSelectionText = '';
13615         this.lastData = false;
13616         
13617         var close = this.closeTriggerEl();
13618         
13619         if(close){
13620             close.hide();
13621         }
13622         
13623         this.validate();
13624         
13625     },
13626
13627     /**
13628      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13629      * will be displayed in the field.  If the value does not match the data value of an existing item,
13630      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13631      * Otherwise the field will be blank (although the value will still be set).
13632      * @param {String} value The value to match
13633      */
13634     setValue : function(v)
13635     {
13636         if(Roo.isIOS && this.useNativeIOS){
13637             this.setIOSValue(v);
13638             return;
13639         }
13640         
13641         if(this.multiple){
13642             this.syncValue();
13643             return;
13644         }
13645         
13646         var text = v;
13647         if(this.valueField){
13648             var r = this.findRecord(this.valueField, v);
13649             if(r){
13650                 text = r.data[this.displayField];
13651             }else if(this.valueNotFoundText !== undefined){
13652                 text = this.valueNotFoundText;
13653             }
13654         }
13655         this.lastSelectionText = text;
13656         if(this.hiddenField){
13657             this.hiddenField.dom.value = v;
13658         }
13659         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13660         this.value = v;
13661         
13662         var close = this.closeTriggerEl();
13663         
13664         if(close){
13665             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13666         }
13667         
13668         this.validate();
13669     },
13670     /**
13671      * @property {Object} the last set data for the element
13672      */
13673     
13674     lastData : false,
13675     /**
13676      * Sets the value of the field based on a object which is related to the record format for the store.
13677      * @param {Object} value the value to set as. or false on reset?
13678      */
13679     setFromData : function(o){
13680         
13681         if(this.multiple){
13682             this.addItem(o);
13683             return;
13684         }
13685             
13686         var dv = ''; // display value
13687         var vv = ''; // value value..
13688         this.lastData = o;
13689         if (this.displayField) {
13690             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13691         } else {
13692             // this is an error condition!!!
13693             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13694         }
13695         
13696         if(this.valueField){
13697             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13698         }
13699         
13700         var close = this.closeTriggerEl();
13701         
13702         if(close){
13703             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
13704         }
13705         
13706         if(this.hiddenField){
13707             this.hiddenField.dom.value = vv;
13708             
13709             this.lastSelectionText = dv;
13710             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13711             this.value = vv;
13712             return;
13713         }
13714         // no hidden field.. - we store the value in 'value', but still display
13715         // display field!!!!
13716         this.lastSelectionText = dv;
13717         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13718         this.value = vv;
13719         
13720         
13721         
13722     },
13723     // private
13724     reset : function(){
13725         // overridden so that last data is reset..
13726         
13727         if(this.multiple){
13728             this.clearItem();
13729             return;
13730         }
13731         
13732         this.setValue(this.originalValue);
13733         //this.clearInvalid();
13734         this.lastData = false;
13735         if (this.view) {
13736             this.view.clearSelections();
13737         }
13738         
13739         this.validate();
13740     },
13741     // private
13742     findRecord : function(prop, value){
13743         var record;
13744         if(this.store.getCount() > 0){
13745             this.store.each(function(r){
13746                 if(r.data[prop] == value){
13747                     record = r;
13748                     return false;
13749                 }
13750                 return true;
13751             });
13752         }
13753         return record;
13754     },
13755     
13756     getName: function()
13757     {
13758         // returns hidden if it's set..
13759         if (!this.rendered) {return ''};
13760         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13761         
13762     },
13763     // private
13764     onViewMove : function(e, t){
13765         this.inKeyMode = false;
13766     },
13767
13768     // private
13769     onViewOver : function(e, t){
13770         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13771             return;
13772         }
13773         var item = this.view.findItemFromChild(t);
13774         
13775         if(item){
13776             var index = this.view.indexOf(item);
13777             this.select(index, false);
13778         }
13779     },
13780
13781     // private
13782     onViewClick : function(view, doFocus, el, e)
13783     {
13784         var index = this.view.getSelectedIndexes()[0];
13785         
13786         var r = this.store.getAt(index);
13787         
13788         if(this.tickable){
13789             
13790             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13791                 return;
13792             }
13793             
13794             var rm = false;
13795             var _this = this;
13796             
13797             Roo.each(this.tickItems, function(v,k){
13798                 
13799                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13800                     Roo.log(v);
13801                     _this.tickItems.splice(k, 1);
13802                     
13803                     if(typeof(e) == 'undefined' && view == false){
13804                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13805                     }
13806                     
13807                     rm = true;
13808                     return;
13809                 }
13810             });
13811             
13812             if(rm){
13813                 return;
13814             }
13815             
13816             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13817                 this.tickItems.push(r.data);
13818             }
13819             
13820             if(typeof(e) == 'undefined' && view == false){
13821                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13822             }
13823                     
13824             return;
13825         }
13826         
13827         if(r){
13828             this.onSelect(r, index);
13829         }
13830         if(doFocus !== false && !this.blockFocus){
13831             this.inputEl().focus();
13832         }
13833     },
13834
13835     // private
13836     restrictHeight : function(){
13837         //this.innerList.dom.style.height = '';
13838         //var inner = this.innerList.dom;
13839         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13840         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13841         //this.list.beginUpdate();
13842         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13843         this.list.alignTo(this.inputEl(), this.listAlign);
13844         this.list.alignTo(this.inputEl(), this.listAlign);
13845         //this.list.endUpdate();
13846     },
13847
13848     // private
13849     onEmptyResults : function(){
13850         
13851         if(this.tickable && this.editable){
13852             this.restrictHeight();
13853             return;
13854         }
13855         
13856         this.collapse();
13857     },
13858
13859     /**
13860      * Returns true if the dropdown list is expanded, else false.
13861      */
13862     isExpanded : function(){
13863         return this.list.isVisible();
13864     },
13865
13866     /**
13867      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13868      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13869      * @param {String} value The data value of the item to select
13870      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13871      * selected item if it is not currently in view (defaults to true)
13872      * @return {Boolean} True if the value matched an item in the list, else false
13873      */
13874     selectByValue : function(v, scrollIntoView){
13875         if(v !== undefined && v !== null){
13876             var r = this.findRecord(this.valueField || this.displayField, v);
13877             if(r){
13878                 this.select(this.store.indexOf(r), scrollIntoView);
13879                 return true;
13880             }
13881         }
13882         return false;
13883     },
13884
13885     /**
13886      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13887      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13888      * @param {Number} index The zero-based index of the list item to select
13889      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13890      * selected item if it is not currently in view (defaults to true)
13891      */
13892     select : function(index, scrollIntoView){
13893         this.selectedIndex = index;
13894         this.view.select(index);
13895         if(scrollIntoView !== false){
13896             var el = this.view.getNode(index);
13897             /*
13898              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13899              */
13900             if(el){
13901                 this.list.scrollChildIntoView(el, false);
13902             }
13903         }
13904     },
13905
13906     // private
13907     selectNext : function(){
13908         var ct = this.store.getCount();
13909         if(ct > 0){
13910             if(this.selectedIndex == -1){
13911                 this.select(0);
13912             }else if(this.selectedIndex < ct-1){
13913                 this.select(this.selectedIndex+1);
13914             }
13915         }
13916     },
13917
13918     // private
13919     selectPrev : function(){
13920         var ct = this.store.getCount();
13921         if(ct > 0){
13922             if(this.selectedIndex == -1){
13923                 this.select(0);
13924             }else if(this.selectedIndex != 0){
13925                 this.select(this.selectedIndex-1);
13926             }
13927         }
13928     },
13929
13930     // private
13931     onKeyUp : function(e){
13932         if(this.editable !== false && !e.isSpecialKey()){
13933             this.lastKey = e.getKey();
13934             this.dqTask.delay(this.queryDelay);
13935         }
13936     },
13937
13938     // private
13939     validateBlur : function(){
13940         return !this.list || !this.list.isVisible();   
13941     },
13942
13943     // private
13944     initQuery : function(){
13945         
13946         var v = this.getRawValue();
13947         
13948         if(this.tickable && this.editable){
13949             v = this.tickableInputEl().getValue();
13950         }
13951         
13952         this.doQuery(v);
13953     },
13954
13955     // private
13956     doForce : function(){
13957         if(this.inputEl().dom.value.length > 0){
13958             this.inputEl().dom.value =
13959                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13960              
13961         }
13962     },
13963
13964     /**
13965      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
13966      * query allowing the query action to be canceled if needed.
13967      * @param {String} query The SQL query to execute
13968      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13969      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
13970      * saved in the current store (defaults to false)
13971      */
13972     doQuery : function(q, forceAll){
13973         
13974         if(q === undefined || q === null){
13975             q = '';
13976         }
13977         var qe = {
13978             query: q,
13979             forceAll: forceAll,
13980             combo: this,
13981             cancel:false
13982         };
13983         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13984             return false;
13985         }
13986         q = qe.query;
13987         
13988         forceAll = qe.forceAll;
13989         if(forceAll === true || (q.length >= this.minChars)){
13990             
13991             this.hasQuery = true;
13992             
13993             if(this.lastQuery != q || this.alwaysQuery){
13994                 this.lastQuery = q;
13995                 if(this.mode == 'local'){
13996                     this.selectedIndex = -1;
13997                     if(forceAll){
13998                         this.store.clearFilter();
13999                     }else{
14000                         
14001                         if(this.specialFilter){
14002                             this.fireEvent('specialfilter', this);
14003                             this.onLoad();
14004                             return;
14005                         }
14006                         
14007                         this.store.filter(this.displayField, q);
14008                     }
14009                     
14010                     this.store.fireEvent("datachanged", this.store);
14011                     
14012                     this.onLoad();
14013                     
14014                     
14015                 }else{
14016                     
14017                     this.store.baseParams[this.queryParam] = q;
14018                     
14019                     var options = {params : this.getParams(q)};
14020                     
14021                     if(this.loadNext){
14022                         options.add = true;
14023                         options.params.start = this.page * this.pageSize;
14024                     }
14025                     
14026                     this.store.load(options);
14027                     
14028                     /*
14029                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14030                      *  we should expand the list on onLoad
14031                      *  so command out it
14032                      */
14033 //                    this.expand();
14034                 }
14035             }else{
14036                 this.selectedIndex = -1;
14037                 this.onLoad();   
14038             }
14039         }
14040         
14041         this.loadNext = false;
14042     },
14043     
14044     // private
14045     getParams : function(q){
14046         var p = {};
14047         //p[this.queryParam] = q;
14048         
14049         if(this.pageSize){
14050             p.start = 0;
14051             p.limit = this.pageSize;
14052         }
14053         return p;
14054     },
14055
14056     /**
14057      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14058      */
14059     collapse : function(){
14060         if(!this.isExpanded()){
14061             return;
14062         }
14063         
14064         this.list.hide();
14065         
14066         this.hasFocus = false;
14067         
14068         if(this.tickable){
14069             this.okBtn.hide();
14070             this.cancelBtn.hide();
14071             this.trigger.show();
14072             
14073             if(this.editable){
14074                 this.tickableInputEl().dom.value = '';
14075                 this.tickableInputEl().blur();
14076             }
14077             
14078         }
14079         
14080         Roo.get(document).un('mousedown', this.collapseIf, this);
14081         Roo.get(document).un('mousewheel', this.collapseIf, this);
14082         if (!this.editable) {
14083             Roo.get(document).un('keydown', this.listKeyPress, this);
14084         }
14085         this.fireEvent('collapse', this);
14086         
14087         this.validate();
14088     },
14089
14090     // private
14091     collapseIf : function(e){
14092         var in_combo  = e.within(this.el);
14093         var in_list =  e.within(this.list);
14094         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14095         
14096         if (in_combo || in_list || is_list) {
14097             //e.stopPropagation();
14098             return;
14099         }
14100         
14101         if(this.tickable){
14102             this.onTickableFooterButtonClick(e, false, false);
14103         }
14104
14105         this.collapse();
14106         
14107     },
14108
14109     /**
14110      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14111      */
14112     expand : function(){
14113        
14114         if(this.isExpanded() || !this.hasFocus){
14115             return;
14116         }
14117         
14118         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14119         this.list.setWidth(lw);
14120         
14121         Roo.log('expand');
14122         
14123         this.list.show();
14124         
14125         this.restrictHeight();
14126         
14127         if(this.tickable){
14128             
14129             this.tickItems = Roo.apply([], this.item);
14130             
14131             this.okBtn.show();
14132             this.cancelBtn.show();
14133             this.trigger.hide();
14134             
14135             if(this.editable){
14136                 this.tickableInputEl().focus();
14137             }
14138             
14139         }
14140         
14141         Roo.get(document).on('mousedown', this.collapseIf, this);
14142         Roo.get(document).on('mousewheel', this.collapseIf, this);
14143         if (!this.editable) {
14144             Roo.get(document).on('keydown', this.listKeyPress, this);
14145         }
14146         
14147         this.fireEvent('expand', this);
14148     },
14149
14150     // private
14151     // Implements the default empty TriggerField.onTriggerClick function
14152     onTriggerClick : function(e)
14153     {
14154         Roo.log('trigger click');
14155         
14156         if(this.disabled || !this.triggerList){
14157             return;
14158         }
14159         
14160         this.page = 0;
14161         this.loadNext = false;
14162         
14163         if(this.isExpanded()){
14164             this.collapse();
14165             if (!this.blockFocus) {
14166                 this.inputEl().focus();
14167             }
14168             
14169         }else {
14170             this.hasFocus = true;
14171             if(this.triggerAction == 'all') {
14172                 this.doQuery(this.allQuery, true);
14173             } else {
14174                 this.doQuery(this.getRawValue());
14175             }
14176             if (!this.blockFocus) {
14177                 this.inputEl().focus();
14178             }
14179         }
14180     },
14181     
14182     onTickableTriggerClick : function(e)
14183     {
14184         if(this.disabled){
14185             return;
14186         }
14187         
14188         this.page = 0;
14189         this.loadNext = false;
14190         this.hasFocus = true;
14191         
14192         if(this.triggerAction == 'all') {
14193             this.doQuery(this.allQuery, true);
14194         } else {
14195             this.doQuery(this.getRawValue());
14196         }
14197     },
14198     
14199     onSearchFieldClick : function(e)
14200     {
14201         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14202             this.onTickableFooterButtonClick(e, false, false);
14203             return;
14204         }
14205         
14206         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14207             return;
14208         }
14209         
14210         this.page = 0;
14211         this.loadNext = false;
14212         this.hasFocus = true;
14213         
14214         if(this.triggerAction == 'all') {
14215             this.doQuery(this.allQuery, true);
14216         } else {
14217             this.doQuery(this.getRawValue());
14218         }
14219     },
14220     
14221     listKeyPress : function(e)
14222     {
14223         //Roo.log('listkeypress');
14224         // scroll to first matching element based on key pres..
14225         if (e.isSpecialKey()) {
14226             return false;
14227         }
14228         var k = String.fromCharCode(e.getKey()).toUpperCase();
14229         //Roo.log(k);
14230         var match  = false;
14231         var csel = this.view.getSelectedNodes();
14232         var cselitem = false;
14233         if (csel.length) {
14234             var ix = this.view.indexOf(csel[0]);
14235             cselitem  = this.store.getAt(ix);
14236             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14237                 cselitem = false;
14238             }
14239             
14240         }
14241         
14242         this.store.each(function(v) { 
14243             if (cselitem) {
14244                 // start at existing selection.
14245                 if (cselitem.id == v.id) {
14246                     cselitem = false;
14247                 }
14248                 return true;
14249             }
14250                 
14251             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14252                 match = this.store.indexOf(v);
14253                 return false;
14254             }
14255             return true;
14256         }, this);
14257         
14258         if (match === false) {
14259             return true; // no more action?
14260         }
14261         // scroll to?
14262         this.view.select(match);
14263         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14264         sn.scrollIntoView(sn.dom.parentNode, false);
14265     },
14266     
14267     onViewScroll : function(e, t){
14268         
14269         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){
14270             return;
14271         }
14272         
14273         this.hasQuery = true;
14274         
14275         this.loading = this.list.select('.loading', true).first();
14276         
14277         if(this.loading === null){
14278             this.list.createChild({
14279                 tag: 'div',
14280                 cls: 'loading roo-select2-more-results roo-select2-active',
14281                 html: 'Loading more results...'
14282             });
14283             
14284             this.loading = this.list.select('.loading', true).first();
14285             
14286             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14287             
14288             this.loading.hide();
14289         }
14290         
14291         this.loading.show();
14292         
14293         var _combo = this;
14294         
14295         this.page++;
14296         this.loadNext = true;
14297         
14298         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14299         
14300         return;
14301     },
14302     
14303     addItem : function(o)
14304     {   
14305         var dv = ''; // display value
14306         
14307         if (this.displayField) {
14308             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14309         } else {
14310             // this is an error condition!!!
14311             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14312         }
14313         
14314         if(!dv.length){
14315             return;
14316         }
14317         
14318         var choice = this.choices.createChild({
14319             tag: 'li',
14320             cls: 'roo-select2-search-choice',
14321             cn: [
14322                 {
14323                     tag: 'div',
14324                     html: dv
14325                 },
14326                 {
14327                     tag: 'a',
14328                     href: '#',
14329                     cls: 'roo-select2-search-choice-close fa fa-times',
14330                     tabindex: '-1'
14331                 }
14332             ]
14333             
14334         }, this.searchField);
14335         
14336         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14337         
14338         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14339         
14340         this.item.push(o);
14341         
14342         this.lastData = o;
14343         
14344         this.syncValue();
14345         
14346         this.inputEl().dom.value = '';
14347         
14348         this.validate();
14349     },
14350     
14351     onRemoveItem : function(e, _self, o)
14352     {
14353         e.preventDefault();
14354         
14355         this.lastItem = Roo.apply([], this.item);
14356         
14357         var index = this.item.indexOf(o.data) * 1;
14358         
14359         if( index < 0){
14360             Roo.log('not this item?!');
14361             return;
14362         }
14363         
14364         this.item.splice(index, 1);
14365         o.item.remove();
14366         
14367         this.syncValue();
14368         
14369         this.fireEvent('remove', this, e);
14370         
14371         this.validate();
14372         
14373     },
14374     
14375     syncValue : function()
14376     {
14377         if(!this.item.length){
14378             this.clearValue();
14379             return;
14380         }
14381             
14382         var value = [];
14383         var _this = this;
14384         Roo.each(this.item, function(i){
14385             if(_this.valueField){
14386                 value.push(i[_this.valueField]);
14387                 return;
14388             }
14389
14390             value.push(i);
14391         });
14392
14393         this.value = value.join(',');
14394
14395         if(this.hiddenField){
14396             this.hiddenField.dom.value = this.value;
14397         }
14398         
14399         this.store.fireEvent("datachanged", this.store);
14400         
14401         this.validate();
14402     },
14403     
14404     clearItem : function()
14405     {
14406         if(!this.multiple){
14407             return;
14408         }
14409         
14410         this.item = [];
14411         
14412         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14413            c.remove();
14414         });
14415         
14416         this.syncValue();
14417         
14418         this.validate();
14419         
14420         if(this.tickable && !Roo.isTouch){
14421             this.view.refresh();
14422         }
14423     },
14424     
14425     inputEl: function ()
14426     {
14427         if(Roo.isIOS && this.useNativeIOS){
14428             return this.el.select('select.roo-ios-select', true).first();
14429         }
14430         
14431         if(Roo.isTouch && this.mobileTouchView){
14432             return this.el.select('input.form-control',true).first();
14433         }
14434         
14435         if(this.tickable){
14436             return this.searchField;
14437         }
14438         
14439         return this.el.select('input.form-control',true).first();
14440     },
14441     
14442     onTickableFooterButtonClick : function(e, btn, el)
14443     {
14444         e.preventDefault();
14445         
14446         this.lastItem = Roo.apply([], this.item);
14447         
14448         if(btn && btn.name == 'cancel'){
14449             this.tickItems = Roo.apply([], this.item);
14450             this.collapse();
14451             return;
14452         }
14453         
14454         this.clearItem();
14455         
14456         var _this = this;
14457         
14458         Roo.each(this.tickItems, function(o){
14459             _this.addItem(o);
14460         });
14461         
14462         this.collapse();
14463         
14464     },
14465     
14466     validate : function()
14467     {
14468         var v = this.getRawValue();
14469         
14470         if(this.multiple){
14471             v = this.getValue();
14472         }
14473         
14474         if(this.disabled || this.allowBlank || v.length){
14475             this.markValid();
14476             return true;
14477         }
14478         
14479         this.markInvalid();
14480         return false;
14481     },
14482     
14483     tickableInputEl : function()
14484     {
14485         if(!this.tickable || !this.editable){
14486             return this.inputEl();
14487         }
14488         
14489         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14490     },
14491     
14492     
14493     getAutoCreateTouchView : function()
14494     {
14495         var id = Roo.id();
14496         
14497         var cfg = {
14498             cls: 'form-group' //input-group
14499         };
14500         
14501         var input =  {
14502             tag: 'input',
14503             id : id,
14504             type : this.inputType,
14505             cls : 'form-control x-combo-noedit',
14506             autocomplete: 'new-password',
14507             placeholder : this.placeholder || '',
14508             readonly : true
14509         };
14510         
14511         if (this.name) {
14512             input.name = this.name;
14513         }
14514         
14515         if (this.size) {
14516             input.cls += ' input-' + this.size;
14517         }
14518         
14519         if (this.disabled) {
14520             input.disabled = true;
14521         }
14522         
14523         var inputblock = {
14524             cls : '',
14525             cn : [
14526                 input
14527             ]
14528         };
14529         
14530         if(this.before){
14531             inputblock.cls += ' input-group';
14532             
14533             inputblock.cn.unshift({
14534                 tag :'span',
14535                 cls : 'input-group-addon',
14536                 html : this.before
14537             });
14538         }
14539         
14540         if(this.removable && !this.multiple){
14541             inputblock.cls += ' roo-removable';
14542             
14543             inputblock.cn.push({
14544                 tag: 'button',
14545                 html : 'x',
14546                 cls : 'roo-combo-removable-btn close'
14547             });
14548         }
14549
14550         if(this.hasFeedback && !this.allowBlank){
14551             
14552             inputblock.cls += ' has-feedback';
14553             
14554             inputblock.cn.push({
14555                 tag: 'span',
14556                 cls: 'glyphicon form-control-feedback'
14557             });
14558             
14559         }
14560         
14561         if (this.after) {
14562             
14563             inputblock.cls += (this.before) ? '' : ' input-group';
14564             
14565             inputblock.cn.push({
14566                 tag :'span',
14567                 cls : 'input-group-addon',
14568                 html : this.after
14569             });
14570         }
14571
14572         var box = {
14573             tag: 'div',
14574             cn: [
14575                 {
14576                     tag: 'input',
14577                     type : 'hidden',
14578                     cls: 'form-hidden-field'
14579                 },
14580                 inputblock
14581             ]
14582             
14583         };
14584         
14585         if(this.multiple){
14586             box = {
14587                 tag: 'div',
14588                 cn: [
14589                     {
14590                         tag: 'input',
14591                         type : 'hidden',
14592                         cls: 'form-hidden-field'
14593                     },
14594                     {
14595                         tag: 'ul',
14596                         cls: 'roo-select2-choices',
14597                         cn:[
14598                             {
14599                                 tag: 'li',
14600                                 cls: 'roo-select2-search-field',
14601                                 cn: [
14602
14603                                     inputblock
14604                                 ]
14605                             }
14606                         ]
14607                     }
14608                 ]
14609             }
14610         };
14611         
14612         var combobox = {
14613             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14614             cn: [
14615                 box
14616             ]
14617         };
14618         
14619         if(!this.multiple && this.showToggleBtn){
14620             
14621             var caret = {
14622                         tag: 'span',
14623                         cls: 'caret'
14624             };
14625             
14626             if (this.caret != false) {
14627                 caret = {
14628                      tag: 'i',
14629                      cls: 'fa fa-' + this.caret
14630                 };
14631                 
14632             }
14633             
14634             combobox.cn.push({
14635                 tag :'span',
14636                 cls : 'input-group-addon btn dropdown-toggle',
14637                 cn : [
14638                     caret,
14639                     {
14640                         tag: 'span',
14641                         cls: 'combobox-clear',
14642                         cn  : [
14643                             {
14644                                 tag : 'i',
14645                                 cls: 'icon-remove'
14646                             }
14647                         ]
14648                     }
14649                 ]
14650
14651             })
14652         }
14653         
14654         if(this.multiple){
14655             combobox.cls += ' roo-select2-container-multi';
14656         }
14657         
14658         var align = this.labelAlign || this.parentLabelAlign();
14659         
14660         if (align ==='left' && this.fieldLabel.length) {
14661
14662             cfg.cn = [
14663                 {
14664                    tag : 'i',
14665                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14666                    tooltip : 'This field is required'
14667                 },
14668                 {
14669                     tag: 'label',
14670                     cls : 'control-label',
14671                     html : this.fieldLabel
14672
14673                 },
14674                 {
14675                     cls : '', 
14676                     cn: [
14677                         combobox
14678                     ]
14679                 }
14680             ];
14681             
14682             var labelCfg = cfg.cn[1];
14683             var contentCfg = cfg.cn[2];
14684             
14685
14686             if(this.indicatorpos == 'right'){
14687                 cfg.cn = [
14688                     {
14689                         tag: 'label',
14690                         cls : 'control-label',
14691                         html : this.fieldLabel,
14692                         cn : [
14693                             {
14694                                tag : 'i',
14695                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14696                                tooltip : 'This field is required'
14697                             }
14698                         ]
14699                     },
14700                     {
14701                         cls : '', 
14702                         cn: [
14703                             combobox
14704                         ]
14705                     }
14706                 ];
14707             }
14708             
14709             labelCfg = cfg.cn[0];
14710             contentCfg = cfg.cn[2];
14711             
14712             if(this.labelWidth > 12){
14713                 labelCfg.style = "width: " + this.labelWidth + 'px';
14714             }
14715             
14716             if(this.labelWidth < 13 && this.labelmd == 0){
14717                 this.labelmd = this.labelWidth;
14718             }
14719             
14720             if(this.labellg > 0){
14721                 labelCfg.cls += ' col-lg-' + this.labellg;
14722                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14723             }
14724             
14725             if(this.labelmd > 0){
14726                 labelCfg.cls += ' col-md-' + this.labelmd;
14727                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14728             }
14729             
14730             if(this.labelsm > 0){
14731                 labelCfg.cls += ' col-sm-' + this.labelsm;
14732                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14733             }
14734             
14735             if(this.labelxs > 0){
14736                 labelCfg.cls += ' col-xs-' + this.labelxs;
14737                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14738             }
14739                 
14740                 
14741         } else if ( this.fieldLabel.length) {
14742             cfg.cn = [
14743                 {
14744                    tag : 'i',
14745                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14746                    tooltip : 'This field is required'
14747                 },
14748                 {
14749                     tag: 'label',
14750                     cls : 'control-label',
14751                     html : this.fieldLabel
14752
14753                 },
14754                 {
14755                     cls : '', 
14756                     cn: [
14757                         combobox
14758                     ]
14759                 }
14760             ];
14761             
14762             if(this.indicatorpos == 'right'){
14763                 cfg.cn = [
14764                     {
14765                         tag: 'label',
14766                         cls : 'control-label',
14767                         html : this.fieldLabel,
14768                         cn : [
14769                             {
14770                                tag : 'i',
14771                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14772                                tooltip : 'This field is required'
14773                             }
14774                         ]
14775                     },
14776                     {
14777                         cls : '', 
14778                         cn: [
14779                             combobox
14780                         ]
14781                     }
14782                 ];
14783             }
14784         } else {
14785             cfg.cn = combobox;    
14786         }
14787         
14788         
14789         var settings = this;
14790         
14791         ['xs','sm','md','lg'].map(function(size){
14792             if (settings[size]) {
14793                 cfg.cls += ' col-' + size + '-' + settings[size];
14794             }
14795         });
14796         
14797         return cfg;
14798     },
14799     
14800     initTouchView : function()
14801     {
14802         this.renderTouchView();
14803         
14804         this.touchViewEl.on('scroll', function(){
14805             this.el.dom.scrollTop = 0;
14806         }, this);
14807         
14808         this.originalValue = this.getValue();
14809         
14810         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14811         
14812         this.inputEl().on("click", this.showTouchView, this);
14813         if (this.triggerEl) {
14814             this.triggerEl.on("click", this.showTouchView, this);
14815         }
14816         
14817         
14818         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14819         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14820         
14821         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14822         
14823         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14824         this.store.on('load', this.onTouchViewLoad, this);
14825         this.store.on('loadexception', this.onTouchViewLoadException, this);
14826         
14827         if(this.hiddenName){
14828             
14829             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14830             
14831             this.hiddenField.dom.value =
14832                 this.hiddenValue !== undefined ? this.hiddenValue :
14833                 this.value !== undefined ? this.value : '';
14834         
14835             this.el.dom.removeAttribute('name');
14836             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14837         }
14838         
14839         if(this.multiple){
14840             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14841             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14842         }
14843         
14844         if(this.removable && !this.multiple){
14845             var close = this.closeTriggerEl();
14846             if(close){
14847                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14848                 close.on('click', this.removeBtnClick, this, close);
14849             }
14850         }
14851         /*
14852          * fix the bug in Safari iOS8
14853          */
14854         this.inputEl().on("focus", function(e){
14855             document.activeElement.blur();
14856         }, this);
14857         
14858         return;
14859         
14860         
14861     },
14862     
14863     renderTouchView : function()
14864     {
14865         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14866         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14867         
14868         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14869         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14870         
14871         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14872         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14873         this.touchViewBodyEl.setStyle('overflow', 'auto');
14874         
14875         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14876         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14877         
14878         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14879         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14880         
14881     },
14882     
14883     showTouchView : function()
14884     {
14885         if(this.disabled){
14886             return;
14887         }
14888         
14889         this.touchViewHeaderEl.hide();
14890
14891         if(this.modalTitle.length){
14892             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14893             this.touchViewHeaderEl.show();
14894         }
14895
14896         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
14897         this.touchViewEl.show();
14898
14899         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14900         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14901                 Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14902
14903         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14904
14905         if(this.modalTitle.length){
14906             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14907         }
14908         
14909         this.touchViewBodyEl.setHeight(bodyHeight);
14910
14911         if(this.animate){
14912             var _this = this;
14913             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14914         }else{
14915             this.touchViewEl.addClass('in');
14916         }
14917
14918         this.doTouchViewQuery();
14919         
14920     },
14921     
14922     hideTouchView : function()
14923     {
14924         this.touchViewEl.removeClass('in');
14925
14926         if(this.animate){
14927             var _this = this;
14928             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14929         }else{
14930             this.touchViewEl.setStyle('display', 'none');
14931         }
14932         
14933     },
14934     
14935     setTouchViewValue : function()
14936     {
14937         if(this.multiple){
14938             this.clearItem();
14939         
14940             var _this = this;
14941
14942             Roo.each(this.tickItems, function(o){
14943                 this.addItem(o);
14944             }, this);
14945         }
14946         
14947         this.hideTouchView();
14948     },
14949     
14950     doTouchViewQuery : function()
14951     {
14952         var qe = {
14953             query: '',
14954             forceAll: true,
14955             combo: this,
14956             cancel:false
14957         };
14958         
14959         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14960             return false;
14961         }
14962         
14963         if(!this.alwaysQuery || this.mode == 'local'){
14964             this.onTouchViewLoad();
14965             return;
14966         }
14967         
14968         this.store.load();
14969     },
14970     
14971     onTouchViewBeforeLoad : function(combo,opts)
14972     {
14973         return;
14974     },
14975
14976     // private
14977     onTouchViewLoad : function()
14978     {
14979         if(this.store.getCount() < 1){
14980             this.onTouchViewEmptyResults();
14981             return;
14982         }
14983         
14984         this.clearTouchView();
14985         
14986         var rawValue = this.getRawValue();
14987         
14988         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14989         
14990         this.tickItems = [];
14991         
14992         this.store.data.each(function(d, rowIndex){
14993             var row = this.touchViewListGroup.createChild(template);
14994             
14995             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14996                 row.addClass(d.data.cls);
14997             }
14998             
14999             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15000                 var cfg = {
15001                     data : d.data,
15002                     html : d.data[this.displayField]
15003                 };
15004                 
15005                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15006                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15007                 }
15008             }
15009             row.removeClass('selected');
15010             if(!this.multiple && this.valueField &&
15011                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15012             {
15013                 // radio buttons..
15014                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15015                 row.addClass('selected');
15016             }
15017             
15018             if(this.multiple && this.valueField &&
15019                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15020             {
15021                 
15022                 // checkboxes...
15023                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15024                 this.tickItems.push(d.data);
15025             }
15026             
15027             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15028             
15029         }, this);
15030         
15031         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15032         
15033         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15034
15035         if(this.modalTitle.length){
15036             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15037         }
15038
15039         var listHeight = this.touchViewListGroup.getHeight();
15040         
15041         var _this = this;
15042         
15043         if(firstChecked && listHeight > bodyHeight){
15044             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15045         }
15046         
15047     },
15048     
15049     onTouchViewLoadException : function()
15050     {
15051         this.hideTouchView();
15052     },
15053     
15054     onTouchViewEmptyResults : function()
15055     {
15056         this.clearTouchView();
15057         
15058         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15059         
15060         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15061         
15062     },
15063     
15064     clearTouchView : function()
15065     {
15066         this.touchViewListGroup.dom.innerHTML = '';
15067     },
15068     
15069     onTouchViewClick : function(e, el, o)
15070     {
15071         e.preventDefault();
15072         
15073         var row = o.row;
15074         var rowIndex = o.rowIndex;
15075         
15076         var r = this.store.getAt(rowIndex);
15077         
15078         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15079             
15080             if(!this.multiple){
15081                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15082                     c.dom.removeAttribute('checked');
15083                 }, this);
15084
15085                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15086
15087                 this.setFromData(r.data);
15088
15089                 var close = this.closeTriggerEl();
15090
15091                 if(close){
15092                     close.show();
15093                 }
15094
15095                 this.hideTouchView();
15096
15097                 this.fireEvent('select', this, r, rowIndex);
15098
15099                 return;
15100             }
15101
15102             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15103                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15104                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15105                 return;
15106             }
15107
15108             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15109             this.addItem(r.data);
15110             this.tickItems.push(r.data);
15111         }
15112     },
15113     
15114     getAutoCreateNativeIOS : function()
15115     {
15116         var cfg = {
15117             cls: 'form-group' //input-group,
15118         };
15119         
15120         var combobox =  {
15121             tag: 'select',
15122             cls : 'roo-ios-select'
15123         };
15124         
15125         if (this.name) {
15126             combobox.name = this.name;
15127         }
15128         
15129         if (this.disabled) {
15130             combobox.disabled = true;
15131         }
15132         
15133         var settings = this;
15134         
15135         ['xs','sm','md','lg'].map(function(size){
15136             if (settings[size]) {
15137                 cfg.cls += ' col-' + size + '-' + settings[size];
15138             }
15139         });
15140         
15141         cfg.cn = combobox;
15142         
15143         return cfg;
15144         
15145     },
15146     
15147     initIOSView : function()
15148     {
15149         this.store.on('load', this.onIOSViewLoad, this);
15150         
15151         return;
15152     },
15153     
15154     onIOSViewLoad : function()
15155     {
15156         if(this.store.getCount() < 1){
15157             return;
15158         }
15159         
15160         this.clearIOSView();
15161         
15162         if(this.allowBlank) {
15163             
15164             var default_text = '-- SELECT --';
15165             
15166             var opt = this.inputEl().createChild({
15167                 tag: 'option',
15168                 value : 0,
15169                 html : default_text
15170             });
15171             
15172             var o = {};
15173             o[this.valueField] = 0;
15174             o[this.displayField] = default_text;
15175             
15176             this.ios_options.push({
15177                 data : o,
15178                 el : opt
15179             });
15180             
15181         }
15182         
15183         this.store.data.each(function(d, rowIndex){
15184             
15185             var html = '';
15186             
15187             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15188                 html = d.data[this.displayField];
15189             }
15190             
15191             var value = '';
15192             
15193             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15194                 value = d.data[this.valueField];
15195             }
15196             
15197             var option = {
15198                 tag: 'option',
15199                 value : value,
15200                 html : html
15201             };
15202             
15203             if(this.value == d.data[this.valueField]){
15204                 option['selected'] = true;
15205             }
15206             
15207             var opt = this.inputEl().createChild(option);
15208             
15209             this.ios_options.push({
15210                 data : d.data,
15211                 el : opt
15212             });
15213             
15214         }, this);
15215         
15216         this.inputEl().on('change', function(){
15217            this.fireEvent('select', this);
15218         }, this);
15219         
15220     },
15221     
15222     clearIOSView: function()
15223     {
15224         this.inputEl().dom.innerHTML = '';
15225         
15226         this.ios_options = [];
15227     },
15228     
15229     setIOSValue: function(v)
15230     {
15231         this.value = v;
15232         
15233         if(!this.ios_options){
15234             return;
15235         }
15236         
15237         Roo.each(this.ios_options, function(opts){
15238            
15239            opts.el.dom.removeAttribute('selected');
15240            
15241            if(opts.data[this.valueField] != v){
15242                return;
15243            }
15244            
15245            opts.el.dom.setAttribute('selected', true);
15246            
15247         }, this);
15248     }
15249
15250     /** 
15251     * @cfg {Boolean} grow 
15252     * @hide 
15253     */
15254     /** 
15255     * @cfg {Number} growMin 
15256     * @hide 
15257     */
15258     /** 
15259     * @cfg {Number} growMax 
15260     * @hide 
15261     */
15262     /**
15263      * @hide
15264      * @method autoSize
15265      */
15266 });
15267
15268 Roo.apply(Roo.bootstrap.ComboBox,  {
15269     
15270     header : {
15271         tag: 'div',
15272         cls: 'modal-header',
15273         cn: [
15274             {
15275                 tag: 'h4',
15276                 cls: 'modal-title'
15277             }
15278         ]
15279     },
15280     
15281     body : {
15282         tag: 'div',
15283         cls: 'modal-body',
15284         cn: [
15285             {
15286                 tag: 'ul',
15287                 cls: 'list-group'
15288             }
15289         ]
15290     },
15291     
15292     listItemRadio : {
15293         tag: 'li',
15294         cls: 'list-group-item',
15295         cn: [
15296             {
15297                 tag: 'span',
15298                 cls: 'roo-combobox-list-group-item-value'
15299             },
15300             {
15301                 tag: 'div',
15302                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15303                 cn: [
15304                     {
15305                         tag: 'input',
15306                         type: 'radio'
15307                     },
15308                     {
15309                         tag: 'label'
15310                     }
15311                 ]
15312             }
15313         ]
15314     },
15315     
15316     listItemCheckbox : {
15317         tag: 'li',
15318         cls: 'list-group-item',
15319         cn: [
15320             {
15321                 tag: 'span',
15322                 cls: 'roo-combobox-list-group-item-value'
15323             },
15324             {
15325                 tag: 'div',
15326                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15327                 cn: [
15328                     {
15329                         tag: 'input',
15330                         type: 'checkbox'
15331                     },
15332                     {
15333                         tag: 'label'
15334                     }
15335                 ]
15336             }
15337         ]
15338     },
15339     
15340     emptyResult : {
15341         tag: 'div',
15342         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15343     },
15344     
15345     footer : {
15346         tag: 'div',
15347         cls: 'modal-footer',
15348         cn: [
15349             {
15350                 tag: 'div',
15351                 cls: 'row',
15352                 cn: [
15353                     {
15354                         tag: 'div',
15355                         cls: 'col-xs-6 text-left',
15356                         cn: {
15357                             tag: 'button',
15358                             cls: 'btn btn-danger roo-touch-view-cancel',
15359                             html: 'Cancel'
15360                         }
15361                     },
15362                     {
15363                         tag: 'div',
15364                         cls: 'col-xs-6 text-right',
15365                         cn: {
15366                             tag: 'button',
15367                             cls: 'btn btn-success roo-touch-view-ok',
15368                             html: 'OK'
15369                         }
15370                     }
15371                 ]
15372             }
15373         ]
15374         
15375     }
15376 });
15377
15378 Roo.apply(Roo.bootstrap.ComboBox,  {
15379     
15380     touchViewTemplate : {
15381         tag: 'div',
15382         cls: 'modal fade roo-combobox-touch-view',
15383         cn: [
15384             {
15385                 tag: 'div',
15386                 cls: 'modal-dialog',
15387                 style : 'position:fixed', // we have to fix position....
15388                 cn: [
15389                     {
15390                         tag: 'div',
15391                         cls: 'modal-content',
15392                         cn: [
15393                             Roo.bootstrap.ComboBox.header,
15394                             Roo.bootstrap.ComboBox.body,
15395                             Roo.bootstrap.ComboBox.footer
15396                         ]
15397                     }
15398                 ]
15399             }
15400         ]
15401     }
15402 });/*
15403  * Based on:
15404  * Ext JS Library 1.1.1
15405  * Copyright(c) 2006-2007, Ext JS, LLC.
15406  *
15407  * Originally Released Under LGPL - original licence link has changed is not relivant.
15408  *
15409  * Fork - LGPL
15410  * <script type="text/javascript">
15411  */
15412
15413 /**
15414  * @class Roo.View
15415  * @extends Roo.util.Observable
15416  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15417  * This class also supports single and multi selection modes. <br>
15418  * Create a data model bound view:
15419  <pre><code>
15420  var store = new Roo.data.Store(...);
15421
15422  var view = new Roo.View({
15423     el : "my-element",
15424     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15425  
15426     singleSelect: true,
15427     selectedClass: "ydataview-selected",
15428     store: store
15429  });
15430
15431  // listen for node click?
15432  view.on("click", function(vw, index, node, e){
15433  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15434  });
15435
15436  // load XML data
15437  dataModel.load("foobar.xml");
15438  </code></pre>
15439  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15440  * <br><br>
15441  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15442  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15443  * 
15444  * Note: old style constructor is still suported (container, template, config)
15445  * 
15446  * @constructor
15447  * Create a new View
15448  * @param {Object} config The config object
15449  * 
15450  */
15451 Roo.View = function(config, depreciated_tpl, depreciated_config){
15452     
15453     this.parent = false;
15454     
15455     if (typeof(depreciated_tpl) == 'undefined') {
15456         // new way.. - universal constructor.
15457         Roo.apply(this, config);
15458         this.el  = Roo.get(this.el);
15459     } else {
15460         // old format..
15461         this.el  = Roo.get(config);
15462         this.tpl = depreciated_tpl;
15463         Roo.apply(this, depreciated_config);
15464     }
15465     this.wrapEl  = this.el.wrap().wrap();
15466     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15467     
15468     
15469     if(typeof(this.tpl) == "string"){
15470         this.tpl = new Roo.Template(this.tpl);
15471     } else {
15472         // support xtype ctors..
15473         this.tpl = new Roo.factory(this.tpl, Roo);
15474     }
15475     
15476     
15477     this.tpl.compile();
15478     
15479     /** @private */
15480     this.addEvents({
15481         /**
15482          * @event beforeclick
15483          * Fires before a click is processed. Returns false to cancel the default action.
15484          * @param {Roo.View} this
15485          * @param {Number} index The index of the target node
15486          * @param {HTMLElement} node The target node
15487          * @param {Roo.EventObject} e The raw event object
15488          */
15489             "beforeclick" : true,
15490         /**
15491          * @event click
15492          * Fires when a template node is clicked.
15493          * @param {Roo.View} this
15494          * @param {Number} index The index of the target node
15495          * @param {HTMLElement} node The target node
15496          * @param {Roo.EventObject} e The raw event object
15497          */
15498             "click" : true,
15499         /**
15500          * @event dblclick
15501          * Fires when a template node is double clicked.
15502          * @param {Roo.View} this
15503          * @param {Number} index The index of the target node
15504          * @param {HTMLElement} node The target node
15505          * @param {Roo.EventObject} e The raw event object
15506          */
15507             "dblclick" : true,
15508         /**
15509          * @event contextmenu
15510          * Fires when a template node is right clicked.
15511          * @param {Roo.View} this
15512          * @param {Number} index The index of the target node
15513          * @param {HTMLElement} node The target node
15514          * @param {Roo.EventObject} e The raw event object
15515          */
15516             "contextmenu" : true,
15517         /**
15518          * @event selectionchange
15519          * Fires when the selected nodes change.
15520          * @param {Roo.View} this
15521          * @param {Array} selections Array of the selected nodes
15522          */
15523             "selectionchange" : true,
15524     
15525         /**
15526          * @event beforeselect
15527          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15528          * @param {Roo.View} this
15529          * @param {HTMLElement} node The node to be selected
15530          * @param {Array} selections Array of currently selected nodes
15531          */
15532             "beforeselect" : true,
15533         /**
15534          * @event preparedata
15535          * Fires on every row to render, to allow you to change the data.
15536          * @param {Roo.View} this
15537          * @param {Object} data to be rendered (change this)
15538          */
15539           "preparedata" : true
15540           
15541           
15542         });
15543
15544
15545
15546     this.el.on({
15547         "click": this.onClick,
15548         "dblclick": this.onDblClick,
15549         "contextmenu": this.onContextMenu,
15550         scope:this
15551     });
15552
15553     this.selections = [];
15554     this.nodes = [];
15555     this.cmp = new Roo.CompositeElementLite([]);
15556     if(this.store){
15557         this.store = Roo.factory(this.store, Roo.data);
15558         this.setStore(this.store, true);
15559     }
15560     
15561     if ( this.footer && this.footer.xtype) {
15562            
15563          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15564         
15565         this.footer.dataSource = this.store;
15566         this.footer.container = fctr;
15567         this.footer = Roo.factory(this.footer, Roo);
15568         fctr.insertFirst(this.el);
15569         
15570         // this is a bit insane - as the paging toolbar seems to detach the el..
15571 //        dom.parentNode.parentNode.parentNode
15572          // they get detached?
15573     }
15574     
15575     
15576     Roo.View.superclass.constructor.call(this);
15577     
15578     
15579 };
15580
15581 Roo.extend(Roo.View, Roo.util.Observable, {
15582     
15583      /**
15584      * @cfg {Roo.data.Store} store Data store to load data from.
15585      */
15586     store : false,
15587     
15588     /**
15589      * @cfg {String|Roo.Element} el The container element.
15590      */
15591     el : '',
15592     
15593     /**
15594      * @cfg {String|Roo.Template} tpl The template used by this View 
15595      */
15596     tpl : false,
15597     /**
15598      * @cfg {String} dataName the named area of the template to use as the data area
15599      *                          Works with domtemplates roo-name="name"
15600      */
15601     dataName: false,
15602     /**
15603      * @cfg {String} selectedClass The css class to add to selected nodes
15604      */
15605     selectedClass : "x-view-selected",
15606      /**
15607      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15608      */
15609     emptyText : "",
15610     
15611     /**
15612      * @cfg {String} text to display on mask (default Loading)
15613      */
15614     mask : false,
15615     /**
15616      * @cfg {Boolean} multiSelect Allow multiple selection
15617      */
15618     multiSelect : false,
15619     /**
15620      * @cfg {Boolean} singleSelect Allow single selection
15621      */
15622     singleSelect:  false,
15623     
15624     /**
15625      * @cfg {Boolean} toggleSelect - selecting 
15626      */
15627     toggleSelect : false,
15628     
15629     /**
15630      * @cfg {Boolean} tickable - selecting 
15631      */
15632     tickable : false,
15633     
15634     /**
15635      * Returns the element this view is bound to.
15636      * @return {Roo.Element}
15637      */
15638     getEl : function(){
15639         return this.wrapEl;
15640     },
15641     
15642     
15643
15644     /**
15645      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15646      */
15647     refresh : function(){
15648         //Roo.log('refresh');
15649         var t = this.tpl;
15650         
15651         // if we are using something like 'domtemplate', then
15652         // the what gets used is:
15653         // t.applySubtemplate(NAME, data, wrapping data..)
15654         // the outer template then get' applied with
15655         //     the store 'extra data'
15656         // and the body get's added to the
15657         //      roo-name="data" node?
15658         //      <span class='roo-tpl-{name}'></span> ?????
15659         
15660         
15661         
15662         this.clearSelections();
15663         this.el.update("");
15664         var html = [];
15665         var records = this.store.getRange();
15666         if(records.length < 1) {
15667             
15668             // is this valid??  = should it render a template??
15669             
15670             this.el.update(this.emptyText);
15671             return;
15672         }
15673         var el = this.el;
15674         if (this.dataName) {
15675             this.el.update(t.apply(this.store.meta)); //????
15676             el = this.el.child('.roo-tpl-' + this.dataName);
15677         }
15678         
15679         for(var i = 0, len = records.length; i < len; i++){
15680             var data = this.prepareData(records[i].data, i, records[i]);
15681             this.fireEvent("preparedata", this, data, i, records[i]);
15682             
15683             var d = Roo.apply({}, data);
15684             
15685             if(this.tickable){
15686                 Roo.apply(d, {'roo-id' : Roo.id()});
15687                 
15688                 var _this = this;
15689             
15690                 Roo.each(this.parent.item, function(item){
15691                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15692                         return;
15693                     }
15694                     Roo.apply(d, {'roo-data-checked' : 'checked'});
15695                 });
15696             }
15697             
15698             html[html.length] = Roo.util.Format.trim(
15699                 this.dataName ?
15700                     t.applySubtemplate(this.dataName, d, this.store.meta) :
15701                     t.apply(d)
15702             );
15703         }
15704         
15705         
15706         
15707         el.update(html.join(""));
15708         this.nodes = el.dom.childNodes;
15709         this.updateIndexes(0);
15710     },
15711     
15712
15713     /**
15714      * Function to override to reformat the data that is sent to
15715      * the template for each node.
15716      * DEPRICATED - use the preparedata event handler.
15717      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15718      * a JSON object for an UpdateManager bound view).
15719      */
15720     prepareData : function(data, index, record)
15721     {
15722         this.fireEvent("preparedata", this, data, index, record);
15723         return data;
15724     },
15725
15726     onUpdate : function(ds, record){
15727         // Roo.log('on update');   
15728         this.clearSelections();
15729         var index = this.store.indexOf(record);
15730         var n = this.nodes[index];
15731         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15732         n.parentNode.removeChild(n);
15733         this.updateIndexes(index, index);
15734     },
15735
15736     
15737     
15738 // --------- FIXME     
15739     onAdd : function(ds, records, index)
15740     {
15741         //Roo.log(['on Add', ds, records, index] );        
15742         this.clearSelections();
15743         if(this.nodes.length == 0){
15744             this.refresh();
15745             return;
15746         }
15747         var n = this.nodes[index];
15748         for(var i = 0, len = records.length; i < len; i++){
15749             var d = this.prepareData(records[i].data, i, records[i]);
15750             if(n){
15751                 this.tpl.insertBefore(n, d);
15752             }else{
15753                 
15754                 this.tpl.append(this.el, d);
15755             }
15756         }
15757         this.updateIndexes(index);
15758     },
15759
15760     onRemove : function(ds, record, index){
15761        // Roo.log('onRemove');
15762         this.clearSelections();
15763         var el = this.dataName  ?
15764             this.el.child('.roo-tpl-' + this.dataName) :
15765             this.el; 
15766         
15767         el.dom.removeChild(this.nodes[index]);
15768         this.updateIndexes(index);
15769     },
15770
15771     /**
15772      * Refresh an individual node.
15773      * @param {Number} index
15774      */
15775     refreshNode : function(index){
15776         this.onUpdate(this.store, this.store.getAt(index));
15777     },
15778
15779     updateIndexes : function(startIndex, endIndex){
15780         var ns = this.nodes;
15781         startIndex = startIndex || 0;
15782         endIndex = endIndex || ns.length - 1;
15783         for(var i = startIndex; i <= endIndex; i++){
15784             ns[i].nodeIndex = i;
15785         }
15786     },
15787
15788     /**
15789      * Changes the data store this view uses and refresh the view.
15790      * @param {Store} store
15791      */
15792     setStore : function(store, initial){
15793         if(!initial && this.store){
15794             this.store.un("datachanged", this.refresh);
15795             this.store.un("add", this.onAdd);
15796             this.store.un("remove", this.onRemove);
15797             this.store.un("update", this.onUpdate);
15798             this.store.un("clear", this.refresh);
15799             this.store.un("beforeload", this.onBeforeLoad);
15800             this.store.un("load", this.onLoad);
15801             this.store.un("loadexception", this.onLoad);
15802         }
15803         if(store){
15804           
15805             store.on("datachanged", this.refresh, this);
15806             store.on("add", this.onAdd, this);
15807             store.on("remove", this.onRemove, this);
15808             store.on("update", this.onUpdate, this);
15809             store.on("clear", this.refresh, this);
15810             store.on("beforeload", this.onBeforeLoad, this);
15811             store.on("load", this.onLoad, this);
15812             store.on("loadexception", this.onLoad, this);
15813         }
15814         
15815         if(store){
15816             this.refresh();
15817         }
15818     },
15819     /**
15820      * onbeforeLoad - masks the loading area.
15821      *
15822      */
15823     onBeforeLoad : function(store,opts)
15824     {
15825          //Roo.log('onBeforeLoad');   
15826         if (!opts.add) {
15827             this.el.update("");
15828         }
15829         this.el.mask(this.mask ? this.mask : "Loading" ); 
15830     },
15831     onLoad : function ()
15832     {
15833         this.el.unmask();
15834     },
15835     
15836
15837     /**
15838      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15839      * @param {HTMLElement} node
15840      * @return {HTMLElement} The template node
15841      */
15842     findItemFromChild : function(node){
15843         var el = this.dataName  ?
15844             this.el.child('.roo-tpl-' + this.dataName,true) :
15845             this.el.dom; 
15846         
15847         if(!node || node.parentNode == el){
15848                     return node;
15849             }
15850             var p = node.parentNode;
15851             while(p && p != el){
15852             if(p.parentNode == el){
15853                 return p;
15854             }
15855             p = p.parentNode;
15856         }
15857             return null;
15858     },
15859
15860     /** @ignore */
15861     onClick : function(e){
15862         var item = this.findItemFromChild(e.getTarget());
15863         if(item){
15864             var index = this.indexOf(item);
15865             if(this.onItemClick(item, index, e) !== false){
15866                 this.fireEvent("click", this, index, item, e);
15867             }
15868         }else{
15869             this.clearSelections();
15870         }
15871     },
15872
15873     /** @ignore */
15874     onContextMenu : function(e){
15875         var item = this.findItemFromChild(e.getTarget());
15876         if(item){
15877             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15878         }
15879     },
15880
15881     /** @ignore */
15882     onDblClick : function(e){
15883         var item = this.findItemFromChild(e.getTarget());
15884         if(item){
15885             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15886         }
15887     },
15888
15889     onItemClick : function(item, index, e)
15890     {
15891         if(this.fireEvent("beforeclick", this, index, item, e) === false){
15892             return false;
15893         }
15894         if (this.toggleSelect) {
15895             var m = this.isSelected(item) ? 'unselect' : 'select';
15896             //Roo.log(m);
15897             var _t = this;
15898             _t[m](item, true, false);
15899             return true;
15900         }
15901         if(this.multiSelect || this.singleSelect){
15902             if(this.multiSelect && e.shiftKey && this.lastSelection){
15903                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15904             }else{
15905                 this.select(item, this.multiSelect && e.ctrlKey);
15906                 this.lastSelection = item;
15907             }
15908             
15909             if(!this.tickable){
15910                 e.preventDefault();
15911             }
15912             
15913         }
15914         return true;
15915     },
15916
15917     /**
15918      * Get the number of selected nodes.
15919      * @return {Number}
15920      */
15921     getSelectionCount : function(){
15922         return this.selections.length;
15923     },
15924
15925     /**
15926      * Get the currently selected nodes.
15927      * @return {Array} An array of HTMLElements
15928      */
15929     getSelectedNodes : function(){
15930         return this.selections;
15931     },
15932
15933     /**
15934      * Get the indexes of the selected nodes.
15935      * @return {Array}
15936      */
15937     getSelectedIndexes : function(){
15938         var indexes = [], s = this.selections;
15939         for(var i = 0, len = s.length; i < len; i++){
15940             indexes.push(s[i].nodeIndex);
15941         }
15942         return indexes;
15943     },
15944
15945     /**
15946      * Clear all selections
15947      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
15948      */
15949     clearSelections : function(suppressEvent){
15950         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
15951             this.cmp.elements = this.selections;
15952             this.cmp.removeClass(this.selectedClass);
15953             this.selections = [];
15954             if(!suppressEvent){
15955                 this.fireEvent("selectionchange", this, this.selections);
15956             }
15957         }
15958     },
15959
15960     /**
15961      * Returns true if the passed node is selected
15962      * @param {HTMLElement/Number} node The node or node index
15963      * @return {Boolean}
15964      */
15965     isSelected : function(node){
15966         var s = this.selections;
15967         if(s.length < 1){
15968             return false;
15969         }
15970         node = this.getNode(node);
15971         return s.indexOf(node) !== -1;
15972     },
15973
15974     /**
15975      * Selects nodes.
15976      * @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
15977      * @param {Boolean} keepExisting (optional) true to keep existing selections
15978      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15979      */
15980     select : function(nodeInfo, keepExisting, suppressEvent){
15981         if(nodeInfo instanceof Array){
15982             if(!keepExisting){
15983                 this.clearSelections(true);
15984             }
15985             for(var i = 0, len = nodeInfo.length; i < len; i++){
15986                 this.select(nodeInfo[i], true, true);
15987             }
15988             return;
15989         } 
15990         var node = this.getNode(nodeInfo);
15991         if(!node || this.isSelected(node)){
15992             return; // already selected.
15993         }
15994         if(!keepExisting){
15995             this.clearSelections(true);
15996         }
15997         
15998         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
15999             Roo.fly(node).addClass(this.selectedClass);
16000             this.selections.push(node);
16001             if(!suppressEvent){
16002                 this.fireEvent("selectionchange", this, this.selections);
16003             }
16004         }
16005         
16006         
16007     },
16008       /**
16009      * Unselects nodes.
16010      * @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
16011      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16012      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16013      */
16014     unselect : function(nodeInfo, keepExisting, suppressEvent)
16015     {
16016         if(nodeInfo instanceof Array){
16017             Roo.each(this.selections, function(s) {
16018                 this.unselect(s, nodeInfo);
16019             }, this);
16020             return;
16021         }
16022         var node = this.getNode(nodeInfo);
16023         if(!node || !this.isSelected(node)){
16024             //Roo.log("not selected");
16025             return; // not selected.
16026         }
16027         // fireevent???
16028         var ns = [];
16029         Roo.each(this.selections, function(s) {
16030             if (s == node ) {
16031                 Roo.fly(node).removeClass(this.selectedClass);
16032
16033                 return;
16034             }
16035             ns.push(s);
16036         },this);
16037         
16038         this.selections= ns;
16039         this.fireEvent("selectionchange", this, this.selections);
16040     },
16041
16042     /**
16043      * Gets a template node.
16044      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16045      * @return {HTMLElement} The node or null if it wasn't found
16046      */
16047     getNode : function(nodeInfo){
16048         if(typeof nodeInfo == "string"){
16049             return document.getElementById(nodeInfo);
16050         }else if(typeof nodeInfo == "number"){
16051             return this.nodes[nodeInfo];
16052         }
16053         return nodeInfo;
16054     },
16055
16056     /**
16057      * Gets a range template nodes.
16058      * @param {Number} startIndex
16059      * @param {Number} endIndex
16060      * @return {Array} An array of nodes
16061      */
16062     getNodes : function(start, end){
16063         var ns = this.nodes;
16064         start = start || 0;
16065         end = typeof end == "undefined" ? ns.length - 1 : end;
16066         var nodes = [];
16067         if(start <= end){
16068             for(var i = start; i <= end; i++){
16069                 nodes.push(ns[i]);
16070             }
16071         } else{
16072             for(var i = start; i >= end; i--){
16073                 nodes.push(ns[i]);
16074             }
16075         }
16076         return nodes;
16077     },
16078
16079     /**
16080      * Finds the index of the passed node
16081      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16082      * @return {Number} The index of the node or -1
16083      */
16084     indexOf : function(node){
16085         node = this.getNode(node);
16086         if(typeof node.nodeIndex == "number"){
16087             return node.nodeIndex;
16088         }
16089         var ns = this.nodes;
16090         for(var i = 0, len = ns.length; i < len; i++){
16091             if(ns[i] == node){
16092                 return i;
16093             }
16094         }
16095         return -1;
16096     }
16097 });
16098 /*
16099  * - LGPL
16100  *
16101  * based on jquery fullcalendar
16102  * 
16103  */
16104
16105 Roo.bootstrap = Roo.bootstrap || {};
16106 /**
16107  * @class Roo.bootstrap.Calendar
16108  * @extends Roo.bootstrap.Component
16109  * Bootstrap Calendar class
16110  * @cfg {Boolean} loadMask (true|false) default false
16111  * @cfg {Object} header generate the user specific header of the calendar, default false
16112
16113  * @constructor
16114  * Create a new Container
16115  * @param {Object} config The config object
16116  */
16117
16118
16119
16120 Roo.bootstrap.Calendar = function(config){
16121     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16122      this.addEvents({
16123         /**
16124              * @event select
16125              * Fires when a date is selected
16126              * @param {DatePicker} this
16127              * @param {Date} date The selected date
16128              */
16129         'select': true,
16130         /**
16131              * @event monthchange
16132              * Fires when the displayed month changes 
16133              * @param {DatePicker} this
16134              * @param {Date} date The selected month
16135              */
16136         'monthchange': true,
16137         /**
16138              * @event evententer
16139              * Fires when mouse over an event
16140              * @param {Calendar} this
16141              * @param {event} Event
16142              */
16143         'evententer': true,
16144         /**
16145              * @event eventleave
16146              * Fires when the mouse leaves an
16147              * @param {Calendar} this
16148              * @param {event}
16149              */
16150         'eventleave': true,
16151         /**
16152              * @event eventclick
16153              * Fires when the mouse click an
16154              * @param {Calendar} this
16155              * @param {event}
16156              */
16157         'eventclick': true
16158         
16159     });
16160
16161 };
16162
16163 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16164     
16165      /**
16166      * @cfg {Number} startDay
16167      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16168      */
16169     startDay : 0,
16170     
16171     loadMask : false,
16172     
16173     header : false,
16174       
16175     getAutoCreate : function(){
16176         
16177         
16178         var fc_button = function(name, corner, style, content ) {
16179             return Roo.apply({},{
16180                 tag : 'span',
16181                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16182                          (corner.length ?
16183                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16184                             ''
16185                         ),
16186                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16187                 unselectable: 'on'
16188             });
16189         };
16190         
16191         var header = {};
16192         
16193         if(!this.header){
16194             header = {
16195                 tag : 'table',
16196                 cls : 'fc-header',
16197                 style : 'width:100%',
16198                 cn : [
16199                     {
16200                         tag: 'tr',
16201                         cn : [
16202                             {
16203                                 tag : 'td',
16204                                 cls : 'fc-header-left',
16205                                 cn : [
16206                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16207                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16208                                     { tag: 'span', cls: 'fc-header-space' },
16209                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16210
16211
16212                                 ]
16213                             },
16214
16215                             {
16216                                 tag : 'td',
16217                                 cls : 'fc-header-center',
16218                                 cn : [
16219                                     {
16220                                         tag: 'span',
16221                                         cls: 'fc-header-title',
16222                                         cn : {
16223                                             tag: 'H2',
16224                                             html : 'month / year'
16225                                         }
16226                                     }
16227
16228                                 ]
16229                             },
16230                             {
16231                                 tag : 'td',
16232                                 cls : 'fc-header-right',
16233                                 cn : [
16234                               /*      fc_button('month', 'left', '', 'month' ),
16235                                     fc_button('week', '', '', 'week' ),
16236                                     fc_button('day', 'right', '', 'day' )
16237                                 */    
16238
16239                                 ]
16240                             }
16241
16242                         ]
16243                     }
16244                 ]
16245             };
16246         }
16247         
16248         header = this.header;
16249         
16250        
16251         var cal_heads = function() {
16252             var ret = [];
16253             // fixme - handle this.
16254             
16255             for (var i =0; i < Date.dayNames.length; i++) {
16256                 var d = Date.dayNames[i];
16257                 ret.push({
16258                     tag: 'th',
16259                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16260                     html : d.substring(0,3)
16261                 });
16262                 
16263             }
16264             ret[0].cls += ' fc-first';
16265             ret[6].cls += ' fc-last';
16266             return ret;
16267         };
16268         var cal_cell = function(n) {
16269             return  {
16270                 tag: 'td',
16271                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16272                 cn : [
16273                     {
16274                         cn : [
16275                             {
16276                                 cls: 'fc-day-number',
16277                                 html: 'D'
16278                             },
16279                             {
16280                                 cls: 'fc-day-content',
16281                              
16282                                 cn : [
16283                                      {
16284                                         style: 'position: relative;' // height: 17px;
16285                                     }
16286                                 ]
16287                             }
16288                             
16289                             
16290                         ]
16291                     }
16292                 ]
16293                 
16294             }
16295         };
16296         var cal_rows = function() {
16297             
16298             var ret = [];
16299             for (var r = 0; r < 6; r++) {
16300                 var row= {
16301                     tag : 'tr',
16302                     cls : 'fc-week',
16303                     cn : []
16304                 };
16305                 
16306                 for (var i =0; i < Date.dayNames.length; i++) {
16307                     var d = Date.dayNames[i];
16308                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16309
16310                 }
16311                 row.cn[0].cls+=' fc-first';
16312                 row.cn[0].cn[0].style = 'min-height:90px';
16313                 row.cn[6].cls+=' fc-last';
16314                 ret.push(row);
16315                 
16316             }
16317             ret[0].cls += ' fc-first';
16318             ret[4].cls += ' fc-prev-last';
16319             ret[5].cls += ' fc-last';
16320             return ret;
16321             
16322         };
16323         
16324         var cal_table = {
16325             tag: 'table',
16326             cls: 'fc-border-separate',
16327             style : 'width:100%',
16328             cellspacing  : 0,
16329             cn : [
16330                 { 
16331                     tag: 'thead',
16332                     cn : [
16333                         { 
16334                             tag: 'tr',
16335                             cls : 'fc-first fc-last',
16336                             cn : cal_heads()
16337                         }
16338                     ]
16339                 },
16340                 { 
16341                     tag: 'tbody',
16342                     cn : cal_rows()
16343                 }
16344                   
16345             ]
16346         };
16347          
16348          var cfg = {
16349             cls : 'fc fc-ltr',
16350             cn : [
16351                 header,
16352                 {
16353                     cls : 'fc-content',
16354                     style : "position: relative;",
16355                     cn : [
16356                         {
16357                             cls : 'fc-view fc-view-month fc-grid',
16358                             style : 'position: relative',
16359                             unselectable : 'on',
16360                             cn : [
16361                                 {
16362                                     cls : 'fc-event-container',
16363                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16364                                 },
16365                                 cal_table
16366                             ]
16367                         }
16368                     ]
16369     
16370                 }
16371            ] 
16372             
16373         };
16374         
16375          
16376         
16377         return cfg;
16378     },
16379     
16380     
16381     initEvents : function()
16382     {
16383         if(!this.store){
16384             throw "can not find store for calendar";
16385         }
16386         
16387         var mark = {
16388             tag: "div",
16389             cls:"x-dlg-mask",
16390             style: "text-align:center",
16391             cn: [
16392                 {
16393                     tag: "div",
16394                     style: "background-color:white;width:50%;margin:250 auto",
16395                     cn: [
16396                         {
16397                             tag: "img",
16398                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16399                         },
16400                         {
16401                             tag: "span",
16402                             html: "Loading"
16403                         }
16404                         
16405                     ]
16406                 }
16407             ]
16408         };
16409         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16410         
16411         var size = this.el.select('.fc-content', true).first().getSize();
16412         this.maskEl.setSize(size.width, size.height);
16413         this.maskEl.enableDisplayMode("block");
16414         if(!this.loadMask){
16415             this.maskEl.hide();
16416         }
16417         
16418         this.store = Roo.factory(this.store, Roo.data);
16419         this.store.on('load', this.onLoad, this);
16420         this.store.on('beforeload', this.onBeforeLoad, this);
16421         
16422         this.resize();
16423         
16424         this.cells = this.el.select('.fc-day',true);
16425         //Roo.log(this.cells);
16426         this.textNodes = this.el.query('.fc-day-number');
16427         this.cells.addClassOnOver('fc-state-hover');
16428         
16429         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16430         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16431         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16432         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16433         
16434         this.on('monthchange', this.onMonthChange, this);
16435         
16436         this.update(new Date().clearTime());
16437     },
16438     
16439     resize : function() {
16440         var sz  = this.el.getSize();
16441         
16442         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16443         this.el.select('.fc-day-content div',true).setHeight(34);
16444     },
16445     
16446     
16447     // private
16448     showPrevMonth : function(e){
16449         this.update(this.activeDate.add("mo", -1));
16450     },
16451     showToday : function(e){
16452         this.update(new Date().clearTime());
16453     },
16454     // private
16455     showNextMonth : function(e){
16456         this.update(this.activeDate.add("mo", 1));
16457     },
16458
16459     // private
16460     showPrevYear : function(){
16461         this.update(this.activeDate.add("y", -1));
16462     },
16463
16464     // private
16465     showNextYear : function(){
16466         this.update(this.activeDate.add("y", 1));
16467     },
16468
16469     
16470    // private
16471     update : function(date)
16472     {
16473         var vd = this.activeDate;
16474         this.activeDate = date;
16475 //        if(vd && this.el){
16476 //            var t = date.getTime();
16477 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16478 //                Roo.log('using add remove');
16479 //                
16480 //                this.fireEvent('monthchange', this, date);
16481 //                
16482 //                this.cells.removeClass("fc-state-highlight");
16483 //                this.cells.each(function(c){
16484 //                   if(c.dateValue == t){
16485 //                       c.addClass("fc-state-highlight");
16486 //                       setTimeout(function(){
16487 //                            try{c.dom.firstChild.focus();}catch(e){}
16488 //                       }, 50);
16489 //                       return false;
16490 //                   }
16491 //                   return true;
16492 //                });
16493 //                return;
16494 //            }
16495 //        }
16496         
16497         var days = date.getDaysInMonth();
16498         
16499         var firstOfMonth = date.getFirstDateOfMonth();
16500         var startingPos = firstOfMonth.getDay()-this.startDay;
16501         
16502         if(startingPos < this.startDay){
16503             startingPos += 7;
16504         }
16505         
16506         var pm = date.add(Date.MONTH, -1);
16507         var prevStart = pm.getDaysInMonth()-startingPos;
16508 //        
16509         this.cells = this.el.select('.fc-day',true);
16510         this.textNodes = this.el.query('.fc-day-number');
16511         this.cells.addClassOnOver('fc-state-hover');
16512         
16513         var cells = this.cells.elements;
16514         var textEls = this.textNodes;
16515         
16516         Roo.each(cells, function(cell){
16517             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16518         });
16519         
16520         days += startingPos;
16521
16522         // convert everything to numbers so it's fast
16523         var day = 86400000;
16524         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16525         //Roo.log(d);
16526         //Roo.log(pm);
16527         //Roo.log(prevStart);
16528         
16529         var today = new Date().clearTime().getTime();
16530         var sel = date.clearTime().getTime();
16531         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16532         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16533         var ddMatch = this.disabledDatesRE;
16534         var ddText = this.disabledDatesText;
16535         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16536         var ddaysText = this.disabledDaysText;
16537         var format = this.format;
16538         
16539         var setCellClass = function(cal, cell){
16540             cell.row = 0;
16541             cell.events = [];
16542             cell.more = [];
16543             //Roo.log('set Cell Class');
16544             cell.title = "";
16545             var t = d.getTime();
16546             
16547             //Roo.log(d);
16548             
16549             cell.dateValue = t;
16550             if(t == today){
16551                 cell.className += " fc-today";
16552                 cell.className += " fc-state-highlight";
16553                 cell.title = cal.todayText;
16554             }
16555             if(t == sel){
16556                 // disable highlight in other month..
16557                 //cell.className += " fc-state-highlight";
16558                 
16559             }
16560             // disabling
16561             if(t < min) {
16562                 cell.className = " fc-state-disabled";
16563                 cell.title = cal.minText;
16564                 return;
16565             }
16566             if(t > max) {
16567                 cell.className = " fc-state-disabled";
16568                 cell.title = cal.maxText;
16569                 return;
16570             }
16571             if(ddays){
16572                 if(ddays.indexOf(d.getDay()) != -1){
16573                     cell.title = ddaysText;
16574                     cell.className = " fc-state-disabled";
16575                 }
16576             }
16577             if(ddMatch && format){
16578                 var fvalue = d.dateFormat(format);
16579                 if(ddMatch.test(fvalue)){
16580                     cell.title = ddText.replace("%0", fvalue);
16581                     cell.className = " fc-state-disabled";
16582                 }
16583             }
16584             
16585             if (!cell.initialClassName) {
16586                 cell.initialClassName = cell.dom.className;
16587             }
16588             
16589             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16590         };
16591
16592         var i = 0;
16593         
16594         for(; i < startingPos; i++) {
16595             textEls[i].innerHTML = (++prevStart);
16596             d.setDate(d.getDate()+1);
16597             
16598             cells[i].className = "fc-past fc-other-month";
16599             setCellClass(this, cells[i]);
16600         }
16601         
16602         var intDay = 0;
16603         
16604         for(; i < days; i++){
16605             intDay = i - startingPos + 1;
16606             textEls[i].innerHTML = (intDay);
16607             d.setDate(d.getDate()+1);
16608             
16609             cells[i].className = ''; // "x-date-active";
16610             setCellClass(this, cells[i]);
16611         }
16612         var extraDays = 0;
16613         
16614         for(; i < 42; i++) {
16615             textEls[i].innerHTML = (++extraDays);
16616             d.setDate(d.getDate()+1);
16617             
16618             cells[i].className = "fc-future fc-other-month";
16619             setCellClass(this, cells[i]);
16620         }
16621         
16622         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16623         
16624         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16625         
16626         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16627         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16628         
16629         if(totalRows != 6){
16630             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16631             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16632         }
16633         
16634         this.fireEvent('monthchange', this, date);
16635         
16636         
16637         /*
16638         if(!this.internalRender){
16639             var main = this.el.dom.firstChild;
16640             var w = main.offsetWidth;
16641             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16642             Roo.fly(main).setWidth(w);
16643             this.internalRender = true;
16644             // opera does not respect the auto grow header center column
16645             // then, after it gets a width opera refuses to recalculate
16646             // without a second pass
16647             if(Roo.isOpera && !this.secondPass){
16648                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16649                 this.secondPass = true;
16650                 this.update.defer(10, this, [date]);
16651             }
16652         }
16653         */
16654         
16655     },
16656     
16657     findCell : function(dt) {
16658         dt = dt.clearTime().getTime();
16659         var ret = false;
16660         this.cells.each(function(c){
16661             //Roo.log("check " +c.dateValue + '?=' + dt);
16662             if(c.dateValue == dt){
16663                 ret = c;
16664                 return false;
16665             }
16666             return true;
16667         });
16668         
16669         return ret;
16670     },
16671     
16672     findCells : function(ev) {
16673         var s = ev.start.clone().clearTime().getTime();
16674        // Roo.log(s);
16675         var e= ev.end.clone().clearTime().getTime();
16676        // Roo.log(e);
16677         var ret = [];
16678         this.cells.each(function(c){
16679              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16680             
16681             if(c.dateValue > e){
16682                 return ;
16683             }
16684             if(c.dateValue < s){
16685                 return ;
16686             }
16687             ret.push(c);
16688         });
16689         
16690         return ret;    
16691     },
16692     
16693 //    findBestRow: function(cells)
16694 //    {
16695 //        var ret = 0;
16696 //        
16697 //        for (var i =0 ; i < cells.length;i++) {
16698 //            ret  = Math.max(cells[i].rows || 0,ret);
16699 //        }
16700 //        return ret;
16701 //        
16702 //    },
16703     
16704     
16705     addItem : function(ev)
16706     {
16707         // look for vertical location slot in
16708         var cells = this.findCells(ev);
16709         
16710 //        ev.row = this.findBestRow(cells);
16711         
16712         // work out the location.
16713         
16714         var crow = false;
16715         var rows = [];
16716         for(var i =0; i < cells.length; i++) {
16717             
16718             cells[i].row = cells[0].row;
16719             
16720             if(i == 0){
16721                 cells[i].row = cells[i].row + 1;
16722             }
16723             
16724             if (!crow) {
16725                 crow = {
16726                     start : cells[i],
16727                     end :  cells[i]
16728                 };
16729                 continue;
16730             }
16731             if (crow.start.getY() == cells[i].getY()) {
16732                 // on same row.
16733                 crow.end = cells[i];
16734                 continue;
16735             }
16736             // different row.
16737             rows.push(crow);
16738             crow = {
16739                 start: cells[i],
16740                 end : cells[i]
16741             };
16742             
16743         }
16744         
16745         rows.push(crow);
16746         ev.els = [];
16747         ev.rows = rows;
16748         ev.cells = cells;
16749         
16750         cells[0].events.push(ev);
16751         
16752         this.calevents.push(ev);
16753     },
16754     
16755     clearEvents: function() {
16756         
16757         if(!this.calevents){
16758             return;
16759         }
16760         
16761         Roo.each(this.cells.elements, function(c){
16762             c.row = 0;
16763             c.events = [];
16764             c.more = [];
16765         });
16766         
16767         Roo.each(this.calevents, function(e) {
16768             Roo.each(e.els, function(el) {
16769                 el.un('mouseenter' ,this.onEventEnter, this);
16770                 el.un('mouseleave' ,this.onEventLeave, this);
16771                 el.remove();
16772             },this);
16773         },this);
16774         
16775         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16776             e.remove();
16777         });
16778         
16779     },
16780     
16781     renderEvents: function()
16782     {   
16783         var _this = this;
16784         
16785         this.cells.each(function(c) {
16786             
16787             if(c.row < 5){
16788                 return;
16789             }
16790             
16791             var ev = c.events;
16792             
16793             var r = 4;
16794             if(c.row != c.events.length){
16795                 r = 4 - (4 - (c.row - c.events.length));
16796             }
16797             
16798             c.events = ev.slice(0, r);
16799             c.more = ev.slice(r);
16800             
16801             if(c.more.length && c.more.length == 1){
16802                 c.events.push(c.more.pop());
16803             }
16804             
16805             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16806             
16807         });
16808             
16809         this.cells.each(function(c) {
16810             
16811             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16812             
16813             
16814             for (var e = 0; e < c.events.length; e++){
16815                 var ev = c.events[e];
16816                 var rows = ev.rows;
16817                 
16818                 for(var i = 0; i < rows.length; i++) {
16819                 
16820                     // how many rows should it span..
16821
16822                     var  cfg = {
16823                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16824                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16825
16826                         unselectable : "on",
16827                         cn : [
16828                             {
16829                                 cls: 'fc-event-inner',
16830                                 cn : [
16831     //                                {
16832     //                                  tag:'span',
16833     //                                  cls: 'fc-event-time',
16834     //                                  html : cells.length > 1 ? '' : ev.time
16835     //                                },
16836                                     {
16837                                       tag:'span',
16838                                       cls: 'fc-event-title',
16839                                       html : String.format('{0}', ev.title)
16840                                     }
16841
16842
16843                                 ]
16844                             },
16845                             {
16846                                 cls: 'ui-resizable-handle ui-resizable-e',
16847                                 html : '&nbsp;&nbsp;&nbsp'
16848                             }
16849
16850                         ]
16851                     };
16852
16853                     if (i == 0) {
16854                         cfg.cls += ' fc-event-start';
16855                     }
16856                     if ((i+1) == rows.length) {
16857                         cfg.cls += ' fc-event-end';
16858                     }
16859
16860                     var ctr = _this.el.select('.fc-event-container',true).first();
16861                     var cg = ctr.createChild(cfg);
16862
16863                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16864                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16865
16866                     var r = (c.more.length) ? 1 : 0;
16867                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
16868                     cg.setWidth(ebox.right - sbox.x -2);
16869
16870                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16871                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16872                     cg.on('click', _this.onEventClick, _this, ev);
16873
16874                     ev.els.push(cg);
16875                     
16876                 }
16877                 
16878             }
16879             
16880             
16881             if(c.more.length){
16882                 var  cfg = {
16883                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16884                     style : 'position: absolute',
16885                     unselectable : "on",
16886                     cn : [
16887                         {
16888                             cls: 'fc-event-inner',
16889                             cn : [
16890                                 {
16891                                   tag:'span',
16892                                   cls: 'fc-event-title',
16893                                   html : 'More'
16894                                 }
16895
16896
16897                             ]
16898                         },
16899                         {
16900                             cls: 'ui-resizable-handle ui-resizable-e',
16901                             html : '&nbsp;&nbsp;&nbsp'
16902                         }
16903
16904                     ]
16905                 };
16906
16907                 var ctr = _this.el.select('.fc-event-container',true).first();
16908                 var cg = ctr.createChild(cfg);
16909
16910                 var sbox = c.select('.fc-day-content',true).first().getBox();
16911                 var ebox = c.select('.fc-day-content',true).first().getBox();
16912                 //Roo.log(cg);
16913                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
16914                 cg.setWidth(ebox.right - sbox.x -2);
16915
16916                 cg.on('click', _this.onMoreEventClick, _this, c.more);
16917                 
16918             }
16919             
16920         });
16921         
16922         
16923         
16924     },
16925     
16926     onEventEnter: function (e, el,event,d) {
16927         this.fireEvent('evententer', this, el, event);
16928     },
16929     
16930     onEventLeave: function (e, el,event,d) {
16931         this.fireEvent('eventleave', this, el, event);
16932     },
16933     
16934     onEventClick: function (e, el,event,d) {
16935         this.fireEvent('eventclick', this, el, event);
16936     },
16937     
16938     onMonthChange: function () {
16939         this.store.load();
16940     },
16941     
16942     onMoreEventClick: function(e, el, more)
16943     {
16944         var _this = this;
16945         
16946         this.calpopover.placement = 'right';
16947         this.calpopover.setTitle('More');
16948         
16949         this.calpopover.setContent('');
16950         
16951         var ctr = this.calpopover.el.select('.popover-content', true).first();
16952         
16953         Roo.each(more, function(m){
16954             var cfg = {
16955                 cls : 'fc-event-hori fc-event-draggable',
16956                 html : m.title
16957             };
16958             var cg = ctr.createChild(cfg);
16959             
16960             cg.on('click', _this.onEventClick, _this, m);
16961         });
16962         
16963         this.calpopover.show(el);
16964         
16965         
16966     },
16967     
16968     onLoad: function () 
16969     {   
16970         this.calevents = [];
16971         var cal = this;
16972         
16973         if(this.store.getCount() > 0){
16974             this.store.data.each(function(d){
16975                cal.addItem({
16976                     id : d.data.id,
16977                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
16978                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
16979                     time : d.data.start_time,
16980                     title : d.data.title,
16981                     description : d.data.description,
16982                     venue : d.data.venue
16983                 });
16984             });
16985         }
16986         
16987         this.renderEvents();
16988         
16989         if(this.calevents.length && this.loadMask){
16990             this.maskEl.hide();
16991         }
16992     },
16993     
16994     onBeforeLoad: function()
16995     {
16996         this.clearEvents();
16997         if(this.loadMask){
16998             this.maskEl.show();
16999         }
17000     }
17001 });
17002
17003  
17004  /*
17005  * - LGPL
17006  *
17007  * element
17008  * 
17009  */
17010
17011 /**
17012  * @class Roo.bootstrap.Popover
17013  * @extends Roo.bootstrap.Component
17014  * Bootstrap Popover class
17015  * @cfg {String} html contents of the popover   (or false to use children..)
17016  * @cfg {String} title of popover (or false to hide)
17017  * @cfg {String} placement how it is placed
17018  * @cfg {String} trigger click || hover (or false to trigger manually)
17019  * @cfg {String} over what (parent or false to trigger manually.)
17020  * @cfg {Number} delay - delay before showing
17021  
17022  * @constructor
17023  * Create a new Popover
17024  * @param {Object} config The config object
17025  */
17026
17027 Roo.bootstrap.Popover = function(config){
17028     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17029     
17030     this.addEvents({
17031         // raw events
17032          /**
17033          * @event show
17034          * After the popover show
17035          * 
17036          * @param {Roo.bootstrap.Popover} this
17037          */
17038         "show" : true,
17039         /**
17040          * @event hide
17041          * After the popover hide
17042          * 
17043          * @param {Roo.bootstrap.Popover} this
17044          */
17045         "hide" : true
17046     });
17047 };
17048
17049 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17050     
17051     title: 'Fill in a title',
17052     html: false,
17053     
17054     placement : 'right',
17055     trigger : 'hover', // hover
17056     
17057     delay : 0,
17058     
17059     over: 'parent',
17060     
17061     can_build_overlaid : false,
17062     
17063     getChildContainer : function()
17064     {
17065         return this.el.select('.popover-content',true).first();
17066     },
17067     
17068     getAutoCreate : function(){
17069          
17070         var cfg = {
17071            cls : 'popover roo-dynamic',
17072            style: 'display:block',
17073            cn : [
17074                 {
17075                     cls : 'arrow'
17076                 },
17077                 {
17078                     cls : 'popover-inner',
17079                     cn : [
17080                         {
17081                             tag: 'h3',
17082                             cls: 'popover-title',
17083                             html : this.title
17084                         },
17085                         {
17086                             cls : 'popover-content',
17087                             html : this.html
17088                         }
17089                     ]
17090                     
17091                 }
17092            ]
17093         };
17094         
17095         return cfg;
17096     },
17097     setTitle: function(str)
17098     {
17099         this.title = str;
17100         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17101     },
17102     setContent: function(str)
17103     {
17104         this.html = str;
17105         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17106     },
17107     // as it get's added to the bottom of the page.
17108     onRender : function(ct, position)
17109     {
17110         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17111         if(!this.el){
17112             var cfg = Roo.apply({},  this.getAutoCreate());
17113             cfg.id = Roo.id();
17114             
17115             if (this.cls) {
17116                 cfg.cls += ' ' + this.cls;
17117             }
17118             if (this.style) {
17119                 cfg.style = this.style;
17120             }
17121             //Roo.log("adding to ");
17122             this.el = Roo.get(document.body).createChild(cfg, position);
17123 //            Roo.log(this.el);
17124         }
17125         this.initEvents();
17126     },
17127     
17128     initEvents : function()
17129     {
17130         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17131         this.el.enableDisplayMode('block');
17132         this.el.hide();
17133         if (this.over === false) {
17134             return; 
17135         }
17136         if (this.triggers === false) {
17137             return;
17138         }
17139         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17140         var triggers = this.trigger ? this.trigger.split(' ') : [];
17141         Roo.each(triggers, function(trigger) {
17142         
17143             if (trigger == 'click') {
17144                 on_el.on('click', this.toggle, this);
17145             } else if (trigger != 'manual') {
17146                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17147                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17148       
17149                 on_el.on(eventIn  ,this.enter, this);
17150                 on_el.on(eventOut, this.leave, this);
17151             }
17152         }, this);
17153         
17154     },
17155     
17156     
17157     // private
17158     timeout : null,
17159     hoverState : null,
17160     
17161     toggle : function () {
17162         this.hoverState == 'in' ? this.leave() : this.enter();
17163     },
17164     
17165     enter : function () {
17166         
17167         clearTimeout(this.timeout);
17168     
17169         this.hoverState = 'in';
17170     
17171         if (!this.delay || !this.delay.show) {
17172             this.show();
17173             return;
17174         }
17175         var _t = this;
17176         this.timeout = setTimeout(function () {
17177             if (_t.hoverState == 'in') {
17178                 _t.show();
17179             }
17180         }, this.delay.show)
17181     },
17182     
17183     leave : function() {
17184         clearTimeout(this.timeout);
17185     
17186         this.hoverState = 'out';
17187     
17188         if (!this.delay || !this.delay.hide) {
17189             this.hide();
17190             return;
17191         }
17192         var _t = this;
17193         this.timeout = setTimeout(function () {
17194             if (_t.hoverState == 'out') {
17195                 _t.hide();
17196             }
17197         }, this.delay.hide)
17198     },
17199     
17200     show : function (on_el)
17201     {
17202         if (!on_el) {
17203             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17204         }
17205         
17206         // set content.
17207         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17208         if (this.html !== false) {
17209             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17210         }
17211         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17212         if (!this.title.length) {
17213             this.el.select('.popover-title',true).hide();
17214         }
17215         
17216         var placement = typeof this.placement == 'function' ?
17217             this.placement.call(this, this.el, on_el) :
17218             this.placement;
17219             
17220         var autoToken = /\s?auto?\s?/i;
17221         var autoPlace = autoToken.test(placement);
17222         if (autoPlace) {
17223             placement = placement.replace(autoToken, '') || 'top';
17224         }
17225         
17226         //this.el.detach()
17227         //this.el.setXY([0,0]);
17228         this.el.show();
17229         this.el.dom.style.display='block';
17230         this.el.addClass(placement);
17231         
17232         //this.el.appendTo(on_el);
17233         
17234         var p = this.getPosition();
17235         var box = this.el.getBox();
17236         
17237         if (autoPlace) {
17238             // fixme..
17239         }
17240         var align = Roo.bootstrap.Popover.alignment[placement];
17241         this.el.alignTo(on_el, align[0],align[1]);
17242         //var arrow = this.el.select('.arrow',true).first();
17243         //arrow.set(align[2], 
17244         
17245         this.el.addClass('in');
17246         
17247         
17248         if (this.el.hasClass('fade')) {
17249             // fade it?
17250         }
17251         
17252         this.hoverState = 'in';
17253         
17254         this.fireEvent('show', this);
17255         
17256     },
17257     hide : function()
17258     {
17259         this.el.setXY([0,0]);
17260         this.el.removeClass('in');
17261         this.el.hide();
17262         this.hoverState = null;
17263         
17264         this.fireEvent('hide', this);
17265     }
17266     
17267 });
17268
17269 Roo.bootstrap.Popover.alignment = {
17270     'left' : ['r-l', [-10,0], 'right'],
17271     'right' : ['l-r', [10,0], 'left'],
17272     'bottom' : ['t-b', [0,10], 'top'],
17273     'top' : [ 'b-t', [0,-10], 'bottom']
17274 };
17275
17276  /*
17277  * - LGPL
17278  *
17279  * Progress
17280  * 
17281  */
17282
17283 /**
17284  * @class Roo.bootstrap.Progress
17285  * @extends Roo.bootstrap.Component
17286  * Bootstrap Progress class
17287  * @cfg {Boolean} striped striped of the progress bar
17288  * @cfg {Boolean} active animated of the progress bar
17289  * 
17290  * 
17291  * @constructor
17292  * Create a new Progress
17293  * @param {Object} config The config object
17294  */
17295
17296 Roo.bootstrap.Progress = function(config){
17297     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17298 };
17299
17300 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17301     
17302     striped : false,
17303     active: false,
17304     
17305     getAutoCreate : function(){
17306         var cfg = {
17307             tag: 'div',
17308             cls: 'progress'
17309         };
17310         
17311         
17312         if(this.striped){
17313             cfg.cls += ' progress-striped';
17314         }
17315       
17316         if(this.active){
17317             cfg.cls += ' active';
17318         }
17319         
17320         
17321         return cfg;
17322     }
17323    
17324 });
17325
17326  
17327
17328  /*
17329  * - LGPL
17330  *
17331  * ProgressBar
17332  * 
17333  */
17334
17335 /**
17336  * @class Roo.bootstrap.ProgressBar
17337  * @extends Roo.bootstrap.Component
17338  * Bootstrap ProgressBar class
17339  * @cfg {Number} aria_valuenow aria-value now
17340  * @cfg {Number} aria_valuemin aria-value min
17341  * @cfg {Number} aria_valuemax aria-value max
17342  * @cfg {String} label label for the progress bar
17343  * @cfg {String} panel (success | info | warning | danger )
17344  * @cfg {String} role role of the progress bar
17345  * @cfg {String} sr_only text
17346  * 
17347  * 
17348  * @constructor
17349  * Create a new ProgressBar
17350  * @param {Object} config The config object
17351  */
17352
17353 Roo.bootstrap.ProgressBar = function(config){
17354     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17355 };
17356
17357 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17358     
17359     aria_valuenow : 0,
17360     aria_valuemin : 0,
17361     aria_valuemax : 100,
17362     label : false,
17363     panel : false,
17364     role : false,
17365     sr_only: false,
17366     
17367     getAutoCreate : function()
17368     {
17369         
17370         var cfg = {
17371             tag: 'div',
17372             cls: 'progress-bar',
17373             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17374         };
17375         
17376         if(this.sr_only){
17377             cfg.cn = {
17378                 tag: 'span',
17379                 cls: 'sr-only',
17380                 html: this.sr_only
17381             }
17382         }
17383         
17384         if(this.role){
17385             cfg.role = this.role;
17386         }
17387         
17388         if(this.aria_valuenow){
17389             cfg['aria-valuenow'] = this.aria_valuenow;
17390         }
17391         
17392         if(this.aria_valuemin){
17393             cfg['aria-valuemin'] = this.aria_valuemin;
17394         }
17395         
17396         if(this.aria_valuemax){
17397             cfg['aria-valuemax'] = this.aria_valuemax;
17398         }
17399         
17400         if(this.label && !this.sr_only){
17401             cfg.html = this.label;
17402         }
17403         
17404         if(this.panel){
17405             cfg.cls += ' progress-bar-' + this.panel;
17406         }
17407         
17408         return cfg;
17409     },
17410     
17411     update : function(aria_valuenow)
17412     {
17413         this.aria_valuenow = aria_valuenow;
17414         
17415         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17416     }
17417    
17418 });
17419
17420  
17421
17422  /*
17423  * - LGPL
17424  *
17425  * column
17426  * 
17427  */
17428
17429 /**
17430  * @class Roo.bootstrap.TabGroup
17431  * @extends Roo.bootstrap.Column
17432  * Bootstrap Column class
17433  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17434  * @cfg {Boolean} carousel true to make the group behave like a carousel
17435  * @cfg {Boolean} bullets show bullets for the panels
17436  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17437  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17438  * @cfg {Boolean} showarrow (true|false) show arrow default true
17439  * 
17440  * @constructor
17441  * Create a new TabGroup
17442  * @param {Object} config The config object
17443  */
17444
17445 Roo.bootstrap.TabGroup = function(config){
17446     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17447     if (!this.navId) {
17448         this.navId = Roo.id();
17449     }
17450     this.tabs = [];
17451     Roo.bootstrap.TabGroup.register(this);
17452     
17453 };
17454
17455 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17456     
17457     carousel : false,
17458     transition : false,
17459     bullets : 0,
17460     timer : 0,
17461     autoslide : false,
17462     slideFn : false,
17463     slideOnTouch : false,
17464     showarrow : true,
17465     
17466     getAutoCreate : function()
17467     {
17468         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17469         
17470         cfg.cls += ' tab-content';
17471         
17472         if (this.carousel) {
17473             cfg.cls += ' carousel slide';
17474             
17475             cfg.cn = [{
17476                cls : 'carousel-inner',
17477                cn : []
17478             }];
17479         
17480             if(this.bullets  && !Roo.isTouch){
17481                 
17482                 var bullets = {
17483                     cls : 'carousel-bullets',
17484                     cn : []
17485                 };
17486                
17487                 if(this.bullets_cls){
17488                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17489                 }
17490                 
17491                 bullets.cn.push({
17492                     cls : 'clear'
17493                 });
17494                 
17495                 cfg.cn[0].cn.push(bullets);
17496             }
17497             
17498             if(this.showarrow){
17499                 cfg.cn[0].cn.push({
17500                     tag : 'div',
17501                     class : 'carousel-arrow',
17502                     cn : [
17503                         {
17504                             tag : 'div',
17505                             class : 'carousel-prev',
17506                             cn : [
17507                                 {
17508                                     tag : 'i',
17509                                     class : 'fa fa-chevron-left'
17510                                 }
17511                             ]
17512                         },
17513                         {
17514                             tag : 'div',
17515                             class : 'carousel-next',
17516                             cn : [
17517                                 {
17518                                     tag : 'i',
17519                                     class : 'fa fa-chevron-right'
17520                                 }
17521                             ]
17522                         }
17523                     ]
17524                 });
17525             }
17526             
17527         }
17528         
17529         return cfg;
17530     },
17531     
17532     initEvents:  function()
17533     {
17534 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17535 //            this.el.on("touchstart", this.onTouchStart, this);
17536 //        }
17537         
17538         if(this.autoslide){
17539             var _this = this;
17540             
17541             this.slideFn = window.setInterval(function() {
17542                 _this.showPanelNext();
17543             }, this.timer);
17544         }
17545         
17546         if(this.showarrow){
17547             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17548             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17549         }
17550         
17551         
17552     },
17553     
17554 //    onTouchStart : function(e, el, o)
17555 //    {
17556 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17557 //            return;
17558 //        }
17559 //        
17560 //        this.showPanelNext();
17561 //    },
17562     
17563     
17564     getChildContainer : function()
17565     {
17566         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17567     },
17568     
17569     /**
17570     * register a Navigation item
17571     * @param {Roo.bootstrap.NavItem} the navitem to add
17572     */
17573     register : function(item)
17574     {
17575         this.tabs.push( item);
17576         item.navId = this.navId; // not really needed..
17577         this.addBullet();
17578     
17579     },
17580     
17581     getActivePanel : function()
17582     {
17583         var r = false;
17584         Roo.each(this.tabs, function(t) {
17585             if (t.active) {
17586                 r = t;
17587                 return false;
17588             }
17589             return null;
17590         });
17591         return r;
17592         
17593     },
17594     getPanelByName : function(n)
17595     {
17596         var r = false;
17597         Roo.each(this.tabs, function(t) {
17598             if (t.tabId == n) {
17599                 r = t;
17600                 return false;
17601             }
17602             return null;
17603         });
17604         return r;
17605     },
17606     indexOfPanel : function(p)
17607     {
17608         var r = false;
17609         Roo.each(this.tabs, function(t,i) {
17610             if (t.tabId == p.tabId) {
17611                 r = i;
17612                 return false;
17613             }
17614             return null;
17615         });
17616         return r;
17617     },
17618     /**
17619      * show a specific panel
17620      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17621      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17622      */
17623     showPanel : function (pan)
17624     {
17625         if(this.transition || typeof(pan) == 'undefined'){
17626             Roo.log("waiting for the transitionend");
17627             return;
17628         }
17629         
17630         if (typeof(pan) == 'number') {
17631             pan = this.tabs[pan];
17632         }
17633         
17634         if (typeof(pan) == 'string') {
17635             pan = this.getPanelByName(pan);
17636         }
17637         
17638         var cur = this.getActivePanel();
17639         
17640         if(!pan || !cur){
17641             Roo.log('pan or acitve pan is undefined');
17642             return false;
17643         }
17644         
17645         if (pan.tabId == this.getActivePanel().tabId) {
17646             return true;
17647         }
17648         
17649         if (false === cur.fireEvent('beforedeactivate')) {
17650             return false;
17651         }
17652         
17653         if(this.bullets > 0 && !Roo.isTouch){
17654             this.setActiveBullet(this.indexOfPanel(pan));
17655         }
17656         
17657         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17658             
17659             this.transition = true;
17660             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17661             var lr = dir == 'next' ? 'left' : 'right';
17662             pan.el.addClass(dir); // or prev
17663             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17664             cur.el.addClass(lr); // or right
17665             pan.el.addClass(lr);
17666             
17667             var _this = this;
17668             cur.el.on('transitionend', function() {
17669                 Roo.log("trans end?");
17670                 
17671                 pan.el.removeClass([lr,dir]);
17672                 pan.setActive(true);
17673                 
17674                 cur.el.removeClass([lr]);
17675                 cur.setActive(false);
17676                 
17677                 _this.transition = false;
17678                 
17679             }, this, { single:  true } );
17680             
17681             return true;
17682         }
17683         
17684         cur.setActive(false);
17685         pan.setActive(true);
17686         
17687         return true;
17688         
17689     },
17690     showPanelNext : function()
17691     {
17692         var i = this.indexOfPanel(this.getActivePanel());
17693         
17694         if (i >= this.tabs.length - 1 && !this.autoslide) {
17695             return;
17696         }
17697         
17698         if (i >= this.tabs.length - 1 && this.autoslide) {
17699             i = -1;
17700         }
17701         
17702         this.showPanel(this.tabs[i+1]);
17703     },
17704     
17705     showPanelPrev : function()
17706     {
17707         var i = this.indexOfPanel(this.getActivePanel());
17708         
17709         if (i  < 1 && !this.autoslide) {
17710             return;
17711         }
17712         
17713         if (i < 1 && this.autoslide) {
17714             i = this.tabs.length;
17715         }
17716         
17717         this.showPanel(this.tabs[i-1]);
17718     },
17719     
17720     
17721     addBullet: function()
17722     {
17723         if(!this.bullets || Roo.isTouch){
17724             return;
17725         }
17726         var ctr = this.el.select('.carousel-bullets',true).first();
17727         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17728         var bullet = ctr.createChild({
17729             cls : 'bullet bullet-' + i
17730         },ctr.dom.lastChild);
17731         
17732         
17733         var _this = this;
17734         
17735         bullet.on('click', (function(e, el, o, ii, t){
17736
17737             e.preventDefault();
17738
17739             this.showPanel(ii);
17740
17741             if(this.autoslide && this.slideFn){
17742                 clearInterval(this.slideFn);
17743                 this.slideFn = window.setInterval(function() {
17744                     _this.showPanelNext();
17745                 }, this.timer);
17746             }
17747
17748         }).createDelegate(this, [i, bullet], true));
17749                 
17750         
17751     },
17752      
17753     setActiveBullet : function(i)
17754     {
17755         if(Roo.isTouch){
17756             return;
17757         }
17758         
17759         Roo.each(this.el.select('.bullet', true).elements, function(el){
17760             el.removeClass('selected');
17761         });
17762
17763         var bullet = this.el.select('.bullet-' + i, true).first();
17764         
17765         if(!bullet){
17766             return;
17767         }
17768         
17769         bullet.addClass('selected');
17770     }
17771     
17772     
17773   
17774 });
17775
17776  
17777
17778  
17779  
17780 Roo.apply(Roo.bootstrap.TabGroup, {
17781     
17782     groups: {},
17783      /**
17784     * register a Navigation Group
17785     * @param {Roo.bootstrap.NavGroup} the navgroup to add
17786     */
17787     register : function(navgrp)
17788     {
17789         this.groups[navgrp.navId] = navgrp;
17790         
17791     },
17792     /**
17793     * fetch a Navigation Group based on the navigation ID
17794     * if one does not exist , it will get created.
17795     * @param {string} the navgroup to add
17796     * @returns {Roo.bootstrap.NavGroup} the navgroup 
17797     */
17798     get: function(navId) {
17799         if (typeof(this.groups[navId]) == 'undefined') {
17800             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17801         }
17802         return this.groups[navId] ;
17803     }
17804     
17805     
17806     
17807 });
17808
17809  /*
17810  * - LGPL
17811  *
17812  * TabPanel
17813  * 
17814  */
17815
17816 /**
17817  * @class Roo.bootstrap.TabPanel
17818  * @extends Roo.bootstrap.Component
17819  * Bootstrap TabPanel class
17820  * @cfg {Boolean} active panel active
17821  * @cfg {String} html panel content
17822  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17823  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17824  * @cfg {String} href click to link..
17825  * 
17826  * 
17827  * @constructor
17828  * Create a new TabPanel
17829  * @param {Object} config The config object
17830  */
17831
17832 Roo.bootstrap.TabPanel = function(config){
17833     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17834     this.addEvents({
17835         /**
17836              * @event changed
17837              * Fires when the active status changes
17838              * @param {Roo.bootstrap.TabPanel} this
17839              * @param {Boolean} state the new state
17840             
17841          */
17842         'changed': true,
17843         /**
17844              * @event beforedeactivate
17845              * Fires before a tab is de-activated - can be used to do validation on a form.
17846              * @param {Roo.bootstrap.TabPanel} this
17847              * @return {Boolean} false if there is an error
17848             
17849          */
17850         'beforedeactivate': true
17851      });
17852     
17853     this.tabId = this.tabId || Roo.id();
17854   
17855 };
17856
17857 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
17858     
17859     active: false,
17860     html: false,
17861     tabId: false,
17862     navId : false,
17863     href : '',
17864     
17865     getAutoCreate : function(){
17866         var cfg = {
17867             tag: 'div',
17868             // item is needed for carousel - not sure if it has any effect otherwise
17869             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17870             html: this.html || ''
17871         };
17872         
17873         if(this.active){
17874             cfg.cls += ' active';
17875         }
17876         
17877         if(this.tabId){
17878             cfg.tabId = this.tabId;
17879         }
17880         
17881         
17882         return cfg;
17883     },
17884     
17885     initEvents:  function()
17886     {
17887         var p = this.parent();
17888         
17889         this.navId = this.navId || p.navId;
17890         
17891         if (typeof(this.navId) != 'undefined') {
17892             // not really needed.. but just in case.. parent should be a NavGroup.
17893             var tg = Roo.bootstrap.TabGroup.get(this.navId);
17894             
17895             tg.register(this);
17896             
17897             var i = tg.tabs.length - 1;
17898             
17899             if(this.active && tg.bullets > 0 && i < tg.bullets){
17900                 tg.setActiveBullet(i);
17901             }
17902         }
17903         
17904         this.el.on('click', this.onClick, this);
17905         
17906         if(Roo.isTouch){
17907             this.el.on("touchstart", this.onTouchStart, this);
17908             this.el.on("touchmove", this.onTouchMove, this);
17909             this.el.on("touchend", this.onTouchEnd, this);
17910         }
17911         
17912     },
17913     
17914     onRender : function(ct, position)
17915     {
17916         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17917     },
17918     
17919     setActive : function(state)
17920     {
17921         Roo.log("panel - set active " + this.tabId + "=" + state);
17922         
17923         this.active = state;
17924         if (!state) {
17925             this.el.removeClass('active');
17926             
17927         } else  if (!this.el.hasClass('active')) {
17928             this.el.addClass('active');
17929         }
17930         
17931         this.fireEvent('changed', this, state);
17932     },
17933     
17934     onClick : function(e)
17935     {
17936         e.preventDefault();
17937         
17938         if(!this.href.length){
17939             return;
17940         }
17941         
17942         window.location.href = this.href;
17943     },
17944     
17945     startX : 0,
17946     startY : 0,
17947     endX : 0,
17948     endY : 0,
17949     swiping : false,
17950     
17951     onTouchStart : function(e)
17952     {
17953         this.swiping = false;
17954         
17955         this.startX = e.browserEvent.touches[0].clientX;
17956         this.startY = e.browserEvent.touches[0].clientY;
17957     },
17958     
17959     onTouchMove : function(e)
17960     {
17961         this.swiping = true;
17962         
17963         this.endX = e.browserEvent.touches[0].clientX;
17964         this.endY = e.browserEvent.touches[0].clientY;
17965     },
17966     
17967     onTouchEnd : function(e)
17968     {
17969         if(!this.swiping){
17970             this.onClick(e);
17971             return;
17972         }
17973         
17974         var tabGroup = this.parent();
17975         
17976         if(this.endX > this.startX){ // swiping right
17977             tabGroup.showPanelPrev();
17978             return;
17979         }
17980         
17981         if(this.startX > this.endX){ // swiping left
17982             tabGroup.showPanelNext();
17983             return;
17984         }
17985     }
17986     
17987     
17988 });
17989  
17990
17991  
17992
17993  /*
17994  * - LGPL
17995  *
17996  * DateField
17997  * 
17998  */
17999
18000 /**
18001  * @class Roo.bootstrap.DateField
18002  * @extends Roo.bootstrap.Input
18003  * Bootstrap DateField class
18004  * @cfg {Number} weekStart default 0
18005  * @cfg {String} viewMode default empty, (months|years)
18006  * @cfg {String} minViewMode default empty, (months|years)
18007  * @cfg {Number} startDate default -Infinity
18008  * @cfg {Number} endDate default Infinity
18009  * @cfg {Boolean} todayHighlight default false
18010  * @cfg {Boolean} todayBtn default false
18011  * @cfg {Boolean} calendarWeeks default false
18012  * @cfg {Object} daysOfWeekDisabled default empty
18013  * @cfg {Boolean} singleMode default false (true | false)
18014  * 
18015  * @cfg {Boolean} keyboardNavigation default true
18016  * @cfg {String} language default en
18017  * 
18018  * @constructor
18019  * Create a new DateField
18020  * @param {Object} config The config object
18021  */
18022
18023 Roo.bootstrap.DateField = function(config){
18024     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18025      this.addEvents({
18026             /**
18027              * @event show
18028              * Fires when this field show.
18029              * @param {Roo.bootstrap.DateField} this
18030              * @param {Mixed} date The date value
18031              */
18032             show : true,
18033             /**
18034              * @event show
18035              * Fires when this field hide.
18036              * @param {Roo.bootstrap.DateField} this
18037              * @param {Mixed} date The date value
18038              */
18039             hide : true,
18040             /**
18041              * @event select
18042              * Fires when select a date.
18043              * @param {Roo.bootstrap.DateField} this
18044              * @param {Mixed} date The date value
18045              */
18046             select : true,
18047             /**
18048              * @event beforeselect
18049              * Fires when before select a date.
18050              * @param {Roo.bootstrap.DateField} this
18051              * @param {Mixed} date The date value
18052              */
18053             beforeselect : true
18054         });
18055 };
18056
18057 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18058     
18059     /**
18060      * @cfg {String} format
18061      * The default date format string which can be overriden for localization support.  The format must be
18062      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18063      */
18064     format : "m/d/y",
18065     /**
18066      * @cfg {String} altFormats
18067      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18068      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18069      */
18070     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18071     
18072     weekStart : 0,
18073     
18074     viewMode : '',
18075     
18076     minViewMode : '',
18077     
18078     todayHighlight : false,
18079     
18080     todayBtn: false,
18081     
18082     language: 'en',
18083     
18084     keyboardNavigation: true,
18085     
18086     calendarWeeks: false,
18087     
18088     startDate: -Infinity,
18089     
18090     endDate: Infinity,
18091     
18092     daysOfWeekDisabled: [],
18093     
18094     _events: [],
18095     
18096     singleMode : false,
18097     
18098     UTCDate: function()
18099     {
18100         return new Date(Date.UTC.apply(Date, arguments));
18101     },
18102     
18103     UTCToday: function()
18104     {
18105         var today = new Date();
18106         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18107     },
18108     
18109     getDate: function() {
18110             var d = this.getUTCDate();
18111             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18112     },
18113     
18114     getUTCDate: function() {
18115             return this.date;
18116     },
18117     
18118     setDate: function(d) {
18119             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18120     },
18121     
18122     setUTCDate: function(d) {
18123             this.date = d;
18124             this.setValue(this.formatDate(this.date));
18125     },
18126         
18127     onRender: function(ct, position)
18128     {
18129         
18130         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18131         
18132         this.language = this.language || 'en';
18133         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18134         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18135         
18136         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18137         this.format = this.format || 'm/d/y';
18138         this.isInline = false;
18139         this.isInput = true;
18140         this.component = this.el.select('.add-on', true).first() || false;
18141         this.component = (this.component && this.component.length === 0) ? false : this.component;
18142         this.hasInput = this.component && this.inputEl().length;
18143         
18144         if (typeof(this.minViewMode === 'string')) {
18145             switch (this.minViewMode) {
18146                 case 'months':
18147                     this.minViewMode = 1;
18148                     break;
18149                 case 'years':
18150                     this.minViewMode = 2;
18151                     break;
18152                 default:
18153                     this.minViewMode = 0;
18154                     break;
18155             }
18156         }
18157         
18158         if (typeof(this.viewMode === 'string')) {
18159             switch (this.viewMode) {
18160                 case 'months':
18161                     this.viewMode = 1;
18162                     break;
18163                 case 'years':
18164                     this.viewMode = 2;
18165                     break;
18166                 default:
18167                     this.viewMode = 0;
18168                     break;
18169             }
18170         }
18171                 
18172         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18173         
18174 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18175         
18176         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18177         
18178         this.picker().on('mousedown', this.onMousedown, this);
18179         this.picker().on('click', this.onClick, this);
18180         
18181         this.picker().addClass('datepicker-dropdown');
18182         
18183         this.startViewMode = this.viewMode;
18184         
18185         if(this.singleMode){
18186             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18187                 v.setVisibilityMode(Roo.Element.DISPLAY);
18188                 v.hide();
18189             });
18190             
18191             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18192                 v.setStyle('width', '189px');
18193             });
18194         }
18195         
18196         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18197             if(!this.calendarWeeks){
18198                 v.remove();
18199                 return;
18200             }
18201             
18202             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18203             v.attr('colspan', function(i, val){
18204                 return parseInt(val) + 1;
18205             });
18206         });
18207                         
18208         
18209         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18210         
18211         this.setStartDate(this.startDate);
18212         this.setEndDate(this.endDate);
18213         
18214         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18215         
18216         this.fillDow();
18217         this.fillMonths();
18218         this.update();
18219         this.showMode();
18220         
18221         if(this.isInline) {
18222             this.show();
18223         }
18224     },
18225     
18226     picker : function()
18227     {
18228         return this.pickerEl;
18229 //        return this.el.select('.datepicker', true).first();
18230     },
18231     
18232     fillDow: function()
18233     {
18234         var dowCnt = this.weekStart;
18235         
18236         var dow = {
18237             tag: 'tr',
18238             cn: [
18239                 
18240             ]
18241         };
18242         
18243         if(this.calendarWeeks){
18244             dow.cn.push({
18245                 tag: 'th',
18246                 cls: 'cw',
18247                 html: '&nbsp;'
18248             })
18249         }
18250         
18251         while (dowCnt < this.weekStart + 7) {
18252             dow.cn.push({
18253                 tag: 'th',
18254                 cls: 'dow',
18255                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18256             });
18257         }
18258         
18259         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18260     },
18261     
18262     fillMonths: function()
18263     {    
18264         var i = 0;
18265         var months = this.picker().select('>.datepicker-months td', true).first();
18266         
18267         months.dom.innerHTML = '';
18268         
18269         while (i < 12) {
18270             var month = {
18271                 tag: 'span',
18272                 cls: 'month',
18273                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18274             };
18275             
18276             months.createChild(month);
18277         }
18278         
18279     },
18280     
18281     update: function()
18282     {
18283         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;
18284         
18285         if (this.date < this.startDate) {
18286             this.viewDate = new Date(this.startDate);
18287         } else if (this.date > this.endDate) {
18288             this.viewDate = new Date(this.endDate);
18289         } else {
18290             this.viewDate = new Date(this.date);
18291         }
18292         
18293         this.fill();
18294     },
18295     
18296     fill: function() 
18297     {
18298         var d = new Date(this.viewDate),
18299                 year = d.getUTCFullYear(),
18300                 month = d.getUTCMonth(),
18301                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18302                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18303                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18304                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18305                 currentDate = this.date && this.date.valueOf(),
18306                 today = this.UTCToday();
18307         
18308         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18309         
18310 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18311         
18312 //        this.picker.select('>tfoot th.today').
18313 //                                              .text(dates[this.language].today)
18314 //                                              .toggle(this.todayBtn !== false);
18315     
18316         this.updateNavArrows();
18317         this.fillMonths();
18318                                                 
18319         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18320         
18321         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18322          
18323         prevMonth.setUTCDate(day);
18324         
18325         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18326         
18327         var nextMonth = new Date(prevMonth);
18328         
18329         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18330         
18331         nextMonth = nextMonth.valueOf();
18332         
18333         var fillMonths = false;
18334         
18335         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18336         
18337         while(prevMonth.valueOf() < nextMonth) {
18338             var clsName = '';
18339             
18340             if (prevMonth.getUTCDay() === this.weekStart) {
18341                 if(fillMonths){
18342                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18343                 }
18344                     
18345                 fillMonths = {
18346                     tag: 'tr',
18347                     cn: []
18348                 };
18349                 
18350                 if(this.calendarWeeks){
18351                     // ISO 8601: First week contains first thursday.
18352                     // ISO also states week starts on Monday, but we can be more abstract here.
18353                     var
18354                     // Start of current week: based on weekstart/current date
18355                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18356                     // Thursday of this week
18357                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18358                     // First Thursday of year, year from thursday
18359                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18360                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18361                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18362                     
18363                     fillMonths.cn.push({
18364                         tag: 'td',
18365                         cls: 'cw',
18366                         html: calWeek
18367                     });
18368                 }
18369             }
18370             
18371             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18372                 clsName += ' old';
18373             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18374                 clsName += ' new';
18375             }
18376             if (this.todayHighlight &&
18377                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18378                 prevMonth.getUTCMonth() == today.getMonth() &&
18379                 prevMonth.getUTCDate() == today.getDate()) {
18380                 clsName += ' today';
18381             }
18382             
18383             if (currentDate && prevMonth.valueOf() === currentDate) {
18384                 clsName += ' active';
18385             }
18386             
18387             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18388                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18389                     clsName += ' disabled';
18390             }
18391             
18392             fillMonths.cn.push({
18393                 tag: 'td',
18394                 cls: 'day ' + clsName,
18395                 html: prevMonth.getDate()
18396             });
18397             
18398             prevMonth.setDate(prevMonth.getDate()+1);
18399         }
18400           
18401         var currentYear = this.date && this.date.getUTCFullYear();
18402         var currentMonth = this.date && this.date.getUTCMonth();
18403         
18404         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18405         
18406         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18407             v.removeClass('active');
18408             
18409             if(currentYear === year && k === currentMonth){
18410                 v.addClass('active');
18411             }
18412             
18413             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18414                 v.addClass('disabled');
18415             }
18416             
18417         });
18418         
18419         
18420         year = parseInt(year/10, 10) * 10;
18421         
18422         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18423         
18424         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18425         
18426         year -= 1;
18427         for (var i = -1; i < 11; i++) {
18428             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18429                 tag: 'span',
18430                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18431                 html: year
18432             });
18433             
18434             year += 1;
18435         }
18436     },
18437     
18438     showMode: function(dir) 
18439     {
18440         if (dir) {
18441             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18442         }
18443         
18444         Roo.each(this.picker().select('>div',true).elements, function(v){
18445             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18446             v.hide();
18447         });
18448         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18449     },
18450     
18451     place: function()
18452     {
18453         if(this.isInline) {
18454             return;
18455         }
18456         
18457         this.picker().removeClass(['bottom', 'top']);
18458         
18459         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18460             /*
18461              * place to the top of element!
18462              *
18463              */
18464             
18465             this.picker().addClass('top');
18466             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18467             
18468             return;
18469         }
18470         
18471         this.picker().addClass('bottom');
18472         
18473         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18474     },
18475     
18476     parseDate : function(value)
18477     {
18478         if(!value || value instanceof Date){
18479             return value;
18480         }
18481         var v = Date.parseDate(value, this.format);
18482         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18483             v = Date.parseDate(value, 'Y-m-d');
18484         }
18485         if(!v && this.altFormats){
18486             if(!this.altFormatsArray){
18487                 this.altFormatsArray = this.altFormats.split("|");
18488             }
18489             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18490                 v = Date.parseDate(value, this.altFormatsArray[i]);
18491             }
18492         }
18493         return v;
18494     },
18495     
18496     formatDate : function(date, fmt)
18497     {   
18498         return (!date || !(date instanceof Date)) ?
18499         date : date.dateFormat(fmt || this.format);
18500     },
18501     
18502     onFocus : function()
18503     {
18504         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18505         this.show();
18506     },
18507     
18508     onBlur : function()
18509     {
18510         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18511         
18512         var d = this.inputEl().getValue();
18513         
18514         this.setValue(d);
18515                 
18516         this.hide();
18517     },
18518     
18519     show : function()
18520     {
18521         this.picker().show();
18522         this.update();
18523         this.place();
18524         
18525         this.fireEvent('show', this, this.date);
18526     },
18527     
18528     hide : function()
18529     {
18530         if(this.isInline) {
18531             return;
18532         }
18533         this.picker().hide();
18534         this.viewMode = this.startViewMode;
18535         this.showMode();
18536         
18537         this.fireEvent('hide', this, this.date);
18538         
18539     },
18540     
18541     onMousedown: function(e)
18542     {
18543         e.stopPropagation();
18544         e.preventDefault();
18545     },
18546     
18547     keyup: function(e)
18548     {
18549         Roo.bootstrap.DateField.superclass.keyup.call(this);
18550         this.update();
18551     },
18552
18553     setValue: function(v)
18554     {
18555         if(this.fireEvent('beforeselect', this, v) !== false){
18556             var d = new Date(this.parseDate(v) ).clearTime();
18557         
18558             if(isNaN(d.getTime())){
18559                 this.date = this.viewDate = '';
18560                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18561                 return;
18562             }
18563
18564             v = this.formatDate(d);
18565
18566             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18567
18568             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18569
18570             this.update();
18571
18572             this.fireEvent('select', this, this.date);
18573         }
18574     },
18575     
18576     getValue: function()
18577     {
18578         return this.formatDate(this.date);
18579     },
18580     
18581     fireKey: function(e)
18582     {
18583         if (!this.picker().isVisible()){
18584             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18585                 this.show();
18586             }
18587             return;
18588         }
18589         
18590         var dateChanged = false,
18591         dir, day, month,
18592         newDate, newViewDate;
18593         
18594         switch(e.keyCode){
18595             case 27: // escape
18596                 this.hide();
18597                 e.preventDefault();
18598                 break;
18599             case 37: // left
18600             case 39: // right
18601                 if (!this.keyboardNavigation) {
18602                     break;
18603                 }
18604                 dir = e.keyCode == 37 ? -1 : 1;
18605                 
18606                 if (e.ctrlKey){
18607                     newDate = this.moveYear(this.date, dir);
18608                     newViewDate = this.moveYear(this.viewDate, dir);
18609                 } else if (e.shiftKey){
18610                     newDate = this.moveMonth(this.date, dir);
18611                     newViewDate = this.moveMonth(this.viewDate, dir);
18612                 } else {
18613                     newDate = new Date(this.date);
18614                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18615                     newViewDate = new Date(this.viewDate);
18616                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18617                 }
18618                 if (this.dateWithinRange(newDate)){
18619                     this.date = newDate;
18620                     this.viewDate = newViewDate;
18621                     this.setValue(this.formatDate(this.date));
18622 //                    this.update();
18623                     e.preventDefault();
18624                     dateChanged = true;
18625                 }
18626                 break;
18627             case 38: // up
18628             case 40: // down
18629                 if (!this.keyboardNavigation) {
18630                     break;
18631                 }
18632                 dir = e.keyCode == 38 ? -1 : 1;
18633                 if (e.ctrlKey){
18634                     newDate = this.moveYear(this.date, dir);
18635                     newViewDate = this.moveYear(this.viewDate, dir);
18636                 } else if (e.shiftKey){
18637                     newDate = this.moveMonth(this.date, dir);
18638                     newViewDate = this.moveMonth(this.viewDate, dir);
18639                 } else {
18640                     newDate = new Date(this.date);
18641                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18642                     newViewDate = new Date(this.viewDate);
18643                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18644                 }
18645                 if (this.dateWithinRange(newDate)){
18646                     this.date = newDate;
18647                     this.viewDate = newViewDate;
18648                     this.setValue(this.formatDate(this.date));
18649 //                    this.update();
18650                     e.preventDefault();
18651                     dateChanged = true;
18652                 }
18653                 break;
18654             case 13: // enter
18655                 this.setValue(this.formatDate(this.date));
18656                 this.hide();
18657                 e.preventDefault();
18658                 break;
18659             case 9: // tab
18660                 this.setValue(this.formatDate(this.date));
18661                 this.hide();
18662                 break;
18663             case 16: // shift
18664             case 17: // ctrl
18665             case 18: // alt
18666                 break;
18667             default :
18668                 this.hide();
18669                 
18670         }
18671     },
18672     
18673     
18674     onClick: function(e) 
18675     {
18676         e.stopPropagation();
18677         e.preventDefault();
18678         
18679         var target = e.getTarget();
18680         
18681         if(target.nodeName.toLowerCase() === 'i'){
18682             target = Roo.get(target).dom.parentNode;
18683         }
18684         
18685         var nodeName = target.nodeName;
18686         var className = target.className;
18687         var html = target.innerHTML;
18688         //Roo.log(nodeName);
18689         
18690         switch(nodeName.toLowerCase()) {
18691             case 'th':
18692                 switch(className) {
18693                     case 'switch':
18694                         this.showMode(1);
18695                         break;
18696                     case 'prev':
18697                     case 'next':
18698                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18699                         switch(this.viewMode){
18700                                 case 0:
18701                                         this.viewDate = this.moveMonth(this.viewDate, dir);
18702                                         break;
18703                                 case 1:
18704                                 case 2:
18705                                         this.viewDate = this.moveYear(this.viewDate, dir);
18706                                         break;
18707                         }
18708                         this.fill();
18709                         break;
18710                     case 'today':
18711                         var date = new Date();
18712                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18713 //                        this.fill()
18714                         this.setValue(this.formatDate(this.date));
18715                         
18716                         this.hide();
18717                         break;
18718                 }
18719                 break;
18720             case 'span':
18721                 if (className.indexOf('disabled') < 0) {
18722                     this.viewDate.setUTCDate(1);
18723                     if (className.indexOf('month') > -1) {
18724                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18725                     } else {
18726                         var year = parseInt(html, 10) || 0;
18727                         this.viewDate.setUTCFullYear(year);
18728                         
18729                     }
18730                     
18731                     if(this.singleMode){
18732                         this.setValue(this.formatDate(this.viewDate));
18733                         this.hide();
18734                         return;
18735                     }
18736                     
18737                     this.showMode(-1);
18738                     this.fill();
18739                 }
18740                 break;
18741                 
18742             case 'td':
18743                 //Roo.log(className);
18744                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18745                     var day = parseInt(html, 10) || 1;
18746                     var year = this.viewDate.getUTCFullYear(),
18747                         month = this.viewDate.getUTCMonth();
18748
18749                     if (className.indexOf('old') > -1) {
18750                         if(month === 0 ){
18751                             month = 11;
18752                             year -= 1;
18753                         }else{
18754                             month -= 1;
18755                         }
18756                     } else if (className.indexOf('new') > -1) {
18757                         if (month == 11) {
18758                             month = 0;
18759                             year += 1;
18760                         } else {
18761                             month += 1;
18762                         }
18763                     }
18764                     //Roo.log([year,month,day]);
18765                     this.date = this.UTCDate(year, month, day,0,0,0,0);
18766                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18767 //                    this.fill();
18768                     //Roo.log(this.formatDate(this.date));
18769                     this.setValue(this.formatDate(this.date));
18770                     this.hide();
18771                 }
18772                 break;
18773         }
18774     },
18775     
18776     setStartDate: function(startDate)
18777     {
18778         this.startDate = startDate || -Infinity;
18779         if (this.startDate !== -Infinity) {
18780             this.startDate = this.parseDate(this.startDate);
18781         }
18782         this.update();
18783         this.updateNavArrows();
18784     },
18785
18786     setEndDate: function(endDate)
18787     {
18788         this.endDate = endDate || Infinity;
18789         if (this.endDate !== Infinity) {
18790             this.endDate = this.parseDate(this.endDate);
18791         }
18792         this.update();
18793         this.updateNavArrows();
18794     },
18795     
18796     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18797     {
18798         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18799         if (typeof(this.daysOfWeekDisabled) !== 'object') {
18800             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18801         }
18802         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18803             return parseInt(d, 10);
18804         });
18805         this.update();
18806         this.updateNavArrows();
18807     },
18808     
18809     updateNavArrows: function() 
18810     {
18811         if(this.singleMode){
18812             return;
18813         }
18814         
18815         var d = new Date(this.viewDate),
18816         year = d.getUTCFullYear(),
18817         month = d.getUTCMonth();
18818         
18819         Roo.each(this.picker().select('.prev', true).elements, function(v){
18820             v.show();
18821             switch (this.viewMode) {
18822                 case 0:
18823
18824                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18825                         v.hide();
18826                     }
18827                     break;
18828                 case 1:
18829                 case 2:
18830                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18831                         v.hide();
18832                     }
18833                     break;
18834             }
18835         });
18836         
18837         Roo.each(this.picker().select('.next', true).elements, function(v){
18838             v.show();
18839             switch (this.viewMode) {
18840                 case 0:
18841
18842                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18843                         v.hide();
18844                     }
18845                     break;
18846                 case 1:
18847                 case 2:
18848                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18849                         v.hide();
18850                     }
18851                     break;
18852             }
18853         })
18854     },
18855     
18856     moveMonth: function(date, dir)
18857     {
18858         if (!dir) {
18859             return date;
18860         }
18861         var new_date = new Date(date.valueOf()),
18862         day = new_date.getUTCDate(),
18863         month = new_date.getUTCMonth(),
18864         mag = Math.abs(dir),
18865         new_month, test;
18866         dir = dir > 0 ? 1 : -1;
18867         if (mag == 1){
18868             test = dir == -1
18869             // If going back one month, make sure month is not current month
18870             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18871             ? function(){
18872                 return new_date.getUTCMonth() == month;
18873             }
18874             // If going forward one month, make sure month is as expected
18875             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18876             : function(){
18877                 return new_date.getUTCMonth() != new_month;
18878             };
18879             new_month = month + dir;
18880             new_date.setUTCMonth(new_month);
18881             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18882             if (new_month < 0 || new_month > 11) {
18883                 new_month = (new_month + 12) % 12;
18884             }
18885         } else {
18886             // For magnitudes >1, move one month at a time...
18887             for (var i=0; i<mag; i++) {
18888                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18889                 new_date = this.moveMonth(new_date, dir);
18890             }
18891             // ...then reset the day, keeping it in the new month
18892             new_month = new_date.getUTCMonth();
18893             new_date.setUTCDate(day);
18894             test = function(){
18895                 return new_month != new_date.getUTCMonth();
18896             };
18897         }
18898         // Common date-resetting loop -- if date is beyond end of month, make it
18899         // end of month
18900         while (test()){
18901             new_date.setUTCDate(--day);
18902             new_date.setUTCMonth(new_month);
18903         }
18904         return new_date;
18905     },
18906
18907     moveYear: function(date, dir)
18908     {
18909         return this.moveMonth(date, dir*12);
18910     },
18911
18912     dateWithinRange: function(date)
18913     {
18914         return date >= this.startDate && date <= this.endDate;
18915     },
18916
18917     
18918     remove: function() 
18919     {
18920         this.picker().remove();
18921     },
18922     
18923     validateValue : function(value)
18924     {
18925         if(value.length < 1)  {
18926             if(this.allowBlank){
18927                 return true;
18928             }
18929             return false;
18930         }
18931         
18932         if(value.length < this.minLength){
18933             return false;
18934         }
18935         if(value.length > this.maxLength){
18936             return false;
18937         }
18938         if(this.vtype){
18939             var vt = Roo.form.VTypes;
18940             if(!vt[this.vtype](value, this)){
18941                 return false;
18942             }
18943         }
18944         if(typeof this.validator == "function"){
18945             var msg = this.validator(value);
18946             if(msg !== true){
18947                 return false;
18948             }
18949         }
18950         
18951         if(this.regex && !this.regex.test(value)){
18952             return false;
18953         }
18954         
18955         if(typeof(this.parseDate(value)) == 'undefined'){
18956             return false;
18957         }
18958         
18959         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
18960             return false;
18961         }      
18962         
18963         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
18964             return false;
18965         } 
18966         
18967         
18968         return true;
18969     }
18970    
18971 });
18972
18973 Roo.apply(Roo.bootstrap.DateField,  {
18974     
18975     head : {
18976         tag: 'thead',
18977         cn: [
18978         {
18979             tag: 'tr',
18980             cn: [
18981             {
18982                 tag: 'th',
18983                 cls: 'prev',
18984                 html: '<i class="fa fa-arrow-left"/>'
18985             },
18986             {
18987                 tag: 'th',
18988                 cls: 'switch',
18989                 colspan: '5'
18990             },
18991             {
18992                 tag: 'th',
18993                 cls: 'next',
18994                 html: '<i class="fa fa-arrow-right"/>'
18995             }
18996
18997             ]
18998         }
18999         ]
19000     },
19001     
19002     content : {
19003         tag: 'tbody',
19004         cn: [
19005         {
19006             tag: 'tr',
19007             cn: [
19008             {
19009                 tag: 'td',
19010                 colspan: '7'
19011             }
19012             ]
19013         }
19014         ]
19015     },
19016     
19017     footer : {
19018         tag: 'tfoot',
19019         cn: [
19020         {
19021             tag: 'tr',
19022             cn: [
19023             {
19024                 tag: 'th',
19025                 colspan: '7',
19026                 cls: 'today'
19027             }
19028                     
19029             ]
19030         }
19031         ]
19032     },
19033     
19034     dates:{
19035         en: {
19036             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19037             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19038             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19039             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19040             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19041             today: "Today"
19042         }
19043     },
19044     
19045     modes: [
19046     {
19047         clsName: 'days',
19048         navFnc: 'Month',
19049         navStep: 1
19050     },
19051     {
19052         clsName: 'months',
19053         navFnc: 'FullYear',
19054         navStep: 1
19055     },
19056     {
19057         clsName: 'years',
19058         navFnc: 'FullYear',
19059         navStep: 10
19060     }]
19061 });
19062
19063 Roo.apply(Roo.bootstrap.DateField,  {
19064   
19065     template : {
19066         tag: 'div',
19067         cls: 'datepicker dropdown-menu roo-dynamic',
19068         cn: [
19069         {
19070             tag: 'div',
19071             cls: 'datepicker-days',
19072             cn: [
19073             {
19074                 tag: 'table',
19075                 cls: 'table-condensed',
19076                 cn:[
19077                 Roo.bootstrap.DateField.head,
19078                 {
19079                     tag: 'tbody'
19080                 },
19081                 Roo.bootstrap.DateField.footer
19082                 ]
19083             }
19084             ]
19085         },
19086         {
19087             tag: 'div',
19088             cls: 'datepicker-months',
19089             cn: [
19090             {
19091                 tag: 'table',
19092                 cls: 'table-condensed',
19093                 cn:[
19094                 Roo.bootstrap.DateField.head,
19095                 Roo.bootstrap.DateField.content,
19096                 Roo.bootstrap.DateField.footer
19097                 ]
19098             }
19099             ]
19100         },
19101         {
19102             tag: 'div',
19103             cls: 'datepicker-years',
19104             cn: [
19105             {
19106                 tag: 'table',
19107                 cls: 'table-condensed',
19108                 cn:[
19109                 Roo.bootstrap.DateField.head,
19110                 Roo.bootstrap.DateField.content,
19111                 Roo.bootstrap.DateField.footer
19112                 ]
19113             }
19114             ]
19115         }
19116         ]
19117     }
19118 });
19119
19120  
19121
19122  /*
19123  * - LGPL
19124  *
19125  * TimeField
19126  * 
19127  */
19128
19129 /**
19130  * @class Roo.bootstrap.TimeField
19131  * @extends Roo.bootstrap.Input
19132  * Bootstrap DateField class
19133  * 
19134  * 
19135  * @constructor
19136  * Create a new TimeField
19137  * @param {Object} config The config object
19138  */
19139
19140 Roo.bootstrap.TimeField = function(config){
19141     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19142     this.addEvents({
19143             /**
19144              * @event show
19145              * Fires when this field show.
19146              * @param {Roo.bootstrap.DateField} thisthis
19147              * @param {Mixed} date The date value
19148              */
19149             show : true,
19150             /**
19151              * @event show
19152              * Fires when this field hide.
19153              * @param {Roo.bootstrap.DateField} this
19154              * @param {Mixed} date The date value
19155              */
19156             hide : true,
19157             /**
19158              * @event select
19159              * Fires when select a date.
19160              * @param {Roo.bootstrap.DateField} this
19161              * @param {Mixed} date The date value
19162              */
19163             select : true
19164         });
19165 };
19166
19167 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19168     
19169     /**
19170      * @cfg {String} format
19171      * The default time format string which can be overriden for localization support.  The format must be
19172      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19173      */
19174     format : "H:i",
19175        
19176     onRender: function(ct, position)
19177     {
19178         
19179         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19180                 
19181         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19182         
19183         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19184         
19185         this.pop = this.picker().select('>.datepicker-time',true).first();
19186         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19187         
19188         this.picker().on('mousedown', this.onMousedown, this);
19189         this.picker().on('click', this.onClick, this);
19190         
19191         this.picker().addClass('datepicker-dropdown');
19192     
19193         this.fillTime();
19194         this.update();
19195             
19196         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19197         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19198         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19199         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19200         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19201         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19202
19203     },
19204     
19205     fireKey: function(e){
19206         if (!this.picker().isVisible()){
19207             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19208                 this.show();
19209             }
19210             return;
19211         }
19212
19213         e.preventDefault();
19214         
19215         switch(e.keyCode){
19216             case 27: // escape
19217                 this.hide();
19218                 break;
19219             case 37: // left
19220             case 39: // right
19221                 this.onTogglePeriod();
19222                 break;
19223             case 38: // up
19224                 this.onIncrementMinutes();
19225                 break;
19226             case 40: // down
19227                 this.onDecrementMinutes();
19228                 break;
19229             case 13: // enter
19230             case 9: // tab
19231                 this.setTime();
19232                 break;
19233         }
19234     },
19235     
19236     onClick: function(e) {
19237         e.stopPropagation();
19238         e.preventDefault();
19239     },
19240     
19241     picker : function()
19242     {
19243         return this.el.select('.datepicker', true).first();
19244     },
19245     
19246     fillTime: function()
19247     {    
19248         var time = this.pop.select('tbody', true).first();
19249         
19250         time.dom.innerHTML = '';
19251         
19252         time.createChild({
19253             tag: 'tr',
19254             cn: [
19255                 {
19256                     tag: 'td',
19257                     cn: [
19258                         {
19259                             tag: 'a',
19260                             href: '#',
19261                             cls: 'btn',
19262                             cn: [
19263                                 {
19264                                     tag: 'span',
19265                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19266                                 }
19267                             ]
19268                         } 
19269                     ]
19270                 },
19271                 {
19272                     tag: 'td',
19273                     cls: 'separator'
19274                 },
19275                 {
19276                     tag: 'td',
19277                     cn: [
19278                         {
19279                             tag: 'a',
19280                             href: '#',
19281                             cls: 'btn',
19282                             cn: [
19283                                 {
19284                                     tag: 'span',
19285                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19286                                 }
19287                             ]
19288                         }
19289                     ]
19290                 },
19291                 {
19292                     tag: 'td',
19293                     cls: 'separator'
19294                 }
19295             ]
19296         });
19297         
19298         time.createChild({
19299             tag: 'tr',
19300             cn: [
19301                 {
19302                     tag: 'td',
19303                     cn: [
19304                         {
19305                             tag: 'span',
19306                             cls: 'timepicker-hour',
19307                             html: '00'
19308                         }  
19309                     ]
19310                 },
19311                 {
19312                     tag: 'td',
19313                     cls: 'separator',
19314                     html: ':'
19315                 },
19316                 {
19317                     tag: 'td',
19318                     cn: [
19319                         {
19320                             tag: 'span',
19321                             cls: 'timepicker-minute',
19322                             html: '00'
19323                         }  
19324                     ]
19325                 },
19326                 {
19327                     tag: 'td',
19328                     cls: 'separator'
19329                 },
19330                 {
19331                     tag: 'td',
19332                     cn: [
19333                         {
19334                             tag: 'button',
19335                             type: 'button',
19336                             cls: 'btn btn-primary period',
19337                             html: 'AM'
19338                             
19339                         }
19340                     ]
19341                 }
19342             ]
19343         });
19344         
19345         time.createChild({
19346             tag: 'tr',
19347             cn: [
19348                 {
19349                     tag: 'td',
19350                     cn: [
19351                         {
19352                             tag: 'a',
19353                             href: '#',
19354                             cls: 'btn',
19355                             cn: [
19356                                 {
19357                                     tag: 'span',
19358                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19359                                 }
19360                             ]
19361                         }
19362                     ]
19363                 },
19364                 {
19365                     tag: 'td',
19366                     cls: 'separator'
19367                 },
19368                 {
19369                     tag: 'td',
19370                     cn: [
19371                         {
19372                             tag: 'a',
19373                             href: '#',
19374                             cls: 'btn',
19375                             cn: [
19376                                 {
19377                                     tag: 'span',
19378                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19379                                 }
19380                             ]
19381                         }
19382                     ]
19383                 },
19384                 {
19385                     tag: 'td',
19386                     cls: 'separator'
19387                 }
19388             ]
19389         });
19390         
19391     },
19392     
19393     update: function()
19394     {
19395         
19396         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19397         
19398         this.fill();
19399     },
19400     
19401     fill: function() 
19402     {
19403         var hours = this.time.getHours();
19404         var minutes = this.time.getMinutes();
19405         var period = 'AM';
19406         
19407         if(hours > 11){
19408             period = 'PM';
19409         }
19410         
19411         if(hours == 0){
19412             hours = 12;
19413         }
19414         
19415         
19416         if(hours > 12){
19417             hours = hours - 12;
19418         }
19419         
19420         if(hours < 10){
19421             hours = '0' + hours;
19422         }
19423         
19424         if(minutes < 10){
19425             minutes = '0' + minutes;
19426         }
19427         
19428         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19429         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19430         this.pop.select('button', true).first().dom.innerHTML = period;
19431         
19432     },
19433     
19434     place: function()
19435     {   
19436         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19437         
19438         var cls = ['bottom'];
19439         
19440         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19441             cls.pop();
19442             cls.push('top');
19443         }
19444         
19445         cls.push('right');
19446         
19447         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19448             cls.pop();
19449             cls.push('left');
19450         }
19451         
19452         this.picker().addClass(cls.join('-'));
19453         
19454         var _this = this;
19455         
19456         Roo.each(cls, function(c){
19457             if(c == 'bottom'){
19458                 _this.picker().setTop(_this.inputEl().getHeight());
19459                 return;
19460             }
19461             if(c == 'top'){
19462                 _this.picker().setTop(0 - _this.picker().getHeight());
19463                 return;
19464             }
19465             
19466             if(c == 'left'){
19467                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19468                 return;
19469             }
19470             if(c == 'right'){
19471                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19472                 return;
19473             }
19474         });
19475         
19476     },
19477   
19478     onFocus : function()
19479     {
19480         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19481         this.show();
19482     },
19483     
19484     onBlur : function()
19485     {
19486         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19487         this.hide();
19488     },
19489     
19490     show : function()
19491     {
19492         this.picker().show();
19493         this.pop.show();
19494         this.update();
19495         this.place();
19496         
19497         this.fireEvent('show', this, this.date);
19498     },
19499     
19500     hide : function()
19501     {
19502         this.picker().hide();
19503         this.pop.hide();
19504         
19505         this.fireEvent('hide', this, this.date);
19506     },
19507     
19508     setTime : function()
19509     {
19510         this.hide();
19511         this.setValue(this.time.format(this.format));
19512         
19513         this.fireEvent('select', this, this.date);
19514         
19515         
19516     },
19517     
19518     onMousedown: function(e){
19519         e.stopPropagation();
19520         e.preventDefault();
19521     },
19522     
19523     onIncrementHours: function()
19524     {
19525         Roo.log('onIncrementHours');
19526         this.time = this.time.add(Date.HOUR, 1);
19527         this.update();
19528         
19529     },
19530     
19531     onDecrementHours: function()
19532     {
19533         Roo.log('onDecrementHours');
19534         this.time = this.time.add(Date.HOUR, -1);
19535         this.update();
19536     },
19537     
19538     onIncrementMinutes: function()
19539     {
19540         Roo.log('onIncrementMinutes');
19541         this.time = this.time.add(Date.MINUTE, 1);
19542         this.update();
19543     },
19544     
19545     onDecrementMinutes: function()
19546     {
19547         Roo.log('onDecrementMinutes');
19548         this.time = this.time.add(Date.MINUTE, -1);
19549         this.update();
19550     },
19551     
19552     onTogglePeriod: function()
19553     {
19554         Roo.log('onTogglePeriod');
19555         this.time = this.time.add(Date.HOUR, 12);
19556         this.update();
19557     }
19558     
19559    
19560 });
19561
19562 Roo.apply(Roo.bootstrap.TimeField,  {
19563     
19564     content : {
19565         tag: 'tbody',
19566         cn: [
19567             {
19568                 tag: 'tr',
19569                 cn: [
19570                 {
19571                     tag: 'td',
19572                     colspan: '7'
19573                 }
19574                 ]
19575             }
19576         ]
19577     },
19578     
19579     footer : {
19580         tag: 'tfoot',
19581         cn: [
19582             {
19583                 tag: 'tr',
19584                 cn: [
19585                 {
19586                     tag: 'th',
19587                     colspan: '7',
19588                     cls: '',
19589                     cn: [
19590                         {
19591                             tag: 'button',
19592                             cls: 'btn btn-info ok',
19593                             html: 'OK'
19594                         }
19595                     ]
19596                 }
19597
19598                 ]
19599             }
19600         ]
19601     }
19602 });
19603
19604 Roo.apply(Roo.bootstrap.TimeField,  {
19605   
19606     template : {
19607         tag: 'div',
19608         cls: 'datepicker dropdown-menu',
19609         cn: [
19610             {
19611                 tag: 'div',
19612                 cls: 'datepicker-time',
19613                 cn: [
19614                 {
19615                     tag: 'table',
19616                     cls: 'table-condensed',
19617                     cn:[
19618                     Roo.bootstrap.TimeField.content,
19619                     Roo.bootstrap.TimeField.footer
19620                     ]
19621                 }
19622                 ]
19623             }
19624         ]
19625     }
19626 });
19627
19628  
19629
19630  /*
19631  * - LGPL
19632  *
19633  * MonthField
19634  * 
19635  */
19636
19637 /**
19638  * @class Roo.bootstrap.MonthField
19639  * @extends Roo.bootstrap.Input
19640  * Bootstrap MonthField class
19641  * 
19642  * @cfg {String} language default en
19643  * 
19644  * @constructor
19645  * Create a new MonthField
19646  * @param {Object} config The config object
19647  */
19648
19649 Roo.bootstrap.MonthField = function(config){
19650     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19651     
19652     this.addEvents({
19653         /**
19654          * @event show
19655          * Fires when this field show.
19656          * @param {Roo.bootstrap.MonthField} this
19657          * @param {Mixed} date The date value
19658          */
19659         show : true,
19660         /**
19661          * @event show
19662          * Fires when this field hide.
19663          * @param {Roo.bootstrap.MonthField} this
19664          * @param {Mixed} date The date value
19665          */
19666         hide : true,
19667         /**
19668          * @event select
19669          * Fires when select a date.
19670          * @param {Roo.bootstrap.MonthField} this
19671          * @param {String} oldvalue The old value
19672          * @param {String} newvalue The new value
19673          */
19674         select : true
19675     });
19676 };
19677
19678 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
19679     
19680     onRender: function(ct, position)
19681     {
19682         
19683         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19684         
19685         this.language = this.language || 'en';
19686         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19687         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19688         
19689         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19690         this.isInline = false;
19691         this.isInput = true;
19692         this.component = this.el.select('.add-on', true).first() || false;
19693         this.component = (this.component && this.component.length === 0) ? false : this.component;
19694         this.hasInput = this.component && this.inputEL().length;
19695         
19696         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19697         
19698         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19699         
19700         this.picker().on('mousedown', this.onMousedown, this);
19701         this.picker().on('click', this.onClick, this);
19702         
19703         this.picker().addClass('datepicker-dropdown');
19704         
19705         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19706             v.setStyle('width', '189px');
19707         });
19708         
19709         this.fillMonths();
19710         
19711         this.update();
19712         
19713         if(this.isInline) {
19714             this.show();
19715         }
19716         
19717     },
19718     
19719     setValue: function(v, suppressEvent)
19720     {   
19721         var o = this.getValue();
19722         
19723         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19724         
19725         this.update();
19726
19727         if(suppressEvent !== true){
19728             this.fireEvent('select', this, o, v);
19729         }
19730         
19731     },
19732     
19733     getValue: function()
19734     {
19735         return this.value;
19736     },
19737     
19738     onClick: function(e) 
19739     {
19740         e.stopPropagation();
19741         e.preventDefault();
19742         
19743         var target = e.getTarget();
19744         
19745         if(target.nodeName.toLowerCase() === 'i'){
19746             target = Roo.get(target).dom.parentNode;
19747         }
19748         
19749         var nodeName = target.nodeName;
19750         var className = target.className;
19751         var html = target.innerHTML;
19752         
19753         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19754             return;
19755         }
19756         
19757         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19758         
19759         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19760         
19761         this.hide();
19762                         
19763     },
19764     
19765     picker : function()
19766     {
19767         return this.pickerEl;
19768     },
19769     
19770     fillMonths: function()
19771     {    
19772         var i = 0;
19773         var months = this.picker().select('>.datepicker-months td', true).first();
19774         
19775         months.dom.innerHTML = '';
19776         
19777         while (i < 12) {
19778             var month = {
19779                 tag: 'span',
19780                 cls: 'month',
19781                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19782             };
19783             
19784             months.createChild(month);
19785         }
19786         
19787     },
19788     
19789     update: function()
19790     {
19791         var _this = this;
19792         
19793         if(typeof(this.vIndex) == 'undefined' && this.value.length){
19794             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19795         }
19796         
19797         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19798             e.removeClass('active');
19799             
19800             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19801                 e.addClass('active');
19802             }
19803         })
19804     },
19805     
19806     place: function()
19807     {
19808         if(this.isInline) {
19809             return;
19810         }
19811         
19812         this.picker().removeClass(['bottom', 'top']);
19813         
19814         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19815             /*
19816              * place to the top of element!
19817              *
19818              */
19819             
19820             this.picker().addClass('top');
19821             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19822             
19823             return;
19824         }
19825         
19826         this.picker().addClass('bottom');
19827         
19828         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19829     },
19830     
19831     onFocus : function()
19832     {
19833         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19834         this.show();
19835     },
19836     
19837     onBlur : function()
19838     {
19839         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19840         
19841         var d = this.inputEl().getValue();
19842         
19843         this.setValue(d);
19844                 
19845         this.hide();
19846     },
19847     
19848     show : function()
19849     {
19850         this.picker().show();
19851         this.picker().select('>.datepicker-months', true).first().show();
19852         this.update();
19853         this.place();
19854         
19855         this.fireEvent('show', this, this.date);
19856     },
19857     
19858     hide : function()
19859     {
19860         if(this.isInline) {
19861             return;
19862         }
19863         this.picker().hide();
19864         this.fireEvent('hide', this, this.date);
19865         
19866     },
19867     
19868     onMousedown: function(e)
19869     {
19870         e.stopPropagation();
19871         e.preventDefault();
19872     },
19873     
19874     keyup: function(e)
19875     {
19876         Roo.bootstrap.MonthField.superclass.keyup.call(this);
19877         this.update();
19878     },
19879
19880     fireKey: function(e)
19881     {
19882         if (!this.picker().isVisible()){
19883             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
19884                 this.show();
19885             }
19886             return;
19887         }
19888         
19889         var dir;
19890         
19891         switch(e.keyCode){
19892             case 27: // escape
19893                 this.hide();
19894                 e.preventDefault();
19895                 break;
19896             case 37: // left
19897             case 39: // right
19898                 dir = e.keyCode == 37 ? -1 : 1;
19899                 
19900                 this.vIndex = this.vIndex + dir;
19901                 
19902                 if(this.vIndex < 0){
19903                     this.vIndex = 0;
19904                 }
19905                 
19906                 if(this.vIndex > 11){
19907                     this.vIndex = 11;
19908                 }
19909                 
19910                 if(isNaN(this.vIndex)){
19911                     this.vIndex = 0;
19912                 }
19913                 
19914                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19915                 
19916                 break;
19917             case 38: // up
19918             case 40: // down
19919                 
19920                 dir = e.keyCode == 38 ? -1 : 1;
19921                 
19922                 this.vIndex = this.vIndex + dir * 4;
19923                 
19924                 if(this.vIndex < 0){
19925                     this.vIndex = 0;
19926                 }
19927                 
19928                 if(this.vIndex > 11){
19929                     this.vIndex = 11;
19930                 }
19931                 
19932                 if(isNaN(this.vIndex)){
19933                     this.vIndex = 0;
19934                 }
19935                 
19936                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19937                 break;
19938                 
19939             case 13: // enter
19940                 
19941                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19942                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19943                 }
19944                 
19945                 this.hide();
19946                 e.preventDefault();
19947                 break;
19948             case 9: // tab
19949                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19950                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19951                 }
19952                 this.hide();
19953                 break;
19954             case 16: // shift
19955             case 17: // ctrl
19956             case 18: // alt
19957                 break;
19958             default :
19959                 this.hide();
19960                 
19961         }
19962     },
19963     
19964     remove: function() 
19965     {
19966         this.picker().remove();
19967     }
19968    
19969 });
19970
19971 Roo.apply(Roo.bootstrap.MonthField,  {
19972     
19973     content : {
19974         tag: 'tbody',
19975         cn: [
19976         {
19977             tag: 'tr',
19978             cn: [
19979             {
19980                 tag: 'td',
19981                 colspan: '7'
19982             }
19983             ]
19984         }
19985         ]
19986     },
19987     
19988     dates:{
19989         en: {
19990             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19991             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
19992         }
19993     }
19994 });
19995
19996 Roo.apply(Roo.bootstrap.MonthField,  {
19997   
19998     template : {
19999         tag: 'div',
20000         cls: 'datepicker dropdown-menu roo-dynamic',
20001         cn: [
20002             {
20003                 tag: 'div',
20004                 cls: 'datepicker-months',
20005                 cn: [
20006                 {
20007                     tag: 'table',
20008                     cls: 'table-condensed',
20009                     cn:[
20010                         Roo.bootstrap.DateField.content
20011                     ]
20012                 }
20013                 ]
20014             }
20015         ]
20016     }
20017 });
20018
20019  
20020
20021  
20022  /*
20023  * - LGPL
20024  *
20025  * CheckBox
20026  * 
20027  */
20028
20029 /**
20030  * @class Roo.bootstrap.CheckBox
20031  * @extends Roo.bootstrap.Input
20032  * Bootstrap CheckBox class
20033  * 
20034  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20035  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20036  * @cfg {String} boxLabel The text that appears beside the checkbox
20037  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20038  * @cfg {Boolean} checked initnal the element
20039  * @cfg {Boolean} inline inline the element (default false)
20040  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20041  * 
20042  * @constructor
20043  * Create a new CheckBox
20044  * @param {Object} config The config object
20045  */
20046
20047 Roo.bootstrap.CheckBox = function(config){
20048     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20049    
20050     this.addEvents({
20051         /**
20052         * @event check
20053         * Fires when the element is checked or unchecked.
20054         * @param {Roo.bootstrap.CheckBox} this This input
20055         * @param {Boolean} checked The new checked value
20056         */
20057        check : true
20058     });
20059     
20060 };
20061
20062 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20063   
20064     inputType: 'checkbox',
20065     inputValue: 1,
20066     valueOff: 0,
20067     boxLabel: false,
20068     checked: false,
20069     weight : false,
20070     inline: false,
20071     
20072     getAutoCreate : function()
20073     {
20074         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20075         
20076         var id = Roo.id();
20077         
20078         var cfg = {};
20079         
20080         cfg.cls = 'form-group ' + this.inputType; //input-group
20081         
20082         if(this.inline){
20083             cfg.cls += ' ' + this.inputType + '-inline';
20084         }
20085         
20086         var input =  {
20087             tag: 'input',
20088             id : id,
20089             type : this.inputType,
20090             value : this.inputValue,
20091             cls : 'roo-' + this.inputType, //'form-box',
20092             placeholder : this.placeholder || ''
20093             
20094         };
20095         
20096         if(this.inputType != 'radio'){
20097             var hidden =  {
20098                 tag: 'input',
20099                 type : 'hidden',
20100                 cls : 'roo-hidden-value',
20101                 value : this.checked ? this.valueOff : this.inputValue
20102             };
20103         }
20104         
20105             
20106         if (this.weight) { // Validity check?
20107             cfg.cls += " " + this.inputType + "-" + this.weight;
20108         }
20109         
20110         if (this.disabled) {
20111             input.disabled=true;
20112         }
20113         
20114         if(this.checked){
20115             input.checked = this.checked;
20116             
20117         }
20118         
20119         
20120         if (this.name) {
20121             
20122             input.name = this.name;
20123             
20124             if(this.inputType != 'radio'){
20125                 hidden.name = this.name;
20126                 input.name = '_hidden_' + this.name;
20127             }
20128         }
20129         
20130         if (this.size) {
20131             input.cls += ' input-' + this.size;
20132         }
20133         
20134         var settings=this;
20135         
20136         ['xs','sm','md','lg'].map(function(size){
20137             if (settings[size]) {
20138                 cfg.cls += ' col-' + size + '-' + settings[size];
20139             }
20140         });
20141         
20142         var inputblock = input;
20143          
20144         if (this.before || this.after) {
20145             
20146             inputblock = {
20147                 cls : 'input-group',
20148                 cn :  [] 
20149             };
20150             
20151             if (this.before) {
20152                 inputblock.cn.push({
20153                     tag :'span',
20154                     cls : 'input-group-addon',
20155                     html : this.before
20156                 });
20157             }
20158             
20159             inputblock.cn.push(input);
20160             
20161             if(this.inputType != 'radio'){
20162                 inputblock.cn.push(hidden);
20163             }
20164             
20165             if (this.after) {
20166                 inputblock.cn.push({
20167                     tag :'span',
20168                     cls : 'input-group-addon',
20169                     html : this.after
20170                 });
20171             }
20172             
20173         }
20174         
20175         if (align ==='left' && this.fieldLabel.length) {
20176 //                Roo.log("left and has label");
20177             cfg.cn = [
20178                 {
20179                     tag: 'label',
20180                     'for' :  id,
20181                     cls : 'control-label',
20182                     html : this.fieldLabel
20183
20184                 },
20185                 {
20186                     cls : "", 
20187                     cn: [
20188                         inputblock
20189                     ]
20190                 }
20191             ];
20192             
20193             if(this.labelWidth > 12){
20194                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20195             }
20196             
20197             if(this.labelWidth < 13 && this.labelmd == 0){
20198                 this.labelmd = this.labelWidth;
20199             }
20200             
20201             if(this.labellg > 0){
20202                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20203                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20204             }
20205             
20206             if(this.labelmd > 0){
20207                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20208                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20209             }
20210             
20211             if(this.labelsm > 0){
20212                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20213                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20214             }
20215             
20216             if(this.labelxs > 0){
20217                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20218                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20219             }
20220             
20221         } else if ( this.fieldLabel.length) {
20222 //                Roo.log(" label");
20223                 cfg.cn = [
20224                    
20225                     {
20226                         tag: this.boxLabel ? 'span' : 'label',
20227                         'for': id,
20228                         cls: 'control-label box-input-label',
20229                         //cls : 'input-group-addon',
20230                         html : this.fieldLabel
20231                         
20232                     },
20233                     
20234                     inputblock
20235                     
20236                 ];
20237
20238         } else {
20239             
20240 //                Roo.log(" no label && no align");
20241                 cfg.cn = [  inputblock ] ;
20242                 
20243                 
20244         }
20245         
20246         if(this.boxLabel){
20247              var boxLabelCfg = {
20248                 tag: 'label',
20249                 //'for': id, // box label is handled by onclick - so no for...
20250                 cls: 'box-label',
20251                 html: this.boxLabel
20252             };
20253             
20254             if(this.tooltip){
20255                 boxLabelCfg.tooltip = this.tooltip;
20256             }
20257              
20258             cfg.cn.push(boxLabelCfg);
20259         }
20260         
20261         if(this.inputType != 'radio'){
20262             cfg.cn.push(hidden);
20263         }
20264         
20265         return cfg;
20266         
20267     },
20268     
20269     /**
20270      * return the real input element.
20271      */
20272     inputEl: function ()
20273     {
20274         return this.el.select('input.roo-' + this.inputType,true).first();
20275     },
20276     hiddenEl: function ()
20277     {
20278         return this.el.select('input.roo-hidden-value',true).first();
20279     },
20280     
20281     labelEl: function()
20282     {
20283         return this.el.select('label.control-label',true).first();
20284     },
20285     /* depricated... */
20286     
20287     label: function()
20288     {
20289         return this.labelEl();
20290     },
20291     
20292     boxLabelEl: function()
20293     {
20294         return this.el.select('label.box-label',true).first();
20295     },
20296     
20297     initEvents : function()
20298     {
20299 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20300         
20301         this.inputEl().on('click', this.onClick,  this);
20302         
20303         if (this.boxLabel) { 
20304             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20305         }
20306         
20307         this.startValue = this.getValue();
20308         
20309         if(this.groupId){
20310             Roo.bootstrap.CheckBox.register(this);
20311         }
20312     },
20313     
20314     onClick : function()
20315     {   
20316         this.setChecked(!this.checked);
20317     },
20318     
20319     setChecked : function(state,suppressEvent)
20320     {
20321         this.startValue = this.getValue();
20322
20323         if(this.inputType == 'radio'){
20324             
20325             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20326                 e.dom.checked = false;
20327             });
20328             
20329             this.inputEl().dom.checked = true;
20330             
20331             this.inputEl().dom.value = this.inputValue;
20332             
20333             if(suppressEvent !== true){
20334                 this.fireEvent('check', this, true);
20335             }
20336             
20337             this.validate();
20338             
20339             return;
20340         }
20341         
20342         this.checked = state;
20343         
20344         this.inputEl().dom.checked = state;
20345         
20346         
20347         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20348         
20349         if(suppressEvent !== true){
20350             this.fireEvent('check', this, state);
20351         }
20352         
20353         this.validate();
20354     },
20355     
20356     getValue : function()
20357     {
20358         if(this.inputType == 'radio'){
20359             return this.getGroupValue();
20360         }
20361         
20362         return this.hiddenEl().dom.value;
20363         
20364     },
20365     
20366     getGroupValue : function()
20367     {
20368         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20369             return '';
20370         }
20371         
20372         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20373     },
20374     
20375     setValue : function(v,suppressEvent)
20376     {
20377         if(this.inputType == 'radio'){
20378             this.setGroupValue(v, suppressEvent);
20379             return;
20380         }
20381         
20382         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20383         
20384         this.validate();
20385     },
20386     
20387     setGroupValue : function(v, suppressEvent)
20388     {
20389         this.startValue = this.getValue();
20390         
20391         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20392             e.dom.checked = false;
20393             
20394             if(e.dom.value == v){
20395                 e.dom.checked = true;
20396             }
20397         });
20398         
20399         if(suppressEvent !== true){
20400             this.fireEvent('check', this, true);
20401         }
20402
20403         this.validate();
20404         
20405         return;
20406     },
20407     
20408     validate : function()
20409     {
20410         if(
20411                 this.disabled || 
20412                 (this.inputType == 'radio' && this.validateRadio()) ||
20413                 (this.inputType == 'checkbox' && this.validateCheckbox())
20414         ){
20415             this.markValid();
20416             return true;
20417         }
20418         
20419         this.markInvalid();
20420         return false;
20421     },
20422     
20423     validateRadio : function()
20424     {
20425         if(this.allowBlank){
20426             return true;
20427         }
20428         
20429         var valid = false;
20430         
20431         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20432             if(!e.dom.checked){
20433                 return;
20434             }
20435             
20436             valid = true;
20437             
20438             return false;
20439         });
20440         
20441         return valid;
20442     },
20443     
20444     validateCheckbox : function()
20445     {
20446         if(!this.groupId){
20447             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20448             //return (this.getValue() == this.inputValue) ? true : false;
20449         }
20450         
20451         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20452         
20453         if(!group){
20454             return false;
20455         }
20456         
20457         var r = false;
20458         
20459         for(var i in group){
20460             if(r){
20461                 break;
20462             }
20463             
20464             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20465         }
20466         
20467         return r;
20468     },
20469     
20470     /**
20471      * Mark this field as valid
20472      */
20473     markValid : function()
20474     {
20475         var _this = this;
20476         
20477         this.fireEvent('valid', this);
20478         
20479         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20480         
20481         if(this.groupId){
20482             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20483         }
20484         
20485         if(label){
20486             label.markValid();
20487         }
20488
20489         if(this.inputType == 'radio'){
20490             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20491                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20492                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20493             });
20494             
20495             return;
20496         }
20497
20498         if(!this.groupId){
20499             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20500             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20501             return;
20502         }
20503         
20504         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20505         
20506         if(!group){
20507             return;
20508         }
20509         
20510         for(var i in group){
20511             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20512             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20513         }
20514     },
20515     
20516      /**
20517      * Mark this field as invalid
20518      * @param {String} msg The validation message
20519      */
20520     markInvalid : function(msg)
20521     {
20522         if(this.allowBlank){
20523             return;
20524         }
20525         
20526         var _this = this;
20527         
20528         this.fireEvent('invalid', this, msg);
20529         
20530         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20531         
20532         if(this.groupId){
20533             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20534         }
20535         
20536         if(label){
20537             label.markInvalid();
20538         }
20539             
20540         if(this.inputType == 'radio'){
20541             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20542                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20543                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20544             });
20545             
20546             return;
20547         }
20548         
20549         if(!this.groupId){
20550             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20551             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20552             return;
20553         }
20554         
20555         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20556         
20557         if(!group){
20558             return;
20559         }
20560         
20561         for(var i in group){
20562             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20563             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20564         }
20565         
20566     },
20567     
20568     clearInvalid : function()
20569     {
20570         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20571         
20572         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20573         
20574         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20575         
20576         if (label) {
20577             label.iconEl.removeClass(label.validClass);
20578             label.iconEl.removeClass(label.invalidClass);
20579         }
20580     },
20581     
20582     disable : function()
20583     {
20584         if(this.inputType != 'radio'){
20585             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20586             return;
20587         }
20588         
20589         var _this = this;
20590         
20591         if(this.rendered){
20592             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20593                 _this.getActionEl().addClass(this.disabledClass);
20594                 e.dom.disabled = true;
20595             });
20596         }
20597         
20598         this.disabled = true;
20599         this.fireEvent("disable", this);
20600         return this;
20601     },
20602
20603     enable : function()
20604     {
20605         if(this.inputType != 'radio'){
20606             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20607             return;
20608         }
20609         
20610         var _this = this;
20611         
20612         if(this.rendered){
20613             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20614                 _this.getActionEl().removeClass(this.disabledClass);
20615                 e.dom.disabled = false;
20616             });
20617         }
20618         
20619         this.disabled = false;
20620         this.fireEvent("enable", this);
20621         return this;
20622     }
20623
20624 });
20625
20626 Roo.apply(Roo.bootstrap.CheckBox, {
20627     
20628     groups: {},
20629     
20630      /**
20631     * register a CheckBox Group
20632     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20633     */
20634     register : function(checkbox)
20635     {
20636         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20637             this.groups[checkbox.groupId] = {};
20638         }
20639         
20640         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20641             return;
20642         }
20643         
20644         this.groups[checkbox.groupId][checkbox.name] = checkbox;
20645         
20646     },
20647     /**
20648     * fetch a CheckBox Group based on the group ID
20649     * @param {string} the group ID
20650     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20651     */
20652     get: function(groupId) {
20653         if (typeof(this.groups[groupId]) == 'undefined') {
20654             return false;
20655         }
20656         
20657         return this.groups[groupId] ;
20658     }
20659     
20660     
20661 });
20662 /*
20663  * - LGPL
20664  *
20665  * RadioItem
20666  * 
20667  */
20668
20669 /**
20670  * @class Roo.bootstrap.Radio
20671  * @extends Roo.bootstrap.Component
20672  * Bootstrap Radio class
20673  * @cfg {String} boxLabel - the label associated
20674  * @cfg {String} value - the value of radio
20675  * 
20676  * @constructor
20677  * Create a new Radio
20678  * @param {Object} config The config object
20679  */
20680 Roo.bootstrap.Radio = function(config){
20681     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20682     
20683 };
20684
20685 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
20686     
20687     boxLabel : '',
20688     
20689     value : '',
20690     
20691     getAutoCreate : function()
20692     {
20693         var cfg = {
20694             tag : 'div',
20695             cls : 'form-group radio',
20696             cn : [
20697                 {
20698                     tag : 'label',
20699                     cls : 'box-label',
20700                     html : this.boxLabel
20701                 }
20702             ]
20703         };
20704         
20705         return cfg;
20706     },
20707     
20708     initEvents : function() 
20709     {
20710         this.parent().register(this);
20711         
20712         this.el.on('click', this.onClick, this);
20713         
20714     },
20715     
20716     onClick : function()
20717     {
20718         this.setChecked(true);
20719     },
20720     
20721     setChecked : function(state, suppressEvent)
20722     {
20723         this.parent().setValue(this.value, suppressEvent);
20724         
20725     }
20726     
20727 });
20728  
20729
20730  /*
20731  * - LGPL
20732  *
20733  * Input
20734  * 
20735  */
20736
20737 /**
20738  * @class Roo.bootstrap.SecurePass
20739  * @extends Roo.bootstrap.Input
20740  * Bootstrap SecurePass class
20741  *
20742  * 
20743  * @constructor
20744  * Create a new SecurePass
20745  * @param {Object} config The config object
20746  */
20747  
20748 Roo.bootstrap.SecurePass = function (config) {
20749     // these go here, so the translation tool can replace them..
20750     this.errors = {
20751         PwdEmpty: "Please type a password, and then retype it to confirm.",
20752         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20753         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20754         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20755         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20756         FNInPwd: "Your password can't contain your first name. Please type a different password.",
20757         LNInPwd: "Your password can't contain your last name. Please type a different password.",
20758         TooWeak: "Your password is Too Weak."
20759     },
20760     this.meterLabel = "Password strength:";
20761     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
20762     this.meterClass = [
20763         "roo-password-meter-tooweak", 
20764         "roo-password-meter-weak", 
20765         "roo-password-meter-medium", 
20766         "roo-password-meter-strong", 
20767         "roo-password-meter-grey"
20768     ];
20769     
20770     this.errors = {};
20771     
20772     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
20773 }
20774
20775 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
20776     /**
20777      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
20778      * {
20779      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
20780      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20781      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20782      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20783      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20784      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
20785      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
20786      * })
20787      */
20788     // private
20789     
20790     meterWidth: 300,
20791     errorMsg :'',    
20792     errors: false,
20793     imageRoot: '/',
20794     /**
20795      * @cfg {String/Object} Label for the strength meter (defaults to
20796      * 'Password strength:')
20797      */
20798     // private
20799     meterLabel: '',
20800     /**
20801      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
20802      * ['Weak', 'Medium', 'Strong'])
20803      */
20804     // private    
20805     pwdStrengths: false,    
20806     // private
20807     strength: 0,
20808     // private
20809     _lastPwd: null,
20810     // private
20811     kCapitalLetter: 0,
20812     kSmallLetter: 1,
20813     kDigit: 2,
20814     kPunctuation: 3,
20815     
20816     insecure: false,
20817     // private
20818     initEvents: function ()
20819     {
20820         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
20821
20822         if (this.el.is('input[type=password]') && Roo.isSafari) {
20823             this.el.on('keydown', this.SafariOnKeyDown, this);
20824         }
20825
20826         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
20827     },
20828     // private
20829     onRender: function (ct, position)
20830     {
20831         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
20832         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
20833         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
20834
20835         this.trigger.createChild({
20836                    cn: [
20837                     {
20838                     //id: 'PwdMeter',
20839                     tag: 'div',
20840                     cls: 'roo-password-meter-grey col-xs-12',
20841                     style: {
20842                         //width: 0,
20843                         //width: this.meterWidth + 'px'                                                
20844                         }
20845                     },
20846                     {                            
20847                          cls: 'roo-password-meter-text'                          
20848                     }
20849                 ]            
20850         });
20851
20852          
20853         if (this.hideTrigger) {
20854             this.trigger.setDisplayed(false);
20855         }
20856         this.setSize(this.width || '', this.height || '');
20857     },
20858     // private
20859     onDestroy: function ()
20860     {
20861         if (this.trigger) {
20862             this.trigger.removeAllListeners();
20863             this.trigger.remove();
20864         }
20865         if (this.wrap) {
20866             this.wrap.remove();
20867         }
20868         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
20869     },
20870     // private
20871     checkStrength: function ()
20872     {
20873         var pwd = this.inputEl().getValue();
20874         if (pwd == this._lastPwd) {
20875             return;
20876         }
20877
20878         var strength;
20879         if (this.ClientSideStrongPassword(pwd)) {
20880             strength = 3;
20881         } else if (this.ClientSideMediumPassword(pwd)) {
20882             strength = 2;
20883         } else if (this.ClientSideWeakPassword(pwd)) {
20884             strength = 1;
20885         } else {
20886             strength = 0;
20887         }
20888         
20889         Roo.log('strength1: ' + strength);
20890         
20891         //var pm = this.trigger.child('div/div/div').dom;
20892         var pm = this.trigger.child('div/div');
20893         pm.removeClass(this.meterClass);
20894         pm.addClass(this.meterClass[strength]);
20895                 
20896         
20897         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
20898                 
20899         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
20900         
20901         this._lastPwd = pwd;
20902     },
20903     reset: function ()
20904     {
20905         Roo.bootstrap.SecurePass.superclass.reset.call(this);
20906         
20907         this._lastPwd = '';
20908         
20909         var pm = this.trigger.child('div/div');
20910         pm.removeClass(this.meterClass);
20911         pm.addClass('roo-password-meter-grey');        
20912         
20913         
20914         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
20915         
20916         pt.innerHTML = '';
20917         this.inputEl().dom.type='password';
20918     },
20919     // private
20920     validateValue: function (value)
20921     {
20922         
20923         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
20924             return false;
20925         }
20926         if (value.length == 0) {
20927             if (this.allowBlank) {
20928                 this.clearInvalid();
20929                 return true;
20930             }
20931
20932             this.markInvalid(this.errors.PwdEmpty);
20933             this.errorMsg = this.errors.PwdEmpty;
20934             return false;
20935         }
20936         
20937         if(this.insecure){
20938             return true;
20939         }
20940         
20941         if ('[\x21-\x7e]*'.match(value)) {
20942             this.markInvalid(this.errors.PwdBadChar);
20943             this.errorMsg = this.errors.PwdBadChar;
20944             return false;
20945         }
20946         if (value.length < 6) {
20947             this.markInvalid(this.errors.PwdShort);
20948             this.errorMsg = this.errors.PwdShort;
20949             return false;
20950         }
20951         if (value.length > 16) {
20952             this.markInvalid(this.errors.PwdLong);
20953             this.errorMsg = this.errors.PwdLong;
20954             return false;
20955         }
20956         var strength;
20957         if (this.ClientSideStrongPassword(value)) {
20958             strength = 3;
20959         } else if (this.ClientSideMediumPassword(value)) {
20960             strength = 2;
20961         } else if (this.ClientSideWeakPassword(value)) {
20962             strength = 1;
20963         } else {
20964             strength = 0;
20965         }
20966
20967         
20968         if (strength < 2) {
20969             //this.markInvalid(this.errors.TooWeak);
20970             this.errorMsg = this.errors.TooWeak;
20971             //return false;
20972         }
20973         
20974         
20975         console.log('strength2: ' + strength);
20976         
20977         //var pm = this.trigger.child('div/div/div').dom;
20978         
20979         var pm = this.trigger.child('div/div');
20980         pm.removeClass(this.meterClass);
20981         pm.addClass(this.meterClass[strength]);
20982                 
20983         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
20984                 
20985         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
20986         
20987         this.errorMsg = ''; 
20988         return true;
20989     },
20990     // private
20991     CharacterSetChecks: function (type)
20992     {
20993         this.type = type;
20994         this.fResult = false;
20995     },
20996     // private
20997     isctype: function (character, type)
20998     {
20999         switch (type) {  
21000             case this.kCapitalLetter:
21001                 if (character >= 'A' && character <= 'Z') {
21002                     return true;
21003                 }
21004                 break;
21005             
21006             case this.kSmallLetter:
21007                 if (character >= 'a' && character <= 'z') {
21008                     return true;
21009                 }
21010                 break;
21011             
21012             case this.kDigit:
21013                 if (character >= '0' && character <= '9') {
21014                     return true;
21015                 }
21016                 break;
21017             
21018             case this.kPunctuation:
21019                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21020                     return true;
21021                 }
21022                 break;
21023             
21024             default:
21025                 return false;
21026         }
21027
21028     },
21029     // private
21030     IsLongEnough: function (pwd, size)
21031     {
21032         return !(pwd == null || isNaN(size) || pwd.length < size);
21033     },
21034     // private
21035     SpansEnoughCharacterSets: function (word, nb)
21036     {
21037         if (!this.IsLongEnough(word, nb))
21038         {
21039             return false;
21040         }
21041
21042         var characterSetChecks = new Array(
21043             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21044             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21045         );
21046         
21047         for (var index = 0; index < word.length; ++index) {
21048             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21049                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21050                     characterSetChecks[nCharSet].fResult = true;
21051                     break;
21052                 }
21053             }
21054         }
21055
21056         var nCharSets = 0;
21057         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21058             if (characterSetChecks[nCharSet].fResult) {
21059                 ++nCharSets;
21060             }
21061         }
21062
21063         if (nCharSets < nb) {
21064             return false;
21065         }
21066         return true;
21067     },
21068     // private
21069     ClientSideStrongPassword: function (pwd)
21070     {
21071         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21072     },
21073     // private
21074     ClientSideMediumPassword: function (pwd)
21075     {
21076         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21077     },
21078     // private
21079     ClientSideWeakPassword: function (pwd)
21080     {
21081         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21082     }
21083           
21084 })//<script type="text/javascript">
21085
21086 /*
21087  * Based  Ext JS Library 1.1.1
21088  * Copyright(c) 2006-2007, Ext JS, LLC.
21089  * LGPL
21090  *
21091  */
21092  
21093 /**
21094  * @class Roo.HtmlEditorCore
21095  * @extends Roo.Component
21096  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21097  *
21098  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21099  */
21100
21101 Roo.HtmlEditorCore = function(config){
21102     
21103     
21104     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21105     
21106     
21107     this.addEvents({
21108         /**
21109          * @event initialize
21110          * Fires when the editor is fully initialized (including the iframe)
21111          * @param {Roo.HtmlEditorCore} this
21112          */
21113         initialize: true,
21114         /**
21115          * @event activate
21116          * Fires when the editor is first receives the focus. Any insertion must wait
21117          * until after this event.
21118          * @param {Roo.HtmlEditorCore} this
21119          */
21120         activate: true,
21121          /**
21122          * @event beforesync
21123          * Fires before the textarea is updated with content from the editor iframe. Return false
21124          * to cancel the sync.
21125          * @param {Roo.HtmlEditorCore} this
21126          * @param {String} html
21127          */
21128         beforesync: true,
21129          /**
21130          * @event beforepush
21131          * Fires before the iframe editor is updated with content from the textarea. Return false
21132          * to cancel the push.
21133          * @param {Roo.HtmlEditorCore} this
21134          * @param {String} html
21135          */
21136         beforepush: true,
21137          /**
21138          * @event sync
21139          * Fires when the textarea is updated with content from the editor iframe.
21140          * @param {Roo.HtmlEditorCore} this
21141          * @param {String} html
21142          */
21143         sync: true,
21144          /**
21145          * @event push
21146          * Fires when the iframe editor is updated with content from the textarea.
21147          * @param {Roo.HtmlEditorCore} this
21148          * @param {String} html
21149          */
21150         push: true,
21151         
21152         /**
21153          * @event editorevent
21154          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21155          * @param {Roo.HtmlEditorCore} this
21156          */
21157         editorevent: true
21158         
21159     });
21160     
21161     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21162     
21163     // defaults : white / black...
21164     this.applyBlacklists();
21165     
21166     
21167     
21168 };
21169
21170
21171 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21172
21173
21174      /**
21175      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21176      */
21177     
21178     owner : false,
21179     
21180      /**
21181      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21182      *                        Roo.resizable.
21183      */
21184     resizable : false,
21185      /**
21186      * @cfg {Number} height (in pixels)
21187      */   
21188     height: 300,
21189    /**
21190      * @cfg {Number} width (in pixels)
21191      */   
21192     width: 500,
21193     
21194     /**
21195      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21196      * 
21197      */
21198     stylesheets: false,
21199     
21200     // id of frame..
21201     frameId: false,
21202     
21203     // private properties
21204     validationEvent : false,
21205     deferHeight: true,
21206     initialized : false,
21207     activated : false,
21208     sourceEditMode : false,
21209     onFocus : Roo.emptyFn,
21210     iframePad:3,
21211     hideMode:'offsets',
21212     
21213     clearUp: true,
21214     
21215     // blacklist + whitelisted elements..
21216     black: false,
21217     white: false,
21218      
21219     
21220
21221     /**
21222      * Protected method that will not generally be called directly. It
21223      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21224      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21225      */
21226     getDocMarkup : function(){
21227         // body styles..
21228         var st = '';
21229         
21230         // inherit styels from page...?? 
21231         if (this.stylesheets === false) {
21232             
21233             Roo.get(document.head).select('style').each(function(node) {
21234                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21235             });
21236             
21237             Roo.get(document.head).select('link').each(function(node) { 
21238                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21239             });
21240             
21241         } else if (!this.stylesheets.length) {
21242                 // simple..
21243                 st = '<style type="text/css">' +
21244                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21245                    '</style>';
21246         } else { 
21247             
21248         }
21249         
21250         st +=  '<style type="text/css">' +
21251             'IMG { cursor: pointer } ' +
21252         '</style>';
21253
21254         
21255         return '<html><head>' + st  +
21256             //<style type="text/css">' +
21257             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21258             //'</style>' +
21259             ' </head><body class="roo-htmleditor-body"></body></html>';
21260     },
21261
21262     // private
21263     onRender : function(ct, position)
21264     {
21265         var _t = this;
21266         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21267         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21268         
21269         
21270         this.el.dom.style.border = '0 none';
21271         this.el.dom.setAttribute('tabIndex', -1);
21272         this.el.addClass('x-hidden hide');
21273         
21274         
21275         
21276         if(Roo.isIE){ // fix IE 1px bogus margin
21277             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21278         }
21279        
21280         
21281         this.frameId = Roo.id();
21282         
21283          
21284         
21285         var iframe = this.owner.wrap.createChild({
21286             tag: 'iframe',
21287             cls: 'form-control', // bootstrap..
21288             id: this.frameId,
21289             name: this.frameId,
21290             frameBorder : 'no',
21291             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21292         }, this.el
21293         );
21294         
21295         
21296         this.iframe = iframe.dom;
21297
21298          this.assignDocWin();
21299         
21300         this.doc.designMode = 'on';
21301        
21302         this.doc.open();
21303         this.doc.write(this.getDocMarkup());
21304         this.doc.close();
21305
21306         
21307         var task = { // must defer to wait for browser to be ready
21308             run : function(){
21309                 //console.log("run task?" + this.doc.readyState);
21310                 this.assignDocWin();
21311                 if(this.doc.body || this.doc.readyState == 'complete'){
21312                     try {
21313                         this.doc.designMode="on";
21314                     } catch (e) {
21315                         return;
21316                     }
21317                     Roo.TaskMgr.stop(task);
21318                     this.initEditor.defer(10, this);
21319                 }
21320             },
21321             interval : 10,
21322             duration: 10000,
21323             scope: this
21324         };
21325         Roo.TaskMgr.start(task);
21326
21327     },
21328
21329     // private
21330     onResize : function(w, h)
21331     {
21332          Roo.log('resize: ' +w + ',' + h );
21333         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21334         if(!this.iframe){
21335             return;
21336         }
21337         if(typeof w == 'number'){
21338             
21339             this.iframe.style.width = w + 'px';
21340         }
21341         if(typeof h == 'number'){
21342             
21343             this.iframe.style.height = h + 'px';
21344             if(this.doc){
21345                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21346             }
21347         }
21348         
21349     },
21350
21351     /**
21352      * Toggles the editor between standard and source edit mode.
21353      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21354      */
21355     toggleSourceEdit : function(sourceEditMode){
21356         
21357         this.sourceEditMode = sourceEditMode === true;
21358         
21359         if(this.sourceEditMode){
21360  
21361             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21362             
21363         }else{
21364             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21365             //this.iframe.className = '';
21366             this.deferFocus();
21367         }
21368         //this.setSize(this.owner.wrap.getSize());
21369         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21370     },
21371
21372     
21373   
21374
21375     /**
21376      * Protected method that will not generally be called directly. If you need/want
21377      * custom HTML cleanup, this is the method you should override.
21378      * @param {String} html The HTML to be cleaned
21379      * return {String} The cleaned HTML
21380      */
21381     cleanHtml : function(html){
21382         html = String(html);
21383         if(html.length > 5){
21384             if(Roo.isSafari){ // strip safari nonsense
21385                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21386             }
21387         }
21388         if(html == '&nbsp;'){
21389             html = '';
21390         }
21391         return html;
21392     },
21393
21394     /**
21395      * HTML Editor -> Textarea
21396      * Protected method that will not generally be called directly. Syncs the contents
21397      * of the editor iframe with the textarea.
21398      */
21399     syncValue : function(){
21400         if(this.initialized){
21401             var bd = (this.doc.body || this.doc.documentElement);
21402             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21403             var html = bd.innerHTML;
21404             if(Roo.isSafari){
21405                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21406                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21407                 if(m && m[1]){
21408                     html = '<div style="'+m[0]+'">' + html + '</div>';
21409                 }
21410             }
21411             html = this.cleanHtml(html);
21412             // fix up the special chars.. normaly like back quotes in word...
21413             // however we do not want to do this with chinese..
21414             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21415                 var cc = b.charCodeAt();
21416                 if (
21417                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21418                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21419                     (cc >= 0xf900 && cc < 0xfb00 )
21420                 ) {
21421                         return b;
21422                 }
21423                 return "&#"+cc+";" 
21424             });
21425             if(this.owner.fireEvent('beforesync', this, html) !== false){
21426                 this.el.dom.value = html;
21427                 this.owner.fireEvent('sync', this, html);
21428             }
21429         }
21430     },
21431
21432     /**
21433      * Protected method that will not generally be called directly. Pushes the value of the textarea
21434      * into the iframe editor.
21435      */
21436     pushValue : function(){
21437         if(this.initialized){
21438             var v = this.el.dom.value.trim();
21439             
21440 //            if(v.length < 1){
21441 //                v = '&#160;';
21442 //            }
21443             
21444             if(this.owner.fireEvent('beforepush', this, v) !== false){
21445                 var d = (this.doc.body || this.doc.documentElement);
21446                 d.innerHTML = v;
21447                 this.cleanUpPaste();
21448                 this.el.dom.value = d.innerHTML;
21449                 this.owner.fireEvent('push', this, v);
21450             }
21451         }
21452     },
21453
21454     // private
21455     deferFocus : function(){
21456         this.focus.defer(10, this);
21457     },
21458
21459     // doc'ed in Field
21460     focus : function(){
21461         if(this.win && !this.sourceEditMode){
21462             this.win.focus();
21463         }else{
21464             this.el.focus();
21465         }
21466     },
21467     
21468     assignDocWin: function()
21469     {
21470         var iframe = this.iframe;
21471         
21472          if(Roo.isIE){
21473             this.doc = iframe.contentWindow.document;
21474             this.win = iframe.contentWindow;
21475         } else {
21476 //            if (!Roo.get(this.frameId)) {
21477 //                return;
21478 //            }
21479 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21480 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21481             
21482             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21483                 return;
21484             }
21485             
21486             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21487             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21488         }
21489     },
21490     
21491     // private
21492     initEditor : function(){
21493         //console.log("INIT EDITOR");
21494         this.assignDocWin();
21495         
21496         
21497         
21498         this.doc.designMode="on";
21499         this.doc.open();
21500         this.doc.write(this.getDocMarkup());
21501         this.doc.close();
21502         
21503         var dbody = (this.doc.body || this.doc.documentElement);
21504         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21505         // this copies styles from the containing element into thsi one..
21506         // not sure why we need all of this..
21507         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21508         
21509         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21510         //ss['background-attachment'] = 'fixed'; // w3c
21511         dbody.bgProperties = 'fixed'; // ie
21512         //Roo.DomHelper.applyStyles(dbody, ss);
21513         Roo.EventManager.on(this.doc, {
21514             //'mousedown': this.onEditorEvent,
21515             'mouseup': this.onEditorEvent,
21516             'dblclick': this.onEditorEvent,
21517             'click': this.onEditorEvent,
21518             'keyup': this.onEditorEvent,
21519             buffer:100,
21520             scope: this
21521         });
21522         if(Roo.isGecko){
21523             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21524         }
21525         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21526             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21527         }
21528         this.initialized = true;
21529
21530         this.owner.fireEvent('initialize', this);
21531         this.pushValue();
21532     },
21533
21534     // private
21535     onDestroy : function(){
21536         
21537         
21538         
21539         if(this.rendered){
21540             
21541             //for (var i =0; i < this.toolbars.length;i++) {
21542             //    // fixme - ask toolbars for heights?
21543             //    this.toolbars[i].onDestroy();
21544            // }
21545             
21546             //this.wrap.dom.innerHTML = '';
21547             //this.wrap.remove();
21548         }
21549     },
21550
21551     // private
21552     onFirstFocus : function(){
21553         
21554         this.assignDocWin();
21555         
21556         
21557         this.activated = true;
21558          
21559     
21560         if(Roo.isGecko){ // prevent silly gecko errors
21561             this.win.focus();
21562             var s = this.win.getSelection();
21563             if(!s.focusNode || s.focusNode.nodeType != 3){
21564                 var r = s.getRangeAt(0);
21565                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21566                 r.collapse(true);
21567                 this.deferFocus();
21568             }
21569             try{
21570                 this.execCmd('useCSS', true);
21571                 this.execCmd('styleWithCSS', false);
21572             }catch(e){}
21573         }
21574         this.owner.fireEvent('activate', this);
21575     },
21576
21577     // private
21578     adjustFont: function(btn){
21579         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21580         //if(Roo.isSafari){ // safari
21581         //    adjust *= 2;
21582        // }
21583         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21584         if(Roo.isSafari){ // safari
21585             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21586             v =  (v < 10) ? 10 : v;
21587             v =  (v > 48) ? 48 : v;
21588             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21589             
21590         }
21591         
21592         
21593         v = Math.max(1, v+adjust);
21594         
21595         this.execCmd('FontSize', v  );
21596     },
21597
21598     onEditorEvent : function(e)
21599     {
21600         this.owner.fireEvent('editorevent', this, e);
21601       //  this.updateToolbar();
21602         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21603     },
21604
21605     insertTag : function(tg)
21606     {
21607         // could be a bit smarter... -> wrap the current selected tRoo..
21608         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21609             
21610             range = this.createRange(this.getSelection());
21611             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21612             wrappingNode.appendChild(range.extractContents());
21613             range.insertNode(wrappingNode);
21614
21615             return;
21616             
21617             
21618             
21619         }
21620         this.execCmd("formatblock",   tg);
21621         
21622     },
21623     
21624     insertText : function(txt)
21625     {
21626         
21627         
21628         var range = this.createRange();
21629         range.deleteContents();
21630                //alert(Sender.getAttribute('label'));
21631                
21632         range.insertNode(this.doc.createTextNode(txt));
21633     } ,
21634     
21635      
21636
21637     /**
21638      * Executes a Midas editor command on the editor document and performs necessary focus and
21639      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21640      * @param {String} cmd The Midas command
21641      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21642      */
21643     relayCmd : function(cmd, value){
21644         this.win.focus();
21645         this.execCmd(cmd, value);
21646         this.owner.fireEvent('editorevent', this);
21647         //this.updateToolbar();
21648         this.owner.deferFocus();
21649     },
21650
21651     /**
21652      * Executes a Midas editor command directly on the editor document.
21653      * For visual commands, you should use {@link #relayCmd} instead.
21654      * <b>This should only be called after the editor is initialized.</b>
21655      * @param {String} cmd The Midas command
21656      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21657      */
21658     execCmd : function(cmd, value){
21659         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21660         this.syncValue();
21661     },
21662  
21663  
21664    
21665     /**
21666      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21667      * to insert tRoo.
21668      * @param {String} text | dom node.. 
21669      */
21670     insertAtCursor : function(text)
21671     {
21672         
21673         if(!this.activated){
21674             return;
21675         }
21676         /*
21677         if(Roo.isIE){
21678             this.win.focus();
21679             var r = this.doc.selection.createRange();
21680             if(r){
21681                 r.collapse(true);
21682                 r.pasteHTML(text);
21683                 this.syncValue();
21684                 this.deferFocus();
21685             
21686             }
21687             return;
21688         }
21689         */
21690         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21691             this.win.focus();
21692             
21693             
21694             // from jquery ui (MIT licenced)
21695             var range, node;
21696             var win = this.win;
21697             
21698             if (win.getSelection && win.getSelection().getRangeAt) {
21699                 range = win.getSelection().getRangeAt(0);
21700                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21701                 range.insertNode(node);
21702             } else if (win.document.selection && win.document.selection.createRange) {
21703                 // no firefox support
21704                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21705                 win.document.selection.createRange().pasteHTML(txt);
21706             } else {
21707                 // no firefox support
21708                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21709                 this.execCmd('InsertHTML', txt);
21710             } 
21711             
21712             this.syncValue();
21713             
21714             this.deferFocus();
21715         }
21716     },
21717  // private
21718     mozKeyPress : function(e){
21719         if(e.ctrlKey){
21720             var c = e.getCharCode(), cmd;
21721           
21722             if(c > 0){
21723                 c = String.fromCharCode(c).toLowerCase();
21724                 switch(c){
21725                     case 'b':
21726                         cmd = 'bold';
21727                         break;
21728                     case 'i':
21729                         cmd = 'italic';
21730                         break;
21731                     
21732                     case 'u':
21733                         cmd = 'underline';
21734                         break;
21735                     
21736                     case 'v':
21737                         this.cleanUpPaste.defer(100, this);
21738                         return;
21739                         
21740                 }
21741                 if(cmd){
21742                     this.win.focus();
21743                     this.execCmd(cmd);
21744                     this.deferFocus();
21745                     e.preventDefault();
21746                 }
21747                 
21748             }
21749         }
21750     },
21751
21752     // private
21753     fixKeys : function(){ // load time branching for fastest keydown performance
21754         if(Roo.isIE){
21755             return function(e){
21756                 var k = e.getKey(), r;
21757                 if(k == e.TAB){
21758                     e.stopEvent();
21759                     r = this.doc.selection.createRange();
21760                     if(r){
21761                         r.collapse(true);
21762                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21763                         this.deferFocus();
21764                     }
21765                     return;
21766                 }
21767                 
21768                 if(k == e.ENTER){
21769                     r = this.doc.selection.createRange();
21770                     if(r){
21771                         var target = r.parentElement();
21772                         if(!target || target.tagName.toLowerCase() != 'li'){
21773                             e.stopEvent();
21774                             r.pasteHTML('<br />');
21775                             r.collapse(false);
21776                             r.select();
21777                         }
21778                     }
21779                 }
21780                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21781                     this.cleanUpPaste.defer(100, this);
21782                     return;
21783                 }
21784                 
21785                 
21786             };
21787         }else if(Roo.isOpera){
21788             return function(e){
21789                 var k = e.getKey();
21790                 if(k == e.TAB){
21791                     e.stopEvent();
21792                     this.win.focus();
21793                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21794                     this.deferFocus();
21795                 }
21796                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21797                     this.cleanUpPaste.defer(100, this);
21798                     return;
21799                 }
21800                 
21801             };
21802         }else if(Roo.isSafari){
21803             return function(e){
21804                 var k = e.getKey();
21805                 
21806                 if(k == e.TAB){
21807                     e.stopEvent();
21808                     this.execCmd('InsertText','\t');
21809                     this.deferFocus();
21810                     return;
21811                 }
21812                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21813                     this.cleanUpPaste.defer(100, this);
21814                     return;
21815                 }
21816                 
21817              };
21818         }
21819     }(),
21820     
21821     getAllAncestors: function()
21822     {
21823         var p = this.getSelectedNode();
21824         var a = [];
21825         if (!p) {
21826             a.push(p); // push blank onto stack..
21827             p = this.getParentElement();
21828         }
21829         
21830         
21831         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21832             a.push(p);
21833             p = p.parentNode;
21834         }
21835         a.push(this.doc.body);
21836         return a;
21837     },
21838     lastSel : false,
21839     lastSelNode : false,
21840     
21841     
21842     getSelection : function() 
21843     {
21844         this.assignDocWin();
21845         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21846     },
21847     
21848     getSelectedNode: function() 
21849     {
21850         // this may only work on Gecko!!!
21851         
21852         // should we cache this!!!!
21853         
21854         
21855         
21856          
21857         var range = this.createRange(this.getSelection()).cloneRange();
21858         
21859         if (Roo.isIE) {
21860             var parent = range.parentElement();
21861             while (true) {
21862                 var testRange = range.duplicate();
21863                 testRange.moveToElementText(parent);
21864                 if (testRange.inRange(range)) {
21865                     break;
21866                 }
21867                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21868                     break;
21869                 }
21870                 parent = parent.parentElement;
21871             }
21872             return parent;
21873         }
21874         
21875         // is ancestor a text element.
21876         var ac =  range.commonAncestorContainer;
21877         if (ac.nodeType == 3) {
21878             ac = ac.parentNode;
21879         }
21880         
21881         var ar = ac.childNodes;
21882          
21883         var nodes = [];
21884         var other_nodes = [];
21885         var has_other_nodes = false;
21886         for (var i=0;i<ar.length;i++) {
21887             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21888                 continue;
21889             }
21890             // fullly contained node.
21891             
21892             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21893                 nodes.push(ar[i]);
21894                 continue;
21895             }
21896             
21897             // probably selected..
21898             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21899                 other_nodes.push(ar[i]);
21900                 continue;
21901             }
21902             // outer..
21903             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21904                 continue;
21905             }
21906             
21907             
21908             has_other_nodes = true;
21909         }
21910         if (!nodes.length && other_nodes.length) {
21911             nodes= other_nodes;
21912         }
21913         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21914             return false;
21915         }
21916         
21917         return nodes[0];
21918     },
21919     createRange: function(sel)
21920     {
21921         // this has strange effects when using with 
21922         // top toolbar - not sure if it's a great idea.
21923         //this.editor.contentWindow.focus();
21924         if (typeof sel != "undefined") {
21925             try {
21926                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21927             } catch(e) {
21928                 return this.doc.createRange();
21929             }
21930         } else {
21931             return this.doc.createRange();
21932         }
21933     },
21934     getParentElement: function()
21935     {
21936         
21937         this.assignDocWin();
21938         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21939         
21940         var range = this.createRange(sel);
21941          
21942         try {
21943             var p = range.commonAncestorContainer;
21944             while (p.nodeType == 3) { // text node
21945                 p = p.parentNode;
21946             }
21947             return p;
21948         } catch (e) {
21949             return null;
21950         }
21951     
21952     },
21953     /***
21954      *
21955      * Range intersection.. the hard stuff...
21956      *  '-1' = before
21957      *  '0' = hits..
21958      *  '1' = after.
21959      *         [ -- selected range --- ]
21960      *   [fail]                        [fail]
21961      *
21962      *    basically..
21963      *      if end is before start or  hits it. fail.
21964      *      if start is after end or hits it fail.
21965      *
21966      *   if either hits (but other is outside. - then it's not 
21967      *   
21968      *    
21969      **/
21970     
21971     
21972     // @see http://www.thismuchiknow.co.uk/?p=64.
21973     rangeIntersectsNode : function(range, node)
21974     {
21975         var nodeRange = node.ownerDocument.createRange();
21976         try {
21977             nodeRange.selectNode(node);
21978         } catch (e) {
21979             nodeRange.selectNodeContents(node);
21980         }
21981     
21982         var rangeStartRange = range.cloneRange();
21983         rangeStartRange.collapse(true);
21984     
21985         var rangeEndRange = range.cloneRange();
21986         rangeEndRange.collapse(false);
21987     
21988         var nodeStartRange = nodeRange.cloneRange();
21989         nodeStartRange.collapse(true);
21990     
21991         var nodeEndRange = nodeRange.cloneRange();
21992         nodeEndRange.collapse(false);
21993     
21994         return rangeStartRange.compareBoundaryPoints(
21995                  Range.START_TO_START, nodeEndRange) == -1 &&
21996                rangeEndRange.compareBoundaryPoints(
21997                  Range.START_TO_START, nodeStartRange) == 1;
21998         
21999          
22000     },
22001     rangeCompareNode : function(range, node)
22002     {
22003         var nodeRange = node.ownerDocument.createRange();
22004         try {
22005             nodeRange.selectNode(node);
22006         } catch (e) {
22007             nodeRange.selectNodeContents(node);
22008         }
22009         
22010         
22011         range.collapse(true);
22012     
22013         nodeRange.collapse(true);
22014      
22015         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22016         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22017          
22018         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22019         
22020         var nodeIsBefore   =  ss == 1;
22021         var nodeIsAfter    = ee == -1;
22022         
22023         if (nodeIsBefore && nodeIsAfter) {
22024             return 0; // outer
22025         }
22026         if (!nodeIsBefore && nodeIsAfter) {
22027             return 1; //right trailed.
22028         }
22029         
22030         if (nodeIsBefore && !nodeIsAfter) {
22031             return 2;  // left trailed.
22032         }
22033         // fully contined.
22034         return 3;
22035     },
22036
22037     // private? - in a new class?
22038     cleanUpPaste :  function()
22039     {
22040         // cleans up the whole document..
22041         Roo.log('cleanuppaste');
22042         
22043         this.cleanUpChildren(this.doc.body);
22044         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22045         if (clean != this.doc.body.innerHTML) {
22046             this.doc.body.innerHTML = clean;
22047         }
22048         
22049     },
22050     
22051     cleanWordChars : function(input) {// change the chars to hex code
22052         var he = Roo.HtmlEditorCore;
22053         
22054         var output = input;
22055         Roo.each(he.swapCodes, function(sw) { 
22056             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22057             
22058             output = output.replace(swapper, sw[1]);
22059         });
22060         
22061         return output;
22062     },
22063     
22064     
22065     cleanUpChildren : function (n)
22066     {
22067         if (!n.childNodes.length) {
22068             return;
22069         }
22070         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22071            this.cleanUpChild(n.childNodes[i]);
22072         }
22073     },
22074     
22075     
22076         
22077     
22078     cleanUpChild : function (node)
22079     {
22080         var ed = this;
22081         //console.log(node);
22082         if (node.nodeName == "#text") {
22083             // clean up silly Windows -- stuff?
22084             return; 
22085         }
22086         if (node.nodeName == "#comment") {
22087             node.parentNode.removeChild(node);
22088             // clean up silly Windows -- stuff?
22089             return; 
22090         }
22091         var lcname = node.tagName.toLowerCase();
22092         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22093         // whitelist of tags..
22094         
22095         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22096             // remove node.
22097             node.parentNode.removeChild(node);
22098             return;
22099             
22100         }
22101         
22102         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22103         
22104         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22105         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22106         
22107         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22108         //    remove_keep_children = true;
22109         //}
22110         
22111         if (remove_keep_children) {
22112             this.cleanUpChildren(node);
22113             // inserts everything just before this node...
22114             while (node.childNodes.length) {
22115                 var cn = node.childNodes[0];
22116                 node.removeChild(cn);
22117                 node.parentNode.insertBefore(cn, node);
22118             }
22119             node.parentNode.removeChild(node);
22120             return;
22121         }
22122         
22123         if (!node.attributes || !node.attributes.length) {
22124             this.cleanUpChildren(node);
22125             return;
22126         }
22127         
22128         function cleanAttr(n,v)
22129         {
22130             
22131             if (v.match(/^\./) || v.match(/^\//)) {
22132                 return;
22133             }
22134             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
22135                 return;
22136             }
22137             if (v.match(/^#/)) {
22138                 return;
22139             }
22140 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22141             node.removeAttribute(n);
22142             
22143         }
22144         
22145         var cwhite = this.cwhite;
22146         var cblack = this.cblack;
22147             
22148         function cleanStyle(n,v)
22149         {
22150             if (v.match(/expression/)) { //XSS?? should we even bother..
22151                 node.removeAttribute(n);
22152                 return;
22153             }
22154             
22155             var parts = v.split(/;/);
22156             var clean = [];
22157             
22158             Roo.each(parts, function(p) {
22159                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22160                 if (!p.length) {
22161                     return true;
22162                 }
22163                 var l = p.split(':').shift().replace(/\s+/g,'');
22164                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22165                 
22166                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22167 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22168                     //node.removeAttribute(n);
22169                     return true;
22170                 }
22171                 //Roo.log()
22172                 // only allow 'c whitelisted system attributes'
22173                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22174 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22175                     //node.removeAttribute(n);
22176                     return true;
22177                 }
22178                 
22179                 
22180                  
22181                 
22182                 clean.push(p);
22183                 return true;
22184             });
22185             if (clean.length) { 
22186                 node.setAttribute(n, clean.join(';'));
22187             } else {
22188                 node.removeAttribute(n);
22189             }
22190             
22191         }
22192         
22193         
22194         for (var i = node.attributes.length-1; i > -1 ; i--) {
22195             var a = node.attributes[i];
22196             //console.log(a);
22197             
22198             if (a.name.toLowerCase().substr(0,2)=='on')  {
22199                 node.removeAttribute(a.name);
22200                 continue;
22201             }
22202             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22203                 node.removeAttribute(a.name);
22204                 continue;
22205             }
22206             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22207                 cleanAttr(a.name,a.value); // fixme..
22208                 continue;
22209             }
22210             if (a.name == 'style') {
22211                 cleanStyle(a.name,a.value);
22212                 continue;
22213             }
22214             /// clean up MS crap..
22215             // tecnically this should be a list of valid class'es..
22216             
22217             
22218             if (a.name == 'class') {
22219                 if (a.value.match(/^Mso/)) {
22220                     node.className = '';
22221                 }
22222                 
22223                 if (a.value.match(/^body$/)) {
22224                     node.className = '';
22225                 }
22226                 continue;
22227             }
22228             
22229             // style cleanup!?
22230             // class cleanup?
22231             
22232         }
22233         
22234         
22235         this.cleanUpChildren(node);
22236         
22237         
22238     },
22239     
22240     /**
22241      * Clean up MS wordisms...
22242      */
22243     cleanWord : function(node)
22244     {
22245         
22246         
22247         if (!node) {
22248             this.cleanWord(this.doc.body);
22249             return;
22250         }
22251         if (node.nodeName == "#text") {
22252             // clean up silly Windows -- stuff?
22253             return; 
22254         }
22255         if (node.nodeName == "#comment") {
22256             node.parentNode.removeChild(node);
22257             // clean up silly Windows -- stuff?
22258             return; 
22259         }
22260         
22261         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22262             node.parentNode.removeChild(node);
22263             return;
22264         }
22265         
22266         // remove - but keep children..
22267         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22268             while (node.childNodes.length) {
22269                 var cn = node.childNodes[0];
22270                 node.removeChild(cn);
22271                 node.parentNode.insertBefore(cn, node);
22272             }
22273             node.parentNode.removeChild(node);
22274             this.iterateChildren(node, this.cleanWord);
22275             return;
22276         }
22277         // clean styles
22278         if (node.className.length) {
22279             
22280             var cn = node.className.split(/\W+/);
22281             var cna = [];
22282             Roo.each(cn, function(cls) {
22283                 if (cls.match(/Mso[a-zA-Z]+/)) {
22284                     return;
22285                 }
22286                 cna.push(cls);
22287             });
22288             node.className = cna.length ? cna.join(' ') : '';
22289             if (!cna.length) {
22290                 node.removeAttribute("class");
22291             }
22292         }
22293         
22294         if (node.hasAttribute("lang")) {
22295             node.removeAttribute("lang");
22296         }
22297         
22298         if (node.hasAttribute("style")) {
22299             
22300             var styles = node.getAttribute("style").split(";");
22301             var nstyle = [];
22302             Roo.each(styles, function(s) {
22303                 if (!s.match(/:/)) {
22304                     return;
22305                 }
22306                 var kv = s.split(":");
22307                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22308                     return;
22309                 }
22310                 // what ever is left... we allow.
22311                 nstyle.push(s);
22312             });
22313             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22314             if (!nstyle.length) {
22315                 node.removeAttribute('style');
22316             }
22317         }
22318         this.iterateChildren(node, this.cleanWord);
22319         
22320         
22321         
22322     },
22323     /**
22324      * iterateChildren of a Node, calling fn each time, using this as the scole..
22325      * @param {DomNode} node node to iterate children of.
22326      * @param {Function} fn method of this class to call on each item.
22327      */
22328     iterateChildren : function(node, fn)
22329     {
22330         if (!node.childNodes.length) {
22331                 return;
22332         }
22333         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22334            fn.call(this, node.childNodes[i])
22335         }
22336     },
22337     
22338     
22339     /**
22340      * cleanTableWidths.
22341      *
22342      * Quite often pasting from word etc.. results in tables with column and widths.
22343      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22344      *
22345      */
22346     cleanTableWidths : function(node)
22347     {
22348          
22349          
22350         if (!node) {
22351             this.cleanTableWidths(this.doc.body);
22352             return;
22353         }
22354         
22355         // ignore list...
22356         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22357             return; 
22358         }
22359         Roo.log(node.tagName);
22360         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22361             this.iterateChildren(node, this.cleanTableWidths);
22362             return;
22363         }
22364         if (node.hasAttribute('width')) {
22365             node.removeAttribute('width');
22366         }
22367         
22368          
22369         if (node.hasAttribute("style")) {
22370             // pretty basic...
22371             
22372             var styles = node.getAttribute("style").split(";");
22373             var nstyle = [];
22374             Roo.each(styles, function(s) {
22375                 if (!s.match(/:/)) {
22376                     return;
22377                 }
22378                 var kv = s.split(":");
22379                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22380                     return;
22381                 }
22382                 // what ever is left... we allow.
22383                 nstyle.push(s);
22384             });
22385             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22386             if (!nstyle.length) {
22387                 node.removeAttribute('style');
22388             }
22389         }
22390         
22391         this.iterateChildren(node, this.cleanTableWidths);
22392         
22393         
22394     },
22395     
22396     
22397     
22398     
22399     domToHTML : function(currentElement, depth, nopadtext) {
22400         
22401         depth = depth || 0;
22402         nopadtext = nopadtext || false;
22403     
22404         if (!currentElement) {
22405             return this.domToHTML(this.doc.body);
22406         }
22407         
22408         //Roo.log(currentElement);
22409         var j;
22410         var allText = false;
22411         var nodeName = currentElement.nodeName;
22412         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22413         
22414         if  (nodeName == '#text') {
22415             
22416             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22417         }
22418         
22419         
22420         var ret = '';
22421         if (nodeName != 'BODY') {
22422              
22423             var i = 0;
22424             // Prints the node tagName, such as <A>, <IMG>, etc
22425             if (tagName) {
22426                 var attr = [];
22427                 for(i = 0; i < currentElement.attributes.length;i++) {
22428                     // quoting?
22429                     var aname = currentElement.attributes.item(i).name;
22430                     if (!currentElement.attributes.item(i).value.length) {
22431                         continue;
22432                     }
22433                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22434                 }
22435                 
22436                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22437             } 
22438             else {
22439                 
22440                 // eack
22441             }
22442         } else {
22443             tagName = false;
22444         }
22445         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22446             return ret;
22447         }
22448         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22449             nopadtext = true;
22450         }
22451         
22452         
22453         // Traverse the tree
22454         i = 0;
22455         var currentElementChild = currentElement.childNodes.item(i);
22456         var allText = true;
22457         var innerHTML  = '';
22458         lastnode = '';
22459         while (currentElementChild) {
22460             // Formatting code (indent the tree so it looks nice on the screen)
22461             var nopad = nopadtext;
22462             if (lastnode == 'SPAN') {
22463                 nopad  = true;
22464             }
22465             // text
22466             if  (currentElementChild.nodeName == '#text') {
22467                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22468                 toadd = nopadtext ? toadd : toadd.trim();
22469                 if (!nopad && toadd.length > 80) {
22470                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22471                 }
22472                 innerHTML  += toadd;
22473                 
22474                 i++;
22475                 currentElementChild = currentElement.childNodes.item(i);
22476                 lastNode = '';
22477                 continue;
22478             }
22479             allText = false;
22480             
22481             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22482                 
22483             // Recursively traverse the tree structure of the child node
22484             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22485             lastnode = currentElementChild.nodeName;
22486             i++;
22487             currentElementChild=currentElement.childNodes.item(i);
22488         }
22489         
22490         ret += innerHTML;
22491         
22492         if (!allText) {
22493                 // The remaining code is mostly for formatting the tree
22494             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22495         }
22496         
22497         
22498         if (tagName) {
22499             ret+= "</"+tagName+">";
22500         }
22501         return ret;
22502         
22503     },
22504         
22505     applyBlacklists : function()
22506     {
22507         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22508         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22509         
22510         this.white = [];
22511         this.black = [];
22512         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22513             if (b.indexOf(tag) > -1) {
22514                 return;
22515             }
22516             this.white.push(tag);
22517             
22518         }, this);
22519         
22520         Roo.each(w, function(tag) {
22521             if (b.indexOf(tag) > -1) {
22522                 return;
22523             }
22524             if (this.white.indexOf(tag) > -1) {
22525                 return;
22526             }
22527             this.white.push(tag);
22528             
22529         }, this);
22530         
22531         
22532         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22533             if (w.indexOf(tag) > -1) {
22534                 return;
22535             }
22536             this.black.push(tag);
22537             
22538         }, this);
22539         
22540         Roo.each(b, function(tag) {
22541             if (w.indexOf(tag) > -1) {
22542                 return;
22543             }
22544             if (this.black.indexOf(tag) > -1) {
22545                 return;
22546             }
22547             this.black.push(tag);
22548             
22549         }, this);
22550         
22551         
22552         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22553         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22554         
22555         this.cwhite = [];
22556         this.cblack = [];
22557         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22558             if (b.indexOf(tag) > -1) {
22559                 return;
22560             }
22561             this.cwhite.push(tag);
22562             
22563         }, this);
22564         
22565         Roo.each(w, function(tag) {
22566             if (b.indexOf(tag) > -1) {
22567                 return;
22568             }
22569             if (this.cwhite.indexOf(tag) > -1) {
22570                 return;
22571             }
22572             this.cwhite.push(tag);
22573             
22574         }, this);
22575         
22576         
22577         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22578             if (w.indexOf(tag) > -1) {
22579                 return;
22580             }
22581             this.cblack.push(tag);
22582             
22583         }, this);
22584         
22585         Roo.each(b, function(tag) {
22586             if (w.indexOf(tag) > -1) {
22587                 return;
22588             }
22589             if (this.cblack.indexOf(tag) > -1) {
22590                 return;
22591             }
22592             this.cblack.push(tag);
22593             
22594         }, this);
22595     },
22596     
22597     setStylesheets : function(stylesheets)
22598     {
22599         if(typeof(stylesheets) == 'string'){
22600             Roo.get(this.iframe.contentDocument.head).createChild({
22601                 tag : 'link',
22602                 rel : 'stylesheet',
22603                 type : 'text/css',
22604                 href : stylesheets
22605             });
22606             
22607             return;
22608         }
22609         var _this = this;
22610      
22611         Roo.each(stylesheets, function(s) {
22612             if(!s.length){
22613                 return;
22614             }
22615             
22616             Roo.get(_this.iframe.contentDocument.head).createChild({
22617                 tag : 'link',
22618                 rel : 'stylesheet',
22619                 type : 'text/css',
22620                 href : s
22621             });
22622         });
22623
22624         
22625     },
22626     
22627     removeStylesheets : function()
22628     {
22629         var _this = this;
22630         
22631         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22632             s.remove();
22633         });
22634     }
22635     
22636     // hide stuff that is not compatible
22637     /**
22638      * @event blur
22639      * @hide
22640      */
22641     /**
22642      * @event change
22643      * @hide
22644      */
22645     /**
22646      * @event focus
22647      * @hide
22648      */
22649     /**
22650      * @event specialkey
22651      * @hide
22652      */
22653     /**
22654      * @cfg {String} fieldClass @hide
22655      */
22656     /**
22657      * @cfg {String} focusClass @hide
22658      */
22659     /**
22660      * @cfg {String} autoCreate @hide
22661      */
22662     /**
22663      * @cfg {String} inputType @hide
22664      */
22665     /**
22666      * @cfg {String} invalidClass @hide
22667      */
22668     /**
22669      * @cfg {String} invalidText @hide
22670      */
22671     /**
22672      * @cfg {String} msgFx @hide
22673      */
22674     /**
22675      * @cfg {String} validateOnBlur @hide
22676      */
22677 });
22678
22679 Roo.HtmlEditorCore.white = [
22680         'area', 'br', 'img', 'input', 'hr', 'wbr',
22681         
22682        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22683        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22684        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22685        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22686        'table',   'ul',         'xmp', 
22687        
22688        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22689       'thead',   'tr', 
22690      
22691       'dir', 'menu', 'ol', 'ul', 'dl',
22692        
22693       'embed',  'object'
22694 ];
22695
22696
22697 Roo.HtmlEditorCore.black = [
22698     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22699         'applet', // 
22700         'base',   'basefont', 'bgsound', 'blink',  'body', 
22701         'frame',  'frameset', 'head',    'html',   'ilayer', 
22702         'iframe', 'layer',  'link',     'meta',    'object',   
22703         'script', 'style' ,'title',  'xml' // clean later..
22704 ];
22705 Roo.HtmlEditorCore.clean = [
22706     'script', 'style', 'title', 'xml'
22707 ];
22708 Roo.HtmlEditorCore.remove = [
22709     'font'
22710 ];
22711 // attributes..
22712
22713 Roo.HtmlEditorCore.ablack = [
22714     'on'
22715 ];
22716     
22717 Roo.HtmlEditorCore.aclean = [ 
22718     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22719 ];
22720
22721 // protocols..
22722 Roo.HtmlEditorCore.pwhite= [
22723         'http',  'https',  'mailto'
22724 ];
22725
22726 // white listed style attributes.
22727 Roo.HtmlEditorCore.cwhite= [
22728       //  'text-align', /// default is to allow most things..
22729       
22730          
22731 //        'font-size'//??
22732 ];
22733
22734 // black listed style attributes.
22735 Roo.HtmlEditorCore.cblack= [
22736       //  'font-size' -- this can be set by the project 
22737 ];
22738
22739
22740 Roo.HtmlEditorCore.swapCodes   =[ 
22741     [    8211, "--" ], 
22742     [    8212, "--" ], 
22743     [    8216,  "'" ],  
22744     [    8217, "'" ],  
22745     [    8220, '"' ],  
22746     [    8221, '"' ],  
22747     [    8226, "*" ],  
22748     [    8230, "..." ]
22749 ]; 
22750
22751     /*
22752  * - LGPL
22753  *
22754  * HtmlEditor
22755  * 
22756  */
22757
22758 /**
22759  * @class Roo.bootstrap.HtmlEditor
22760  * @extends Roo.bootstrap.TextArea
22761  * Bootstrap HtmlEditor class
22762
22763  * @constructor
22764  * Create a new HtmlEditor
22765  * @param {Object} config The config object
22766  */
22767
22768 Roo.bootstrap.HtmlEditor = function(config){
22769     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
22770     if (!this.toolbars) {
22771         this.toolbars = [];
22772     }
22773     
22774     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22775     this.addEvents({
22776             /**
22777              * @event initialize
22778              * Fires when the editor is fully initialized (including the iframe)
22779              * @param {HtmlEditor} this
22780              */
22781             initialize: true,
22782             /**
22783              * @event activate
22784              * Fires when the editor is first receives the focus. Any insertion must wait
22785              * until after this event.
22786              * @param {HtmlEditor} this
22787              */
22788             activate: true,
22789              /**
22790              * @event beforesync
22791              * Fires before the textarea is updated with content from the editor iframe. Return false
22792              * to cancel the sync.
22793              * @param {HtmlEditor} this
22794              * @param {String} html
22795              */
22796             beforesync: true,
22797              /**
22798              * @event beforepush
22799              * Fires before the iframe editor is updated with content from the textarea. Return false
22800              * to cancel the push.
22801              * @param {HtmlEditor} this
22802              * @param {String} html
22803              */
22804             beforepush: true,
22805              /**
22806              * @event sync
22807              * Fires when the textarea is updated with content from the editor iframe.
22808              * @param {HtmlEditor} this
22809              * @param {String} html
22810              */
22811             sync: true,
22812              /**
22813              * @event push
22814              * Fires when the iframe editor is updated with content from the textarea.
22815              * @param {HtmlEditor} this
22816              * @param {String} html
22817              */
22818             push: true,
22819              /**
22820              * @event editmodechange
22821              * Fires when the editor switches edit modes
22822              * @param {HtmlEditor} this
22823              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22824              */
22825             editmodechange: true,
22826             /**
22827              * @event editorevent
22828              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22829              * @param {HtmlEditor} this
22830              */
22831             editorevent: true,
22832             /**
22833              * @event firstfocus
22834              * Fires when on first focus - needed by toolbars..
22835              * @param {HtmlEditor} this
22836              */
22837             firstfocus: true,
22838             /**
22839              * @event autosave
22840              * Auto save the htmlEditor value as a file into Events
22841              * @param {HtmlEditor} this
22842              */
22843             autosave: true,
22844             /**
22845              * @event savedpreview
22846              * preview the saved version of htmlEditor
22847              * @param {HtmlEditor} this
22848              */
22849             savedpreview: true
22850         });
22851 };
22852
22853
22854 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
22855     
22856     
22857       /**
22858      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22859      */
22860     toolbars : false,
22861     
22862      /**
22863     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
22864     */
22865     btns : [],
22866    
22867      /**
22868      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22869      *                        Roo.resizable.
22870      */
22871     resizable : false,
22872      /**
22873      * @cfg {Number} height (in pixels)
22874      */   
22875     height: 300,
22876    /**
22877      * @cfg {Number} width (in pixels)
22878      */   
22879     width: false,
22880     
22881     /**
22882      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22883      * 
22884      */
22885     stylesheets: false,
22886     
22887     // id of frame..
22888     frameId: false,
22889     
22890     // private properties
22891     validationEvent : false,
22892     deferHeight: true,
22893     initialized : false,
22894     activated : false,
22895     
22896     onFocus : Roo.emptyFn,
22897     iframePad:3,
22898     hideMode:'offsets',
22899     
22900     tbContainer : false,
22901     
22902     toolbarContainer :function() {
22903         return this.wrap.select('.x-html-editor-tb',true).first();
22904     },
22905
22906     /**
22907      * Protected method that will not generally be called directly. It
22908      * is called when the editor creates its toolbar. Override this method if you need to
22909      * add custom toolbar buttons.
22910      * @param {HtmlEditor} editor
22911      */
22912     createToolbar : function(){
22913         Roo.log('renewing');
22914         Roo.log("create toolbars");
22915         
22916         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
22917         this.toolbars[0].render(this.toolbarContainer());
22918         
22919         return;
22920         
22921 //        if (!editor.toolbars || !editor.toolbars.length) {
22922 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
22923 //        }
22924 //        
22925 //        for (var i =0 ; i < editor.toolbars.length;i++) {
22926 //            editor.toolbars[i] = Roo.factory(
22927 //                    typeof(editor.toolbars[i]) == 'string' ?
22928 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
22929 //                Roo.bootstrap.HtmlEditor);
22930 //            editor.toolbars[i].init(editor);
22931 //        }
22932     },
22933
22934      
22935     // private
22936     onRender : function(ct, position)
22937     {
22938        // Roo.log("Call onRender: " + this.xtype);
22939         var _t = this;
22940         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
22941       
22942         this.wrap = this.inputEl().wrap({
22943             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22944         });
22945         
22946         this.editorcore.onRender(ct, position);
22947          
22948         if (this.resizable) {
22949             this.resizeEl = new Roo.Resizable(this.wrap, {
22950                 pinned : true,
22951                 wrap: true,
22952                 dynamic : true,
22953                 minHeight : this.height,
22954                 height: this.height,
22955                 handles : this.resizable,
22956                 width: this.width,
22957                 listeners : {
22958                     resize : function(r, w, h) {
22959                         _t.onResize(w,h); // -something
22960                     }
22961                 }
22962             });
22963             
22964         }
22965         this.createToolbar(this);
22966        
22967         
22968         if(!this.width && this.resizable){
22969             this.setSize(this.wrap.getSize());
22970         }
22971         if (this.resizeEl) {
22972             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22973             // should trigger onReize..
22974         }
22975         
22976     },
22977
22978     // private
22979     onResize : function(w, h)
22980     {
22981         Roo.log('resize: ' +w + ',' + h );
22982         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
22983         var ew = false;
22984         var eh = false;
22985         
22986         if(this.inputEl() ){
22987             if(typeof w == 'number'){
22988                 var aw = w - this.wrap.getFrameWidth('lr');
22989                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
22990                 ew = aw;
22991             }
22992             if(typeof h == 'number'){
22993                  var tbh = -11;  // fixme it needs to tool bar size!
22994                 for (var i =0; i < this.toolbars.length;i++) {
22995                     // fixme - ask toolbars for heights?
22996                     tbh += this.toolbars[i].el.getHeight();
22997                     //if (this.toolbars[i].footer) {
22998                     //    tbh += this.toolbars[i].footer.el.getHeight();
22999                     //}
23000                 }
23001               
23002                 
23003                 
23004                 
23005                 
23006                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23007                 ah -= 5; // knock a few pixes off for look..
23008                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23009                 var eh = ah;
23010             }
23011         }
23012         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23013         this.editorcore.onResize(ew,eh);
23014         
23015     },
23016
23017     /**
23018      * Toggles the editor between standard and source edit mode.
23019      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23020      */
23021     toggleSourceEdit : function(sourceEditMode)
23022     {
23023         this.editorcore.toggleSourceEdit(sourceEditMode);
23024         
23025         if(this.editorcore.sourceEditMode){
23026             Roo.log('editor - showing textarea');
23027             
23028 //            Roo.log('in');
23029 //            Roo.log(this.syncValue());
23030             this.syncValue();
23031             this.inputEl().removeClass(['hide', 'x-hidden']);
23032             this.inputEl().dom.removeAttribute('tabIndex');
23033             this.inputEl().focus();
23034         }else{
23035             Roo.log('editor - hiding textarea');
23036 //            Roo.log('out')
23037 //            Roo.log(this.pushValue()); 
23038             this.pushValue();
23039             
23040             this.inputEl().addClass(['hide', 'x-hidden']);
23041             this.inputEl().dom.setAttribute('tabIndex', -1);
23042             //this.deferFocus();
23043         }
23044          
23045         if(this.resizable){
23046             this.setSize(this.wrap.getSize());
23047         }
23048         
23049         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23050     },
23051  
23052     // private (for BoxComponent)
23053     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23054
23055     // private (for BoxComponent)
23056     getResizeEl : function(){
23057         return this.wrap;
23058     },
23059
23060     // private (for BoxComponent)
23061     getPositionEl : function(){
23062         return this.wrap;
23063     },
23064
23065     // private
23066     initEvents : function(){
23067         this.originalValue = this.getValue();
23068     },
23069
23070 //    /**
23071 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23072 //     * @method
23073 //     */
23074 //    markInvalid : Roo.emptyFn,
23075 //    /**
23076 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23077 //     * @method
23078 //     */
23079 //    clearInvalid : Roo.emptyFn,
23080
23081     setValue : function(v){
23082         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23083         this.editorcore.pushValue();
23084     },
23085
23086      
23087     // private
23088     deferFocus : function(){
23089         this.focus.defer(10, this);
23090     },
23091
23092     // doc'ed in Field
23093     focus : function(){
23094         this.editorcore.focus();
23095         
23096     },
23097       
23098
23099     // private
23100     onDestroy : function(){
23101         
23102         
23103         
23104         if(this.rendered){
23105             
23106             for (var i =0; i < this.toolbars.length;i++) {
23107                 // fixme - ask toolbars for heights?
23108                 this.toolbars[i].onDestroy();
23109             }
23110             
23111             this.wrap.dom.innerHTML = '';
23112             this.wrap.remove();
23113         }
23114     },
23115
23116     // private
23117     onFirstFocus : function(){
23118         //Roo.log("onFirstFocus");
23119         this.editorcore.onFirstFocus();
23120          for (var i =0; i < this.toolbars.length;i++) {
23121             this.toolbars[i].onFirstFocus();
23122         }
23123         
23124     },
23125     
23126     // private
23127     syncValue : function()
23128     {   
23129         this.editorcore.syncValue();
23130     },
23131     
23132     pushValue : function()
23133     {   
23134         this.editorcore.pushValue();
23135     }
23136      
23137     
23138     // hide stuff that is not compatible
23139     /**
23140      * @event blur
23141      * @hide
23142      */
23143     /**
23144      * @event change
23145      * @hide
23146      */
23147     /**
23148      * @event focus
23149      * @hide
23150      */
23151     /**
23152      * @event specialkey
23153      * @hide
23154      */
23155     /**
23156      * @cfg {String} fieldClass @hide
23157      */
23158     /**
23159      * @cfg {String} focusClass @hide
23160      */
23161     /**
23162      * @cfg {String} autoCreate @hide
23163      */
23164     /**
23165      * @cfg {String} inputType @hide
23166      */
23167     /**
23168      * @cfg {String} invalidClass @hide
23169      */
23170     /**
23171      * @cfg {String} invalidText @hide
23172      */
23173     /**
23174      * @cfg {String} msgFx @hide
23175      */
23176     /**
23177      * @cfg {String} validateOnBlur @hide
23178      */
23179 });
23180  
23181     
23182    
23183    
23184    
23185       
23186 Roo.namespace('Roo.bootstrap.htmleditor');
23187 /**
23188  * @class Roo.bootstrap.HtmlEditorToolbar1
23189  * Basic Toolbar
23190  * 
23191  * Usage:
23192  *
23193  new Roo.bootstrap.HtmlEditor({
23194     ....
23195     toolbars : [
23196         new Roo.bootstrap.HtmlEditorToolbar1({
23197             disable : { fonts: 1 , format: 1, ..., ... , ...],
23198             btns : [ .... ]
23199         })
23200     }
23201      
23202  * 
23203  * @cfg {Object} disable List of elements to disable..
23204  * @cfg {Array} btns List of additional buttons.
23205  * 
23206  * 
23207  * NEEDS Extra CSS? 
23208  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23209  */
23210  
23211 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23212 {
23213     
23214     Roo.apply(this, config);
23215     
23216     // default disabled, based on 'good practice'..
23217     this.disable = this.disable || {};
23218     Roo.applyIf(this.disable, {
23219         fontSize : true,
23220         colors : true,
23221         specialElements : true
23222     });
23223     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23224     
23225     this.editor = config.editor;
23226     this.editorcore = config.editor.editorcore;
23227     
23228     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23229     
23230     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23231     // dont call parent... till later.
23232 }
23233 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23234      
23235     bar : true,
23236     
23237     editor : false,
23238     editorcore : false,
23239     
23240     
23241     formats : [
23242         "p" ,  
23243         "h1","h2","h3","h4","h5","h6", 
23244         "pre", "code", 
23245         "abbr", "acronym", "address", "cite", "samp", "var",
23246         'div','span'
23247     ],
23248     
23249     onRender : function(ct, position)
23250     {
23251        // Roo.log("Call onRender: " + this.xtype);
23252         
23253        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23254        Roo.log(this.el);
23255        this.el.dom.style.marginBottom = '0';
23256        var _this = this;
23257        var editorcore = this.editorcore;
23258        var editor= this.editor;
23259        
23260        var children = [];
23261        var btn = function(id,cmd , toggle, handler, html){
23262        
23263             var  event = toggle ? 'toggle' : 'click';
23264        
23265             var a = {
23266                 size : 'sm',
23267                 xtype: 'Button',
23268                 xns: Roo.bootstrap,
23269                 glyphicon : id,
23270                 cmd : id || cmd,
23271                 enableToggle:toggle !== false,
23272                 html : html || '',
23273                 pressed : toggle ? false : null,
23274                 listeners : {}
23275             };
23276             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23277                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23278             };
23279             children.push(a);
23280             return a;
23281        }
23282        
23283     //    var cb_box = function...
23284         
23285         var style = {
23286                 xtype: 'Button',
23287                 size : 'sm',
23288                 xns: Roo.bootstrap,
23289                 glyphicon : 'font',
23290                 //html : 'submit'
23291                 menu : {
23292                     xtype: 'Menu',
23293                     xns: Roo.bootstrap,
23294                     items:  []
23295                 }
23296         };
23297         Roo.each(this.formats, function(f) {
23298             style.menu.items.push({
23299                 xtype :'MenuItem',
23300                 xns: Roo.bootstrap,
23301                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23302                 tagname : f,
23303                 listeners : {
23304                     click : function()
23305                     {
23306                         editorcore.insertTag(this.tagname);
23307                         editor.focus();
23308                     }
23309                 }
23310                 
23311             });
23312         });
23313         children.push(style);   
23314         
23315         btn('bold',false,true);
23316         btn('italic',false,true);
23317         btn('align-left', 'justifyleft',true);
23318         btn('align-center', 'justifycenter',true);
23319         btn('align-right' , 'justifyright',true);
23320         btn('link', false, false, function(btn) {
23321             //Roo.log("create link?");
23322             var url = prompt(this.createLinkText, this.defaultLinkValue);
23323             if(url && url != 'http:/'+'/'){
23324                 this.editorcore.relayCmd('createlink', url);
23325             }
23326         }),
23327         btn('list','insertunorderedlist',true);
23328         btn('pencil', false,true, function(btn){
23329                 Roo.log(this);
23330                 this.toggleSourceEdit(btn.pressed);
23331         });
23332         
23333         if (this.editor.btns.length > 0) {
23334             for (var i = 0; i<this.editor.btns.length; i++) {
23335                 children.push(this.editor.btns[i]);
23336             }
23337         }
23338         
23339         /*
23340         var cog = {
23341                 xtype: 'Button',
23342                 size : 'sm',
23343                 xns: Roo.bootstrap,
23344                 glyphicon : 'cog',
23345                 //html : 'submit'
23346                 menu : {
23347                     xtype: 'Menu',
23348                     xns: Roo.bootstrap,
23349                     items:  []
23350                 }
23351         };
23352         
23353         cog.menu.items.push({
23354             xtype :'MenuItem',
23355             xns: Roo.bootstrap,
23356             html : Clean styles,
23357             tagname : f,
23358             listeners : {
23359                 click : function()
23360                 {
23361                     editorcore.insertTag(this.tagname);
23362                     editor.focus();
23363                 }
23364             }
23365             
23366         });
23367        */
23368         
23369          
23370        this.xtype = 'NavSimplebar';
23371         
23372         for(var i=0;i< children.length;i++) {
23373             
23374             this.buttons.add(this.addxtypeChild(children[i]));
23375             
23376         }
23377         
23378         editor.on('editorevent', this.updateToolbar, this);
23379     },
23380     onBtnClick : function(id)
23381     {
23382        this.editorcore.relayCmd(id);
23383        this.editorcore.focus();
23384     },
23385     
23386     /**
23387      * Protected method that will not generally be called directly. It triggers
23388      * a toolbar update by reading the markup state of the current selection in the editor.
23389      */
23390     updateToolbar: function(){
23391
23392         if(!this.editorcore.activated){
23393             this.editor.onFirstFocus(); // is this neeed?
23394             return;
23395         }
23396
23397         var btns = this.buttons; 
23398         var doc = this.editorcore.doc;
23399         btns.get('bold').setActive(doc.queryCommandState('bold'));
23400         btns.get('italic').setActive(doc.queryCommandState('italic'));
23401         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23402         
23403         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23404         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23405         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23406         
23407         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23408         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23409          /*
23410         
23411         var ans = this.editorcore.getAllAncestors();
23412         if (this.formatCombo) {
23413             
23414             
23415             var store = this.formatCombo.store;
23416             this.formatCombo.setValue("");
23417             for (var i =0; i < ans.length;i++) {
23418                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23419                     // select it..
23420                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23421                     break;
23422                 }
23423             }
23424         }
23425         
23426         
23427         
23428         // hides menus... - so this cant be on a menu...
23429         Roo.bootstrap.MenuMgr.hideAll();
23430         */
23431         Roo.bootstrap.MenuMgr.hideAll();
23432         //this.editorsyncValue();
23433     },
23434     onFirstFocus: function() {
23435         this.buttons.each(function(item){
23436            item.enable();
23437         });
23438     },
23439     toggleSourceEdit : function(sourceEditMode){
23440         
23441           
23442         if(sourceEditMode){
23443             Roo.log("disabling buttons");
23444            this.buttons.each( function(item){
23445                 if(item.cmd != 'pencil'){
23446                     item.disable();
23447                 }
23448             });
23449           
23450         }else{
23451             Roo.log("enabling buttons");
23452             if(this.editorcore.initialized){
23453                 this.buttons.each( function(item){
23454                     item.enable();
23455                 });
23456             }
23457             
23458         }
23459         Roo.log("calling toggole on editor");
23460         // tell the editor that it's been pressed..
23461         this.editor.toggleSourceEdit(sourceEditMode);
23462        
23463     }
23464 });
23465
23466
23467
23468
23469
23470 /**
23471  * @class Roo.bootstrap.Table.AbstractSelectionModel
23472  * @extends Roo.util.Observable
23473  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23474  * implemented by descendant classes.  This class should not be directly instantiated.
23475  * @constructor
23476  */
23477 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23478     this.locked = false;
23479     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23480 };
23481
23482
23483 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23484     /** @ignore Called by the grid automatically. Do not call directly. */
23485     init : function(grid){
23486         this.grid = grid;
23487         this.initEvents();
23488     },
23489
23490     /**
23491      * Locks the selections.
23492      */
23493     lock : function(){
23494         this.locked = true;
23495     },
23496
23497     /**
23498      * Unlocks the selections.
23499      */
23500     unlock : function(){
23501         this.locked = false;
23502     },
23503
23504     /**
23505      * Returns true if the selections are locked.
23506      * @return {Boolean}
23507      */
23508     isLocked : function(){
23509         return this.locked;
23510     }
23511 });
23512 /**
23513  * @extends Roo.bootstrap.Table.AbstractSelectionModel
23514  * @class Roo.bootstrap.Table.RowSelectionModel
23515  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23516  * It supports multiple selections and keyboard selection/navigation. 
23517  * @constructor
23518  * @param {Object} config
23519  */
23520
23521 Roo.bootstrap.Table.RowSelectionModel = function(config){
23522     Roo.apply(this, config);
23523     this.selections = new Roo.util.MixedCollection(false, function(o){
23524         return o.id;
23525     });
23526
23527     this.last = false;
23528     this.lastActive = false;
23529
23530     this.addEvents({
23531         /**
23532              * @event selectionchange
23533              * Fires when the selection changes
23534              * @param {SelectionModel} this
23535              */
23536             "selectionchange" : true,
23537         /**
23538              * @event afterselectionchange
23539              * Fires after the selection changes (eg. by key press or clicking)
23540              * @param {SelectionModel} this
23541              */
23542             "afterselectionchange" : true,
23543         /**
23544              * @event beforerowselect
23545              * Fires when a row is selected being selected, return false to cancel.
23546              * @param {SelectionModel} this
23547              * @param {Number} rowIndex The selected index
23548              * @param {Boolean} keepExisting False if other selections will be cleared
23549              */
23550             "beforerowselect" : true,
23551         /**
23552              * @event rowselect
23553              * Fires when a row is selected.
23554              * @param {SelectionModel} this
23555              * @param {Number} rowIndex The selected index
23556              * @param {Roo.data.Record} r The record
23557              */
23558             "rowselect" : true,
23559         /**
23560              * @event rowdeselect
23561              * Fires when a row is deselected.
23562              * @param {SelectionModel} this
23563              * @param {Number} rowIndex The selected index
23564              */
23565         "rowdeselect" : true
23566     });
23567     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23568     this.locked = false;
23569  };
23570
23571 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
23572     /**
23573      * @cfg {Boolean} singleSelect
23574      * True to allow selection of only one row at a time (defaults to false)
23575      */
23576     singleSelect : false,
23577
23578     // private
23579     initEvents : function()
23580     {
23581
23582         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23583         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
23584         //}else{ // allow click to work like normal
23585          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
23586         //}
23587         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23588         this.grid.on("rowclick", this.handleMouseDown, this);
23589         
23590         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23591             "up" : function(e){
23592                 if(!e.shiftKey){
23593                     this.selectPrevious(e.shiftKey);
23594                 }else if(this.last !== false && this.lastActive !== false){
23595                     var last = this.last;
23596                     this.selectRange(this.last,  this.lastActive-1);
23597                     this.grid.getView().focusRow(this.lastActive);
23598                     if(last !== false){
23599                         this.last = last;
23600                     }
23601                 }else{
23602                     this.selectFirstRow();
23603                 }
23604                 this.fireEvent("afterselectionchange", this);
23605             },
23606             "down" : function(e){
23607                 if(!e.shiftKey){
23608                     this.selectNext(e.shiftKey);
23609                 }else if(this.last !== false && this.lastActive !== false){
23610                     var last = this.last;
23611                     this.selectRange(this.last,  this.lastActive+1);
23612                     this.grid.getView().focusRow(this.lastActive);
23613                     if(last !== false){
23614                         this.last = last;
23615                     }
23616                 }else{
23617                     this.selectFirstRow();
23618                 }
23619                 this.fireEvent("afterselectionchange", this);
23620             },
23621             scope: this
23622         });
23623         this.grid.store.on('load', function(){
23624             this.selections.clear();
23625         },this);
23626         /*
23627         var view = this.grid.view;
23628         view.on("refresh", this.onRefresh, this);
23629         view.on("rowupdated", this.onRowUpdated, this);
23630         view.on("rowremoved", this.onRemove, this);
23631         */
23632     },
23633
23634     // private
23635     onRefresh : function()
23636     {
23637         var ds = this.grid.store, i, v = this.grid.view;
23638         var s = this.selections;
23639         s.each(function(r){
23640             if((i = ds.indexOfId(r.id)) != -1){
23641                 v.onRowSelect(i);
23642             }else{
23643                 s.remove(r);
23644             }
23645         });
23646     },
23647
23648     // private
23649     onRemove : function(v, index, r){
23650         this.selections.remove(r);
23651     },
23652
23653     // private
23654     onRowUpdated : function(v, index, r){
23655         if(this.isSelected(r)){
23656             v.onRowSelect(index);
23657         }
23658     },
23659
23660     /**
23661      * Select records.
23662      * @param {Array} records The records to select
23663      * @param {Boolean} keepExisting (optional) True to keep existing selections
23664      */
23665     selectRecords : function(records, keepExisting)
23666     {
23667         if(!keepExisting){
23668             this.clearSelections();
23669         }
23670             var ds = this.grid.store;
23671         for(var i = 0, len = records.length; i < len; i++){
23672             this.selectRow(ds.indexOf(records[i]), true);
23673         }
23674     },
23675
23676     /**
23677      * Gets the number of selected rows.
23678      * @return {Number}
23679      */
23680     getCount : function(){
23681         return this.selections.length;
23682     },
23683
23684     /**
23685      * Selects the first row in the grid.
23686      */
23687     selectFirstRow : function(){
23688         this.selectRow(0);
23689     },
23690
23691     /**
23692      * Select the last row.
23693      * @param {Boolean} keepExisting (optional) True to keep existing selections
23694      */
23695     selectLastRow : function(keepExisting){
23696         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
23697         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
23698     },
23699
23700     /**
23701      * Selects the row immediately following the last selected row.
23702      * @param {Boolean} keepExisting (optional) True to keep existing selections
23703      */
23704     selectNext : function(keepExisting)
23705     {
23706             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
23707             this.selectRow(this.last+1, keepExisting);
23708             this.grid.getView().focusRow(this.last);
23709         }
23710     },
23711
23712     /**
23713      * Selects the row that precedes the last selected row.
23714      * @param {Boolean} keepExisting (optional) True to keep existing selections
23715      */
23716     selectPrevious : function(keepExisting){
23717         if(this.last){
23718             this.selectRow(this.last-1, keepExisting);
23719             this.grid.getView().focusRow(this.last);
23720         }
23721     },
23722
23723     /**
23724      * Returns the selected records
23725      * @return {Array} Array of selected records
23726      */
23727     getSelections : function(){
23728         return [].concat(this.selections.items);
23729     },
23730
23731     /**
23732      * Returns the first selected record.
23733      * @return {Record}
23734      */
23735     getSelected : function(){
23736         return this.selections.itemAt(0);
23737     },
23738
23739
23740     /**
23741      * Clears all selections.
23742      */
23743     clearSelections : function(fast)
23744     {
23745         if(this.locked) {
23746             return;
23747         }
23748         if(fast !== true){
23749                 var ds = this.grid.store;
23750             var s = this.selections;
23751             s.each(function(r){
23752                 this.deselectRow(ds.indexOfId(r.id));
23753             }, this);
23754             s.clear();
23755         }else{
23756             this.selections.clear();
23757         }
23758         this.last = false;
23759     },
23760
23761
23762     /**
23763      * Selects all rows.
23764      */
23765     selectAll : function(){
23766         if(this.locked) {
23767             return;
23768         }
23769         this.selections.clear();
23770         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
23771             this.selectRow(i, true);
23772         }
23773     },
23774
23775     /**
23776      * Returns True if there is a selection.
23777      * @return {Boolean}
23778      */
23779     hasSelection : function(){
23780         return this.selections.length > 0;
23781     },
23782
23783     /**
23784      * Returns True if the specified row is selected.
23785      * @param {Number/Record} record The record or index of the record to check
23786      * @return {Boolean}
23787      */
23788     isSelected : function(index){
23789             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
23790         return (r && this.selections.key(r.id) ? true : false);
23791     },
23792
23793     /**
23794      * Returns True if the specified record id is selected.
23795      * @param {String} id The id of record to check
23796      * @return {Boolean}
23797      */
23798     isIdSelected : function(id){
23799         return (this.selections.key(id) ? true : false);
23800     },
23801
23802
23803     // private
23804     handleMouseDBClick : function(e, t){
23805         
23806     },
23807     // private
23808     handleMouseDown : function(e, t)
23809     {
23810             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
23811         if(this.isLocked() || rowIndex < 0 ){
23812             return;
23813         };
23814         if(e.shiftKey && this.last !== false){
23815             var last = this.last;
23816             this.selectRange(last, rowIndex, e.ctrlKey);
23817             this.last = last; // reset the last
23818             t.focus();
23819     
23820         }else{
23821             var isSelected = this.isSelected(rowIndex);
23822             //Roo.log("select row:" + rowIndex);
23823             if(isSelected){
23824                 this.deselectRow(rowIndex);
23825             } else {
23826                         this.selectRow(rowIndex, true);
23827             }
23828     
23829             /*
23830                 if(e.button !== 0 && isSelected){
23831                 alert('rowIndex 2: ' + rowIndex);
23832                     view.focusRow(rowIndex);
23833                 }else if(e.ctrlKey && isSelected){
23834                     this.deselectRow(rowIndex);
23835                 }else if(!isSelected){
23836                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
23837                     view.focusRow(rowIndex);
23838                 }
23839             */
23840         }
23841         this.fireEvent("afterselectionchange", this);
23842     },
23843     // private
23844     handleDragableRowClick :  function(grid, rowIndex, e) 
23845     {
23846         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
23847             this.selectRow(rowIndex, false);
23848             grid.view.focusRow(rowIndex);
23849              this.fireEvent("afterselectionchange", this);
23850         }
23851     },
23852     
23853     /**
23854      * Selects multiple rows.
23855      * @param {Array} rows Array of the indexes of the row to select
23856      * @param {Boolean} keepExisting (optional) True to keep existing selections
23857      */
23858     selectRows : function(rows, keepExisting){
23859         if(!keepExisting){
23860             this.clearSelections();
23861         }
23862         for(var i = 0, len = rows.length; i < len; i++){
23863             this.selectRow(rows[i], true);
23864         }
23865     },
23866
23867     /**
23868      * Selects a range of rows. All rows in between startRow and endRow are also selected.
23869      * @param {Number} startRow The index of the first row in the range
23870      * @param {Number} endRow The index of the last row in the range
23871      * @param {Boolean} keepExisting (optional) True to retain existing selections
23872      */
23873     selectRange : function(startRow, endRow, keepExisting){
23874         if(this.locked) {
23875             return;
23876         }
23877         if(!keepExisting){
23878             this.clearSelections();
23879         }
23880         if(startRow <= endRow){
23881             for(var i = startRow; i <= endRow; i++){
23882                 this.selectRow(i, true);
23883             }
23884         }else{
23885             for(var i = startRow; i >= endRow; i--){
23886                 this.selectRow(i, true);
23887             }
23888         }
23889     },
23890
23891     /**
23892      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
23893      * @param {Number} startRow The index of the first row in the range
23894      * @param {Number} endRow The index of the last row in the range
23895      */
23896     deselectRange : function(startRow, endRow, preventViewNotify){
23897         if(this.locked) {
23898             return;
23899         }
23900         for(var i = startRow; i <= endRow; i++){
23901             this.deselectRow(i, preventViewNotify);
23902         }
23903     },
23904
23905     /**
23906      * Selects a row.
23907      * @param {Number} row The index of the row to select
23908      * @param {Boolean} keepExisting (optional) True to keep existing selections
23909      */
23910     selectRow : function(index, keepExisting, preventViewNotify)
23911     {
23912             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
23913             return;
23914         }
23915         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
23916             if(!keepExisting || this.singleSelect){
23917                 this.clearSelections();
23918             }
23919             
23920             var r = this.grid.store.getAt(index);
23921             //console.log('selectRow - record id :' + r.id);
23922             
23923             this.selections.add(r);
23924             this.last = this.lastActive = index;
23925             if(!preventViewNotify){
23926                 var proxy = new Roo.Element(
23927                                 this.grid.getRowDom(index)
23928                 );
23929                 proxy.addClass('bg-info info');
23930             }
23931             this.fireEvent("rowselect", this, index, r);
23932             this.fireEvent("selectionchange", this);
23933         }
23934     },
23935
23936     /**
23937      * Deselects a row.
23938      * @param {Number} row The index of the row to deselect
23939      */
23940     deselectRow : function(index, preventViewNotify)
23941     {
23942         if(this.locked) {
23943             return;
23944         }
23945         if(this.last == index){
23946             this.last = false;
23947         }
23948         if(this.lastActive == index){
23949             this.lastActive = false;
23950         }
23951         
23952         var r = this.grid.store.getAt(index);
23953         if (!r) {
23954             return;
23955         }
23956         
23957         this.selections.remove(r);
23958         //.console.log('deselectRow - record id :' + r.id);
23959         if(!preventViewNotify){
23960         
23961             var proxy = new Roo.Element(
23962                 this.grid.getRowDom(index)
23963             );
23964             proxy.removeClass('bg-info info');
23965         }
23966         this.fireEvent("rowdeselect", this, index);
23967         this.fireEvent("selectionchange", this);
23968     },
23969
23970     // private
23971     restoreLast : function(){
23972         if(this._last){
23973             this.last = this._last;
23974         }
23975     },
23976
23977     // private
23978     acceptsNav : function(row, col, cm){
23979         return !cm.isHidden(col) && cm.isCellEditable(col, row);
23980     },
23981
23982     // private
23983     onEditorKey : function(field, e){
23984         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
23985         if(k == e.TAB){
23986             e.stopEvent();
23987             ed.completeEdit();
23988             if(e.shiftKey){
23989                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
23990             }else{
23991                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
23992             }
23993         }else if(k == e.ENTER && !e.ctrlKey){
23994             e.stopEvent();
23995             ed.completeEdit();
23996             if(e.shiftKey){
23997                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
23998             }else{
23999                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24000             }
24001         }else if(k == e.ESC){
24002             ed.cancelEdit();
24003         }
24004         if(newCell){
24005             g.startEditing(newCell[0], newCell[1]);
24006         }
24007     }
24008 });
24009 /*
24010  * Based on:
24011  * Ext JS Library 1.1.1
24012  * Copyright(c) 2006-2007, Ext JS, LLC.
24013  *
24014  * Originally Released Under LGPL - original licence link has changed is not relivant.
24015  *
24016  * Fork - LGPL
24017  * <script type="text/javascript">
24018  */
24019  
24020 /**
24021  * @class Roo.bootstrap.PagingToolbar
24022  * @extends Roo.bootstrap.NavSimplebar
24023  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24024  * @constructor
24025  * Create a new PagingToolbar
24026  * @param {Object} config The config object
24027  * @param {Roo.data.Store} store
24028  */
24029 Roo.bootstrap.PagingToolbar = function(config)
24030 {
24031     // old args format still supported... - xtype is prefered..
24032         // created from xtype...
24033     
24034     this.ds = config.dataSource;
24035     
24036     if (config.store && !this.ds) {
24037         this.store= Roo.factory(config.store, Roo.data);
24038         this.ds = this.store;
24039         this.ds.xmodule = this.xmodule || false;
24040     }
24041     
24042     this.toolbarItems = [];
24043     if (config.items) {
24044         this.toolbarItems = config.items;
24045     }
24046     
24047     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24048     
24049     this.cursor = 0;
24050     
24051     if (this.ds) { 
24052         this.bind(this.ds);
24053     }
24054     
24055     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24056     
24057 };
24058
24059 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24060     /**
24061      * @cfg {Roo.data.Store} dataSource
24062      * The underlying data store providing the paged data
24063      */
24064     /**
24065      * @cfg {String/HTMLElement/Element} container
24066      * container The id or element that will contain the toolbar
24067      */
24068     /**
24069      * @cfg {Boolean} displayInfo
24070      * True to display the displayMsg (defaults to false)
24071      */
24072     /**
24073      * @cfg {Number} pageSize
24074      * The number of records to display per page (defaults to 20)
24075      */
24076     pageSize: 20,
24077     /**
24078      * @cfg {String} displayMsg
24079      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24080      */
24081     displayMsg : 'Displaying {0} - {1} of {2}',
24082     /**
24083      * @cfg {String} emptyMsg
24084      * The message to display when no records are found (defaults to "No data to display")
24085      */
24086     emptyMsg : 'No data to display',
24087     /**
24088      * Customizable piece of the default paging text (defaults to "Page")
24089      * @type String
24090      */
24091     beforePageText : "Page",
24092     /**
24093      * Customizable piece of the default paging text (defaults to "of %0")
24094      * @type String
24095      */
24096     afterPageText : "of {0}",
24097     /**
24098      * Customizable piece of the default paging text (defaults to "First Page")
24099      * @type String
24100      */
24101     firstText : "First Page",
24102     /**
24103      * Customizable piece of the default paging text (defaults to "Previous Page")
24104      * @type String
24105      */
24106     prevText : "Previous Page",
24107     /**
24108      * Customizable piece of the default paging text (defaults to "Next Page")
24109      * @type String
24110      */
24111     nextText : "Next Page",
24112     /**
24113      * Customizable piece of the default paging text (defaults to "Last Page")
24114      * @type String
24115      */
24116     lastText : "Last Page",
24117     /**
24118      * Customizable piece of the default paging text (defaults to "Refresh")
24119      * @type String
24120      */
24121     refreshText : "Refresh",
24122
24123     buttons : false,
24124     // private
24125     onRender : function(ct, position) 
24126     {
24127         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24128         this.navgroup.parentId = this.id;
24129         this.navgroup.onRender(this.el, null);
24130         // add the buttons to the navgroup
24131         
24132         if(this.displayInfo){
24133             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24134             this.displayEl = this.el.select('.x-paging-info', true).first();
24135 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24136 //            this.displayEl = navel.el.select('span',true).first();
24137         }
24138         
24139         var _this = this;
24140         
24141         if(this.buttons){
24142             Roo.each(_this.buttons, function(e){ // this might need to use render????
24143                Roo.factory(e).onRender(_this.el, null);
24144             });
24145         }
24146             
24147         Roo.each(_this.toolbarItems, function(e) {
24148             _this.navgroup.addItem(e);
24149         });
24150         
24151         
24152         this.first = this.navgroup.addItem({
24153             tooltip: this.firstText,
24154             cls: "prev",
24155             icon : 'fa fa-backward',
24156             disabled: true,
24157             preventDefault: true,
24158             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24159         });
24160         
24161         this.prev =  this.navgroup.addItem({
24162             tooltip: this.prevText,
24163             cls: "prev",
24164             icon : 'fa fa-step-backward',
24165             disabled: true,
24166             preventDefault: true,
24167             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24168         });
24169     //this.addSeparator();
24170         
24171         
24172         var field = this.navgroup.addItem( {
24173             tagtype : 'span',
24174             cls : 'x-paging-position',
24175             
24176             html : this.beforePageText  +
24177                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24178                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24179          } ); //?? escaped?
24180         
24181         this.field = field.el.select('input', true).first();
24182         this.field.on("keydown", this.onPagingKeydown, this);
24183         this.field.on("focus", function(){this.dom.select();});
24184     
24185     
24186         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24187         //this.field.setHeight(18);
24188         //this.addSeparator();
24189         this.next = this.navgroup.addItem({
24190             tooltip: this.nextText,
24191             cls: "next",
24192             html : ' <i class="fa fa-step-forward">',
24193             disabled: true,
24194             preventDefault: true,
24195             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24196         });
24197         this.last = this.navgroup.addItem({
24198             tooltip: this.lastText,
24199             icon : 'fa fa-forward',
24200             cls: "next",
24201             disabled: true,
24202             preventDefault: true,
24203             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24204         });
24205     //this.addSeparator();
24206         this.loading = this.navgroup.addItem({
24207             tooltip: this.refreshText,
24208             icon: 'fa fa-refresh',
24209             preventDefault: true,
24210             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24211         });
24212         
24213     },
24214
24215     // private
24216     updateInfo : function(){
24217         if(this.displayEl){
24218             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24219             var msg = count == 0 ?
24220                 this.emptyMsg :
24221                 String.format(
24222                     this.displayMsg,
24223                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24224                 );
24225             this.displayEl.update(msg);
24226         }
24227     },
24228
24229     // private
24230     onLoad : function(ds, r, o)
24231     {
24232         this.cursor = o.params ? o.params.start : 0;
24233         var d = this.getPageData(),
24234             ap = d.activePage,
24235             ps = d.pages;
24236         
24237         
24238         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24239         this.field.dom.value = ap;
24240         this.first.setDisabled(ap == 1);
24241         this.prev.setDisabled(ap == 1);
24242         this.next.setDisabled(ap == ps);
24243         this.last.setDisabled(ap == ps);
24244         this.loading.enable();
24245         this.updateInfo();
24246     },
24247
24248     // private
24249     getPageData : function(){
24250         var total = this.ds.getTotalCount();
24251         return {
24252             total : total,
24253             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24254             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24255         };
24256     },
24257
24258     // private
24259     onLoadError : function(){
24260         this.loading.enable();
24261     },
24262
24263     // private
24264     onPagingKeydown : function(e){
24265         var k = e.getKey();
24266         var d = this.getPageData();
24267         if(k == e.RETURN){
24268             var v = this.field.dom.value, pageNum;
24269             if(!v || isNaN(pageNum = parseInt(v, 10))){
24270                 this.field.dom.value = d.activePage;
24271                 return;
24272             }
24273             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24274             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24275             e.stopEvent();
24276         }
24277         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))
24278         {
24279           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24280           this.field.dom.value = pageNum;
24281           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24282           e.stopEvent();
24283         }
24284         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24285         {
24286           var v = this.field.dom.value, pageNum; 
24287           var increment = (e.shiftKey) ? 10 : 1;
24288           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24289                 increment *= -1;
24290           }
24291           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24292             this.field.dom.value = d.activePage;
24293             return;
24294           }
24295           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24296           {
24297             this.field.dom.value = parseInt(v, 10) + increment;
24298             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24299             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24300           }
24301           e.stopEvent();
24302         }
24303     },
24304
24305     // private
24306     beforeLoad : function(){
24307         if(this.loading){
24308             this.loading.disable();
24309         }
24310     },
24311
24312     // private
24313     onClick : function(which){
24314         
24315         var ds = this.ds;
24316         if (!ds) {
24317             return;
24318         }
24319         
24320         switch(which){
24321             case "first":
24322                 ds.load({params:{start: 0, limit: this.pageSize}});
24323             break;
24324             case "prev":
24325                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24326             break;
24327             case "next":
24328                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24329             break;
24330             case "last":
24331                 var total = ds.getTotalCount();
24332                 var extra = total % this.pageSize;
24333                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24334                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24335             break;
24336             case "refresh":
24337                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24338             break;
24339         }
24340     },
24341
24342     /**
24343      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24344      * @param {Roo.data.Store} store The data store to unbind
24345      */
24346     unbind : function(ds){
24347         ds.un("beforeload", this.beforeLoad, this);
24348         ds.un("load", this.onLoad, this);
24349         ds.un("loadexception", this.onLoadError, this);
24350         ds.un("remove", this.updateInfo, this);
24351         ds.un("add", this.updateInfo, this);
24352         this.ds = undefined;
24353     },
24354
24355     /**
24356      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24357      * @param {Roo.data.Store} store The data store to bind
24358      */
24359     bind : function(ds){
24360         ds.on("beforeload", this.beforeLoad, this);
24361         ds.on("load", this.onLoad, this);
24362         ds.on("loadexception", this.onLoadError, this);
24363         ds.on("remove", this.updateInfo, this);
24364         ds.on("add", this.updateInfo, this);
24365         this.ds = ds;
24366     }
24367 });/*
24368  * - LGPL
24369  *
24370  * element
24371  * 
24372  */
24373
24374 /**
24375  * @class Roo.bootstrap.MessageBar
24376  * @extends Roo.bootstrap.Component
24377  * Bootstrap MessageBar class
24378  * @cfg {String} html contents of the MessageBar
24379  * @cfg {String} weight (info | success | warning | danger) default info
24380  * @cfg {String} beforeClass insert the bar before the given class
24381  * @cfg {Boolean} closable (true | false) default false
24382  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24383  * 
24384  * @constructor
24385  * Create a new Element
24386  * @param {Object} config The config object
24387  */
24388
24389 Roo.bootstrap.MessageBar = function(config){
24390     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24391 };
24392
24393 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24394     
24395     html: '',
24396     weight: 'info',
24397     closable: false,
24398     fixed: false,
24399     beforeClass: 'bootstrap-sticky-wrap',
24400     
24401     getAutoCreate : function(){
24402         
24403         var cfg = {
24404             tag: 'div',
24405             cls: 'alert alert-dismissable alert-' + this.weight,
24406             cn: [
24407                 {
24408                     tag: 'span',
24409                     cls: 'message',
24410                     html: this.html || ''
24411                 }
24412             ]
24413         };
24414         
24415         if(this.fixed){
24416             cfg.cls += ' alert-messages-fixed';
24417         }
24418         
24419         if(this.closable){
24420             cfg.cn.push({
24421                 tag: 'button',
24422                 cls: 'close',
24423                 html: 'x'
24424             });
24425         }
24426         
24427         return cfg;
24428     },
24429     
24430     onRender : function(ct, position)
24431     {
24432         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24433         
24434         if(!this.el){
24435             var cfg = Roo.apply({},  this.getAutoCreate());
24436             cfg.id = Roo.id();
24437             
24438             if (this.cls) {
24439                 cfg.cls += ' ' + this.cls;
24440             }
24441             if (this.style) {
24442                 cfg.style = this.style;
24443             }
24444             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24445             
24446             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24447         }
24448         
24449         this.el.select('>button.close').on('click', this.hide, this);
24450         
24451     },
24452     
24453     show : function()
24454     {
24455         if (!this.rendered) {
24456             this.render();
24457         }
24458         
24459         this.el.show();
24460         
24461         this.fireEvent('show', this);
24462         
24463     },
24464     
24465     hide : function()
24466     {
24467         if (!this.rendered) {
24468             this.render();
24469         }
24470         
24471         this.el.hide();
24472         
24473         this.fireEvent('hide', this);
24474     },
24475     
24476     update : function()
24477     {
24478 //        var e = this.el.dom.firstChild;
24479 //        
24480 //        if(this.closable){
24481 //            e = e.nextSibling;
24482 //        }
24483 //        
24484 //        e.data = this.html || '';
24485
24486         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24487     }
24488    
24489 });
24490
24491  
24492
24493      /*
24494  * - LGPL
24495  *
24496  * Graph
24497  * 
24498  */
24499
24500
24501 /**
24502  * @class Roo.bootstrap.Graph
24503  * @extends Roo.bootstrap.Component
24504  * Bootstrap Graph class
24505 > Prameters
24506  -sm {number} sm 4
24507  -md {number} md 5
24508  @cfg {String} graphtype  bar | vbar | pie
24509  @cfg {number} g_x coodinator | centre x (pie)
24510  @cfg {number} g_y coodinator | centre y (pie)
24511  @cfg {number} g_r radius (pie)
24512  @cfg {number} g_height height of the chart (respected by all elements in the set)
24513  @cfg {number} g_width width of the chart (respected by all elements in the set)
24514  @cfg {Object} title The title of the chart
24515     
24516  -{Array}  values
24517  -opts (object) options for the chart 
24518      o {
24519      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24520      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24521      o vgutter (number)
24522      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.
24523      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24524      o to
24525      o stretch (boolean)
24526      o }
24527  -opts (object) options for the pie
24528      o{
24529      o cut
24530      o startAngle (number)
24531      o endAngle (number)
24532      } 
24533  *
24534  * @constructor
24535  * Create a new Input
24536  * @param {Object} config The config object
24537  */
24538
24539 Roo.bootstrap.Graph = function(config){
24540     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24541     
24542     this.addEvents({
24543         // img events
24544         /**
24545          * @event click
24546          * The img click event for the img.
24547          * @param {Roo.EventObject} e
24548          */
24549         "click" : true
24550     });
24551 };
24552
24553 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
24554     
24555     sm: 4,
24556     md: 5,
24557     graphtype: 'bar',
24558     g_height: 250,
24559     g_width: 400,
24560     g_x: 50,
24561     g_y: 50,
24562     g_r: 30,
24563     opts:{
24564         //g_colors: this.colors,
24565         g_type: 'soft',
24566         g_gutter: '20%'
24567
24568     },
24569     title : false,
24570
24571     getAutoCreate : function(){
24572         
24573         var cfg = {
24574             tag: 'div',
24575             html : null
24576         };
24577         
24578         
24579         return  cfg;
24580     },
24581
24582     onRender : function(ct,position){
24583         
24584         
24585         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24586         
24587         if (typeof(Raphael) == 'undefined') {
24588             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24589             return;
24590         }
24591         
24592         this.raphael = Raphael(this.el.dom);
24593         
24594                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24595                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24596                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24597                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24598                 /*
24599                 r.text(160, 10, "Single Series Chart").attr(txtattr);
24600                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24601                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24602                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
24603                 
24604                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
24605                 r.barchart(330, 10, 300, 220, data1);
24606                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
24607                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
24608                 */
24609                 
24610                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24611                 // r.barchart(30, 30, 560, 250,  xdata, {
24612                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
24613                 //     axis : "0 0 1 1",
24614                 //     axisxlabels :  xdata
24615                 //     //yvalues : cols,
24616                    
24617                 // });
24618 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24619 //        
24620 //        this.load(null,xdata,{
24621 //                axis : "0 0 1 1",
24622 //                axisxlabels :  xdata
24623 //                });
24624
24625     },
24626
24627     load : function(graphtype,xdata,opts)
24628     {
24629         this.raphael.clear();
24630         if(!graphtype) {
24631             graphtype = this.graphtype;
24632         }
24633         if(!opts){
24634             opts = this.opts;
24635         }
24636         var r = this.raphael,
24637             fin = function () {
24638                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
24639             },
24640             fout = function () {
24641                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
24642             },
24643             pfin = function() {
24644                 this.sector.stop();
24645                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
24646
24647                 if (this.label) {
24648                     this.label[0].stop();
24649                     this.label[0].attr({ r: 7.5 });
24650                     this.label[1].attr({ "font-weight": 800 });
24651                 }
24652             },
24653             pfout = function() {
24654                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
24655
24656                 if (this.label) {
24657                     this.label[0].animate({ r: 5 }, 500, "bounce");
24658                     this.label[1].attr({ "font-weight": 400 });
24659                 }
24660             };
24661
24662         switch(graphtype){
24663             case 'bar':
24664                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24665                 break;
24666             case 'hbar':
24667                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24668                 break;
24669             case 'pie':
24670 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
24671 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
24672 //            
24673                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
24674                 
24675                 break;
24676
24677         }
24678         
24679         if(this.title){
24680             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
24681         }
24682         
24683     },
24684     
24685     setTitle: function(o)
24686     {
24687         this.title = o;
24688     },
24689     
24690     initEvents: function() {
24691         
24692         if(!this.href){
24693             this.el.on('click', this.onClick, this);
24694         }
24695     },
24696     
24697     onClick : function(e)
24698     {
24699         Roo.log('img onclick');
24700         this.fireEvent('click', this, e);
24701     }
24702    
24703 });
24704
24705  
24706 /*
24707  * - LGPL
24708  *
24709  * numberBox
24710  * 
24711  */
24712 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24713
24714 /**
24715  * @class Roo.bootstrap.dash.NumberBox
24716  * @extends Roo.bootstrap.Component
24717  * Bootstrap NumberBox class
24718  * @cfg {String} headline Box headline
24719  * @cfg {String} content Box content
24720  * @cfg {String} icon Box icon
24721  * @cfg {String} footer Footer text
24722  * @cfg {String} fhref Footer href
24723  * 
24724  * @constructor
24725  * Create a new NumberBox
24726  * @param {Object} config The config object
24727  */
24728
24729
24730 Roo.bootstrap.dash.NumberBox = function(config){
24731     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
24732     
24733 };
24734
24735 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
24736     
24737     headline : '',
24738     content : '',
24739     icon : '',
24740     footer : '',
24741     fhref : '',
24742     ficon : '',
24743     
24744     getAutoCreate : function(){
24745         
24746         var cfg = {
24747             tag : 'div',
24748             cls : 'small-box ',
24749             cn : [
24750                 {
24751                     tag : 'div',
24752                     cls : 'inner',
24753                     cn :[
24754                         {
24755                             tag : 'h3',
24756                             cls : 'roo-headline',
24757                             html : this.headline
24758                         },
24759                         {
24760                             tag : 'p',
24761                             cls : 'roo-content',
24762                             html : this.content
24763                         }
24764                     ]
24765                 }
24766             ]
24767         };
24768         
24769         if(this.icon){
24770             cfg.cn.push({
24771                 tag : 'div',
24772                 cls : 'icon',
24773                 cn :[
24774                     {
24775                         tag : 'i',
24776                         cls : 'ion ' + this.icon
24777                     }
24778                 ]
24779             });
24780         }
24781         
24782         if(this.footer){
24783             var footer = {
24784                 tag : 'a',
24785                 cls : 'small-box-footer',
24786                 href : this.fhref || '#',
24787                 html : this.footer
24788             };
24789             
24790             cfg.cn.push(footer);
24791             
24792         }
24793         
24794         return  cfg;
24795     },
24796
24797     onRender : function(ct,position){
24798         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
24799
24800
24801        
24802                 
24803     },
24804
24805     setHeadline: function (value)
24806     {
24807         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
24808     },
24809     
24810     setFooter: function (value, href)
24811     {
24812         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
24813         
24814         if(href){
24815             this.el.select('a.small-box-footer',true).first().attr('href', href);
24816         }
24817         
24818     },
24819
24820     setContent: function (value)
24821     {
24822         this.el.select('.roo-content',true).first().dom.innerHTML = value;
24823     },
24824
24825     initEvents: function() 
24826     {   
24827         
24828     }
24829     
24830 });
24831
24832  
24833 /*
24834  * - LGPL
24835  *
24836  * TabBox
24837  * 
24838  */
24839 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24840
24841 /**
24842  * @class Roo.bootstrap.dash.TabBox
24843  * @extends Roo.bootstrap.Component
24844  * Bootstrap TabBox class
24845  * @cfg {String} title Title of the TabBox
24846  * @cfg {String} icon Icon of the TabBox
24847  * @cfg {Boolean} showtabs (true|false) show the tabs default true
24848  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
24849  * 
24850  * @constructor
24851  * Create a new TabBox
24852  * @param {Object} config The config object
24853  */
24854
24855
24856 Roo.bootstrap.dash.TabBox = function(config){
24857     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
24858     this.addEvents({
24859         // raw events
24860         /**
24861          * @event addpane
24862          * When a pane is added
24863          * @param {Roo.bootstrap.dash.TabPane} pane
24864          */
24865         "addpane" : true,
24866         /**
24867          * @event activatepane
24868          * When a pane is activated
24869          * @param {Roo.bootstrap.dash.TabPane} pane
24870          */
24871         "activatepane" : true
24872         
24873          
24874     });
24875     
24876     this.panes = [];
24877 };
24878
24879 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
24880
24881     title : '',
24882     icon : false,
24883     showtabs : true,
24884     tabScrollable : false,
24885     
24886     getChildContainer : function()
24887     {
24888         return this.el.select('.tab-content', true).first();
24889     },
24890     
24891     getAutoCreate : function(){
24892         
24893         var header = {
24894             tag: 'li',
24895             cls: 'pull-left header',
24896             html: this.title,
24897             cn : []
24898         };
24899         
24900         if(this.icon){
24901             header.cn.push({
24902                 tag: 'i',
24903                 cls: 'fa ' + this.icon
24904             });
24905         }
24906         
24907         var h = {
24908             tag: 'ul',
24909             cls: 'nav nav-tabs pull-right',
24910             cn: [
24911                 header
24912             ]
24913         };
24914         
24915         if(this.tabScrollable){
24916             h = {
24917                 tag: 'div',
24918                 cls: 'tab-header',
24919                 cn: [
24920                     {
24921                         tag: 'ul',
24922                         cls: 'nav nav-tabs pull-right',
24923                         cn: [
24924                             header
24925                         ]
24926                     }
24927                 ]
24928             };
24929         }
24930         
24931         var cfg = {
24932             tag: 'div',
24933             cls: 'nav-tabs-custom',
24934             cn: [
24935                 h,
24936                 {
24937                     tag: 'div',
24938                     cls: 'tab-content no-padding',
24939                     cn: []
24940                 }
24941             ]
24942         };
24943
24944         return  cfg;
24945     },
24946     initEvents : function()
24947     {
24948         //Roo.log('add add pane handler');
24949         this.on('addpane', this.onAddPane, this);
24950     },
24951      /**
24952      * Updates the box title
24953      * @param {String} html to set the title to.
24954      */
24955     setTitle : function(value)
24956     {
24957         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
24958     },
24959     onAddPane : function(pane)
24960     {
24961         this.panes.push(pane);
24962         //Roo.log('addpane');
24963         //Roo.log(pane);
24964         // tabs are rendere left to right..
24965         if(!this.showtabs){
24966             return;
24967         }
24968         
24969         var ctr = this.el.select('.nav-tabs', true).first();
24970          
24971          
24972         var existing = ctr.select('.nav-tab',true);
24973         var qty = existing.getCount();;
24974         
24975         
24976         var tab = ctr.createChild({
24977             tag : 'li',
24978             cls : 'nav-tab' + (qty ? '' : ' active'),
24979             cn : [
24980                 {
24981                     tag : 'a',
24982                     href:'#',
24983                     html : pane.title
24984                 }
24985             ]
24986         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
24987         pane.tab = tab;
24988         
24989         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
24990         if (!qty) {
24991             pane.el.addClass('active');
24992         }
24993         
24994                 
24995     },
24996     onTabClick : function(ev,un,ob,pane)
24997     {
24998         //Roo.log('tab - prev default');
24999         ev.preventDefault();
25000         
25001         
25002         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25003         pane.tab.addClass('active');
25004         //Roo.log(pane.title);
25005         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25006         // technically we should have a deactivate event.. but maybe add later.
25007         // and it should not de-activate the selected tab...
25008         this.fireEvent('activatepane', pane);
25009         pane.el.addClass('active');
25010         pane.fireEvent('activate');
25011         
25012         
25013     },
25014     
25015     getActivePane : function()
25016     {
25017         var r = false;
25018         Roo.each(this.panes, function(p) {
25019             if(p.el.hasClass('active')){
25020                 r = p;
25021                 return false;
25022             }
25023             
25024             return;
25025         });
25026         
25027         return r;
25028     }
25029     
25030     
25031 });
25032
25033  
25034 /*
25035  * - LGPL
25036  *
25037  * Tab pane
25038  * 
25039  */
25040 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25041 /**
25042  * @class Roo.bootstrap.TabPane
25043  * @extends Roo.bootstrap.Component
25044  * Bootstrap TabPane class
25045  * @cfg {Boolean} active (false | true) Default false
25046  * @cfg {String} title title of panel
25047
25048  * 
25049  * @constructor
25050  * Create a new TabPane
25051  * @param {Object} config The config object
25052  */
25053
25054 Roo.bootstrap.dash.TabPane = function(config){
25055     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25056     
25057     this.addEvents({
25058         // raw events
25059         /**
25060          * @event activate
25061          * When a pane is activated
25062          * @param {Roo.bootstrap.dash.TabPane} pane
25063          */
25064         "activate" : true
25065          
25066     });
25067 };
25068
25069 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25070     
25071     active : false,
25072     title : '',
25073     
25074     // the tabBox that this is attached to.
25075     tab : false,
25076      
25077     getAutoCreate : function() 
25078     {
25079         var cfg = {
25080             tag: 'div',
25081             cls: 'tab-pane'
25082         };
25083         
25084         if(this.active){
25085             cfg.cls += ' active';
25086         }
25087         
25088         return cfg;
25089     },
25090     initEvents  : function()
25091     {
25092         //Roo.log('trigger add pane handler');
25093         this.parent().fireEvent('addpane', this)
25094     },
25095     
25096      /**
25097      * Updates the tab title 
25098      * @param {String} html to set the title to.
25099      */
25100     setTitle: function(str)
25101     {
25102         if (!this.tab) {
25103             return;
25104         }
25105         this.title = str;
25106         this.tab.select('a', true).first().dom.innerHTML = str;
25107         
25108     }
25109     
25110     
25111     
25112 });
25113
25114  
25115
25116
25117  /*
25118  * - LGPL
25119  *
25120  * menu
25121  * 
25122  */
25123 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25124
25125 /**
25126  * @class Roo.bootstrap.menu.Menu
25127  * @extends Roo.bootstrap.Component
25128  * Bootstrap Menu class - container for Menu
25129  * @cfg {String} html Text of the menu
25130  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25131  * @cfg {String} icon Font awesome icon
25132  * @cfg {String} pos Menu align to (top | bottom) default bottom
25133  * 
25134  * 
25135  * @constructor
25136  * Create a new Menu
25137  * @param {Object} config The config object
25138  */
25139
25140
25141 Roo.bootstrap.menu.Menu = function(config){
25142     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25143     
25144     this.addEvents({
25145         /**
25146          * @event beforeshow
25147          * Fires before this menu is displayed
25148          * @param {Roo.bootstrap.menu.Menu} this
25149          */
25150         beforeshow : true,
25151         /**
25152          * @event beforehide
25153          * Fires before this menu is hidden
25154          * @param {Roo.bootstrap.menu.Menu} this
25155          */
25156         beforehide : true,
25157         /**
25158          * @event show
25159          * Fires after this menu is displayed
25160          * @param {Roo.bootstrap.menu.Menu} this
25161          */
25162         show : true,
25163         /**
25164          * @event hide
25165          * Fires after this menu is hidden
25166          * @param {Roo.bootstrap.menu.Menu} this
25167          */
25168         hide : true,
25169         /**
25170          * @event click
25171          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25172          * @param {Roo.bootstrap.menu.Menu} this
25173          * @param {Roo.EventObject} e
25174          */
25175         click : true
25176     });
25177     
25178 };
25179
25180 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25181     
25182     submenu : false,
25183     html : '',
25184     weight : 'default',
25185     icon : false,
25186     pos : 'bottom',
25187     
25188     
25189     getChildContainer : function() {
25190         if(this.isSubMenu){
25191             return this.el;
25192         }
25193         
25194         return this.el.select('ul.dropdown-menu', true).first();  
25195     },
25196     
25197     getAutoCreate : function()
25198     {
25199         var text = [
25200             {
25201                 tag : 'span',
25202                 cls : 'roo-menu-text',
25203                 html : this.html
25204             }
25205         ];
25206         
25207         if(this.icon){
25208             text.unshift({
25209                 tag : 'i',
25210                 cls : 'fa ' + this.icon
25211             })
25212         }
25213         
25214         
25215         var cfg = {
25216             tag : 'div',
25217             cls : 'btn-group',
25218             cn : [
25219                 {
25220                     tag : 'button',
25221                     cls : 'dropdown-button btn btn-' + this.weight,
25222                     cn : text
25223                 },
25224                 {
25225                     tag : 'button',
25226                     cls : 'dropdown-toggle btn btn-' + this.weight,
25227                     cn : [
25228                         {
25229                             tag : 'span',
25230                             cls : 'caret'
25231                         }
25232                     ]
25233                 },
25234                 {
25235                     tag : 'ul',
25236                     cls : 'dropdown-menu'
25237                 }
25238             ]
25239             
25240         };
25241         
25242         if(this.pos == 'top'){
25243             cfg.cls += ' dropup';
25244         }
25245         
25246         if(this.isSubMenu){
25247             cfg = {
25248                 tag : 'ul',
25249                 cls : 'dropdown-menu'
25250             }
25251         }
25252         
25253         return cfg;
25254     },
25255     
25256     onRender : function(ct, position)
25257     {
25258         this.isSubMenu = ct.hasClass('dropdown-submenu');
25259         
25260         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25261     },
25262     
25263     initEvents : function() 
25264     {
25265         if(this.isSubMenu){
25266             return;
25267         }
25268         
25269         this.hidden = true;
25270         
25271         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25272         this.triggerEl.on('click', this.onTriggerPress, this);
25273         
25274         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25275         this.buttonEl.on('click', this.onClick, this);
25276         
25277     },
25278     
25279     list : function()
25280     {
25281         if(this.isSubMenu){
25282             return this.el;
25283         }
25284         
25285         return this.el.select('ul.dropdown-menu', true).first();
25286     },
25287     
25288     onClick : function(e)
25289     {
25290         this.fireEvent("click", this, e);
25291     },
25292     
25293     onTriggerPress  : function(e)
25294     {   
25295         if (this.isVisible()) {
25296             this.hide();
25297         } else {
25298             this.show();
25299         }
25300     },
25301     
25302     isVisible : function(){
25303         return !this.hidden;
25304     },
25305     
25306     show : function()
25307     {
25308         this.fireEvent("beforeshow", this);
25309         
25310         this.hidden = false;
25311         this.el.addClass('open');
25312         
25313         Roo.get(document).on("mouseup", this.onMouseUp, this);
25314         
25315         this.fireEvent("show", this);
25316         
25317         
25318     },
25319     
25320     hide : function()
25321     {
25322         this.fireEvent("beforehide", this);
25323         
25324         this.hidden = true;
25325         this.el.removeClass('open');
25326         
25327         Roo.get(document).un("mouseup", this.onMouseUp);
25328         
25329         this.fireEvent("hide", this);
25330     },
25331     
25332     onMouseUp : function()
25333     {
25334         this.hide();
25335     }
25336     
25337 });
25338
25339  
25340  /*
25341  * - LGPL
25342  *
25343  * menu item
25344  * 
25345  */
25346 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25347
25348 /**
25349  * @class Roo.bootstrap.menu.Item
25350  * @extends Roo.bootstrap.Component
25351  * Bootstrap MenuItem class
25352  * @cfg {Boolean} submenu (true | false) default false
25353  * @cfg {String} html text of the item
25354  * @cfg {String} href the link
25355  * @cfg {Boolean} disable (true | false) default false
25356  * @cfg {Boolean} preventDefault (true | false) default true
25357  * @cfg {String} icon Font awesome icon
25358  * @cfg {String} pos Submenu align to (left | right) default right 
25359  * 
25360  * 
25361  * @constructor
25362  * Create a new Item
25363  * @param {Object} config The config object
25364  */
25365
25366
25367 Roo.bootstrap.menu.Item = function(config){
25368     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25369     this.addEvents({
25370         /**
25371          * @event mouseover
25372          * Fires when the mouse is hovering over this menu
25373          * @param {Roo.bootstrap.menu.Item} this
25374          * @param {Roo.EventObject} e
25375          */
25376         mouseover : true,
25377         /**
25378          * @event mouseout
25379          * Fires when the mouse exits this menu
25380          * @param {Roo.bootstrap.menu.Item} this
25381          * @param {Roo.EventObject} e
25382          */
25383         mouseout : true,
25384         // raw events
25385         /**
25386          * @event click
25387          * The raw click event for the entire grid.
25388          * @param {Roo.EventObject} e
25389          */
25390         click : true
25391     });
25392 };
25393
25394 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25395     
25396     submenu : false,
25397     href : '',
25398     html : '',
25399     preventDefault: true,
25400     disable : false,
25401     icon : false,
25402     pos : 'right',
25403     
25404     getAutoCreate : function()
25405     {
25406         var text = [
25407             {
25408                 tag : 'span',
25409                 cls : 'roo-menu-item-text',
25410                 html : this.html
25411             }
25412         ];
25413         
25414         if(this.icon){
25415             text.unshift({
25416                 tag : 'i',
25417                 cls : 'fa ' + this.icon
25418             })
25419         }
25420         
25421         var cfg = {
25422             tag : 'li',
25423             cn : [
25424                 {
25425                     tag : 'a',
25426                     href : this.href || '#',
25427                     cn : text
25428                 }
25429             ]
25430         };
25431         
25432         if(this.disable){
25433             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25434         }
25435         
25436         if(this.submenu){
25437             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25438             
25439             if(this.pos == 'left'){
25440                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25441             }
25442         }
25443         
25444         return cfg;
25445     },
25446     
25447     initEvents : function() 
25448     {
25449         this.el.on('mouseover', this.onMouseOver, this);
25450         this.el.on('mouseout', this.onMouseOut, this);
25451         
25452         this.el.select('a', true).first().on('click', this.onClick, this);
25453         
25454     },
25455     
25456     onClick : function(e)
25457     {
25458         if(this.preventDefault){
25459             e.preventDefault();
25460         }
25461         
25462         this.fireEvent("click", this, e);
25463     },
25464     
25465     onMouseOver : function(e)
25466     {
25467         if(this.submenu && this.pos == 'left'){
25468             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25469         }
25470         
25471         this.fireEvent("mouseover", this, e);
25472     },
25473     
25474     onMouseOut : function(e)
25475     {
25476         this.fireEvent("mouseout", this, e);
25477     }
25478 });
25479
25480  
25481
25482  /*
25483  * - LGPL
25484  *
25485  * menu separator
25486  * 
25487  */
25488 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25489
25490 /**
25491  * @class Roo.bootstrap.menu.Separator
25492  * @extends Roo.bootstrap.Component
25493  * Bootstrap Separator class
25494  * 
25495  * @constructor
25496  * Create a new Separator
25497  * @param {Object} config The config object
25498  */
25499
25500
25501 Roo.bootstrap.menu.Separator = function(config){
25502     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25503 };
25504
25505 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
25506     
25507     getAutoCreate : function(){
25508         var cfg = {
25509             tag : 'li',
25510             cls: 'divider'
25511         };
25512         
25513         return cfg;
25514     }
25515    
25516 });
25517
25518  
25519
25520  /*
25521  * - LGPL
25522  *
25523  * Tooltip
25524  * 
25525  */
25526
25527 /**
25528  * @class Roo.bootstrap.Tooltip
25529  * Bootstrap Tooltip class
25530  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25531  * to determine which dom element triggers the tooltip.
25532  * 
25533  * It needs to add support for additional attributes like tooltip-position
25534  * 
25535  * @constructor
25536  * Create a new Toolti
25537  * @param {Object} config The config object
25538  */
25539
25540 Roo.bootstrap.Tooltip = function(config){
25541     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25542     
25543     this.alignment = Roo.bootstrap.Tooltip.alignment;
25544     
25545     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25546         this.alignment = config.alignment;
25547     }
25548     
25549 };
25550
25551 Roo.apply(Roo.bootstrap.Tooltip, {
25552     /**
25553      * @function init initialize tooltip monitoring.
25554      * @static
25555      */
25556     currentEl : false,
25557     currentTip : false,
25558     currentRegion : false,
25559     
25560     //  init : delay?
25561     
25562     init : function()
25563     {
25564         Roo.get(document).on('mouseover', this.enter ,this);
25565         Roo.get(document).on('mouseout', this.leave, this);
25566          
25567         
25568         this.currentTip = new Roo.bootstrap.Tooltip();
25569     },
25570     
25571     enter : function(ev)
25572     {
25573         var dom = ev.getTarget();
25574         
25575         //Roo.log(['enter',dom]);
25576         var el = Roo.fly(dom);
25577         if (this.currentEl) {
25578             //Roo.log(dom);
25579             //Roo.log(this.currentEl);
25580             //Roo.log(this.currentEl.contains(dom));
25581             if (this.currentEl == el) {
25582                 return;
25583             }
25584             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25585                 return;
25586             }
25587
25588         }
25589         
25590         if (this.currentTip.el) {
25591             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25592         }    
25593         //Roo.log(ev);
25594         
25595         if(!el || el.dom == document){
25596             return;
25597         }
25598         
25599         var bindEl = el;
25600         
25601         // you can not look for children, as if el is the body.. then everythign is the child..
25602         if (!el.attr('tooltip')) { //
25603             if (!el.select("[tooltip]").elements.length) {
25604                 return;
25605             }
25606             // is the mouse over this child...?
25607             bindEl = el.select("[tooltip]").first();
25608             var xy = ev.getXY();
25609             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
25610                 //Roo.log("not in region.");
25611                 return;
25612             }
25613             //Roo.log("child element over..");
25614             
25615         }
25616         this.currentEl = bindEl;
25617         this.currentTip.bind(bindEl);
25618         this.currentRegion = Roo.lib.Region.getRegion(dom);
25619         this.currentTip.enter();
25620         
25621     },
25622     leave : function(ev)
25623     {
25624         var dom = ev.getTarget();
25625         //Roo.log(['leave',dom]);
25626         if (!this.currentEl) {
25627             return;
25628         }
25629         
25630         
25631         if (dom != this.currentEl.dom) {
25632             return;
25633         }
25634         var xy = ev.getXY();
25635         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
25636             return;
25637         }
25638         // only activate leave if mouse cursor is outside... bounding box..
25639         
25640         
25641         
25642         
25643         if (this.currentTip) {
25644             this.currentTip.leave();
25645         }
25646         //Roo.log('clear currentEl');
25647         this.currentEl = false;
25648         
25649         
25650     },
25651     alignment : {
25652         'left' : ['r-l', [-2,0], 'right'],
25653         'right' : ['l-r', [2,0], 'left'],
25654         'bottom' : ['t-b', [0,2], 'top'],
25655         'top' : [ 'b-t', [0,-2], 'bottom']
25656     }
25657     
25658 });
25659
25660
25661 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
25662     
25663     
25664     bindEl : false,
25665     
25666     delay : null, // can be { show : 300 , hide: 500}
25667     
25668     timeout : null,
25669     
25670     hoverState : null, //???
25671     
25672     placement : 'bottom', 
25673     
25674     alignment : false,
25675     
25676     getAutoCreate : function(){
25677     
25678         var cfg = {
25679            cls : 'tooltip',
25680            role : 'tooltip',
25681            cn : [
25682                 {
25683                     cls : 'tooltip-arrow'
25684                 },
25685                 {
25686                     cls : 'tooltip-inner'
25687                 }
25688            ]
25689         };
25690         
25691         return cfg;
25692     },
25693     bind : function(el)
25694     {
25695         this.bindEl = el;
25696     },
25697       
25698     
25699     enter : function () {
25700        
25701         if (this.timeout != null) {
25702             clearTimeout(this.timeout);
25703         }
25704         
25705         this.hoverState = 'in';
25706          //Roo.log("enter - show");
25707         if (!this.delay || !this.delay.show) {
25708             this.show();
25709             return;
25710         }
25711         var _t = this;
25712         this.timeout = setTimeout(function () {
25713             if (_t.hoverState == 'in') {
25714                 _t.show();
25715             }
25716         }, this.delay.show);
25717     },
25718     leave : function()
25719     {
25720         clearTimeout(this.timeout);
25721     
25722         this.hoverState = 'out';
25723          if (!this.delay || !this.delay.hide) {
25724             this.hide();
25725             return;
25726         }
25727        
25728         var _t = this;
25729         this.timeout = setTimeout(function () {
25730             //Roo.log("leave - timeout");
25731             
25732             if (_t.hoverState == 'out') {
25733                 _t.hide();
25734                 Roo.bootstrap.Tooltip.currentEl = false;
25735             }
25736         }, delay);
25737     },
25738     
25739     show : function (msg)
25740     {
25741         if (!this.el) {
25742             this.render(document.body);
25743         }
25744         // set content.
25745         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
25746         
25747         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
25748         
25749         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
25750         
25751         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
25752         
25753         var placement = typeof this.placement == 'function' ?
25754             this.placement.call(this, this.el, on_el) :
25755             this.placement;
25756             
25757         var autoToken = /\s?auto?\s?/i;
25758         var autoPlace = autoToken.test(placement);
25759         if (autoPlace) {
25760             placement = placement.replace(autoToken, '') || 'top';
25761         }
25762         
25763         //this.el.detach()
25764         //this.el.setXY([0,0]);
25765         this.el.show();
25766         //this.el.dom.style.display='block';
25767         
25768         //this.el.appendTo(on_el);
25769         
25770         var p = this.getPosition();
25771         var box = this.el.getBox();
25772         
25773         if (autoPlace) {
25774             // fixme..
25775         }
25776         
25777         var align = this.alignment[placement];
25778         
25779         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
25780         
25781         if(placement == 'top' || placement == 'bottom'){
25782             if(xy[0] < 0){
25783                 placement = 'right';
25784             }
25785             
25786             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
25787                 placement = 'left';
25788             }
25789             
25790             var scroll = Roo.select('body', true).first().getScroll();
25791             
25792             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
25793                 placement = 'top';
25794             }
25795             
25796         }
25797         
25798         this.el.alignTo(this.bindEl, align[0],align[1]);
25799         //var arrow = this.el.select('.arrow',true).first();
25800         //arrow.set(align[2], 
25801         
25802         this.el.addClass(placement);
25803         
25804         this.el.addClass('in fade');
25805         
25806         this.hoverState = null;
25807         
25808         if (this.el.hasClass('fade')) {
25809             // fade it?
25810         }
25811         
25812     },
25813     hide : function()
25814     {
25815          
25816         if (!this.el) {
25817             return;
25818         }
25819         //this.el.setXY([0,0]);
25820         this.el.removeClass('in');
25821         //this.el.hide();
25822         
25823     }
25824     
25825 });
25826  
25827
25828  /*
25829  * - LGPL
25830  *
25831  * Location Picker
25832  * 
25833  */
25834
25835 /**
25836  * @class Roo.bootstrap.LocationPicker
25837  * @extends Roo.bootstrap.Component
25838  * Bootstrap LocationPicker class
25839  * @cfg {Number} latitude Position when init default 0
25840  * @cfg {Number} longitude Position when init default 0
25841  * @cfg {Number} zoom default 15
25842  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
25843  * @cfg {Boolean} mapTypeControl default false
25844  * @cfg {Boolean} disableDoubleClickZoom default false
25845  * @cfg {Boolean} scrollwheel default true
25846  * @cfg {Boolean} streetViewControl default false
25847  * @cfg {Number} radius default 0
25848  * @cfg {String} locationName
25849  * @cfg {Boolean} draggable default true
25850  * @cfg {Boolean} enableAutocomplete default false
25851  * @cfg {Boolean} enableReverseGeocode default true
25852  * @cfg {String} markerTitle
25853  * 
25854  * @constructor
25855  * Create a new LocationPicker
25856  * @param {Object} config The config object
25857  */
25858
25859
25860 Roo.bootstrap.LocationPicker = function(config){
25861     
25862     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
25863     
25864     this.addEvents({
25865         /**
25866          * @event initial
25867          * Fires when the picker initialized.
25868          * @param {Roo.bootstrap.LocationPicker} this
25869          * @param {Google Location} location
25870          */
25871         initial : true,
25872         /**
25873          * @event positionchanged
25874          * Fires when the picker position changed.
25875          * @param {Roo.bootstrap.LocationPicker} this
25876          * @param {Google Location} location
25877          */
25878         positionchanged : true,
25879         /**
25880          * @event resize
25881          * Fires when the map resize.
25882          * @param {Roo.bootstrap.LocationPicker} this
25883          */
25884         resize : true,
25885         /**
25886          * @event show
25887          * Fires when the map show.
25888          * @param {Roo.bootstrap.LocationPicker} this
25889          */
25890         show : true,
25891         /**
25892          * @event hide
25893          * Fires when the map hide.
25894          * @param {Roo.bootstrap.LocationPicker} this
25895          */
25896         hide : true,
25897         /**
25898          * @event mapClick
25899          * Fires when click the map.
25900          * @param {Roo.bootstrap.LocationPicker} this
25901          * @param {Map event} e
25902          */
25903         mapClick : true,
25904         /**
25905          * @event mapRightClick
25906          * Fires when right click the map.
25907          * @param {Roo.bootstrap.LocationPicker} this
25908          * @param {Map event} e
25909          */
25910         mapRightClick : true,
25911         /**
25912          * @event markerClick
25913          * Fires when click the marker.
25914          * @param {Roo.bootstrap.LocationPicker} this
25915          * @param {Map event} e
25916          */
25917         markerClick : true,
25918         /**
25919          * @event markerRightClick
25920          * Fires when right click the marker.
25921          * @param {Roo.bootstrap.LocationPicker} this
25922          * @param {Map event} e
25923          */
25924         markerRightClick : true,
25925         /**
25926          * @event OverlayViewDraw
25927          * Fires when OverlayView Draw
25928          * @param {Roo.bootstrap.LocationPicker} this
25929          */
25930         OverlayViewDraw : true,
25931         /**
25932          * @event OverlayViewOnAdd
25933          * Fires when OverlayView Draw
25934          * @param {Roo.bootstrap.LocationPicker} this
25935          */
25936         OverlayViewOnAdd : true,
25937         /**
25938          * @event OverlayViewOnRemove
25939          * Fires when OverlayView Draw
25940          * @param {Roo.bootstrap.LocationPicker} this
25941          */
25942         OverlayViewOnRemove : true,
25943         /**
25944          * @event OverlayViewShow
25945          * Fires when OverlayView Draw
25946          * @param {Roo.bootstrap.LocationPicker} this
25947          * @param {Pixel} cpx
25948          */
25949         OverlayViewShow : true,
25950         /**
25951          * @event OverlayViewHide
25952          * Fires when OverlayView Draw
25953          * @param {Roo.bootstrap.LocationPicker} this
25954          */
25955         OverlayViewHide : true,
25956         /**
25957          * @event loadexception
25958          * Fires when load google lib failed.
25959          * @param {Roo.bootstrap.LocationPicker} this
25960          */
25961         loadexception : true
25962     });
25963         
25964 };
25965
25966 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
25967     
25968     gMapContext: false,
25969     
25970     latitude: 0,
25971     longitude: 0,
25972     zoom: 15,
25973     mapTypeId: false,
25974     mapTypeControl: false,
25975     disableDoubleClickZoom: false,
25976     scrollwheel: true,
25977     streetViewControl: false,
25978     radius: 0,
25979     locationName: '',
25980     draggable: true,
25981     enableAutocomplete: false,
25982     enableReverseGeocode: true,
25983     markerTitle: '',
25984     
25985     getAutoCreate: function()
25986     {
25987
25988         var cfg = {
25989             tag: 'div',
25990             cls: 'roo-location-picker'
25991         };
25992         
25993         return cfg
25994     },
25995     
25996     initEvents: function(ct, position)
25997     {       
25998         if(!this.el.getWidth() || this.isApplied()){
25999             return;
26000         }
26001         
26002         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26003         
26004         this.initial();
26005     },
26006     
26007     initial: function()
26008     {
26009         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26010             this.fireEvent('loadexception', this);
26011             return;
26012         }
26013         
26014         if(!this.mapTypeId){
26015             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26016         }
26017         
26018         this.gMapContext = this.GMapContext();
26019         
26020         this.initOverlayView();
26021         
26022         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26023         
26024         var _this = this;
26025                 
26026         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26027             _this.setPosition(_this.gMapContext.marker.position);
26028         });
26029         
26030         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26031             _this.fireEvent('mapClick', this, event);
26032             
26033         });
26034
26035         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26036             _this.fireEvent('mapRightClick', this, event);
26037             
26038         });
26039         
26040         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26041             _this.fireEvent('markerClick', this, event);
26042             
26043         });
26044
26045         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26046             _this.fireEvent('markerRightClick', this, event);
26047             
26048         });
26049         
26050         this.setPosition(this.gMapContext.location);
26051         
26052         this.fireEvent('initial', this, this.gMapContext.location);
26053     },
26054     
26055     initOverlayView: function()
26056     {
26057         var _this = this;
26058         
26059         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26060             
26061             draw: function()
26062             {
26063                 _this.fireEvent('OverlayViewDraw', _this);
26064             },
26065             
26066             onAdd: function()
26067             {
26068                 _this.fireEvent('OverlayViewOnAdd', _this);
26069             },
26070             
26071             onRemove: function()
26072             {
26073                 _this.fireEvent('OverlayViewOnRemove', _this);
26074             },
26075             
26076             show: function(cpx)
26077             {
26078                 _this.fireEvent('OverlayViewShow', _this, cpx);
26079             },
26080             
26081             hide: function()
26082             {
26083                 _this.fireEvent('OverlayViewHide', _this);
26084             }
26085             
26086         });
26087     },
26088     
26089     fromLatLngToContainerPixel: function(event)
26090     {
26091         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26092     },
26093     
26094     isApplied: function() 
26095     {
26096         return this.getGmapContext() == false ? false : true;
26097     },
26098     
26099     getGmapContext: function() 
26100     {
26101         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26102     },
26103     
26104     GMapContext: function() 
26105     {
26106         var position = new google.maps.LatLng(this.latitude, this.longitude);
26107         
26108         var _map = new google.maps.Map(this.el.dom, {
26109             center: position,
26110             zoom: this.zoom,
26111             mapTypeId: this.mapTypeId,
26112             mapTypeControl: this.mapTypeControl,
26113             disableDoubleClickZoom: this.disableDoubleClickZoom,
26114             scrollwheel: this.scrollwheel,
26115             streetViewControl: this.streetViewControl,
26116             locationName: this.locationName,
26117             draggable: this.draggable,
26118             enableAutocomplete: this.enableAutocomplete,
26119             enableReverseGeocode: this.enableReverseGeocode
26120         });
26121         
26122         var _marker = new google.maps.Marker({
26123             position: position,
26124             map: _map,
26125             title: this.markerTitle,
26126             draggable: this.draggable
26127         });
26128         
26129         return {
26130             map: _map,
26131             marker: _marker,
26132             circle: null,
26133             location: position,
26134             radius: this.radius,
26135             locationName: this.locationName,
26136             addressComponents: {
26137                 formatted_address: null,
26138                 addressLine1: null,
26139                 addressLine2: null,
26140                 streetName: null,
26141                 streetNumber: null,
26142                 city: null,
26143                 district: null,
26144                 state: null,
26145                 stateOrProvince: null
26146             },
26147             settings: this,
26148             domContainer: this.el.dom,
26149             geodecoder: new google.maps.Geocoder()
26150         };
26151     },
26152     
26153     drawCircle: function(center, radius, options) 
26154     {
26155         if (this.gMapContext.circle != null) {
26156             this.gMapContext.circle.setMap(null);
26157         }
26158         if (radius > 0) {
26159             radius *= 1;
26160             options = Roo.apply({}, options, {
26161                 strokeColor: "#0000FF",
26162                 strokeOpacity: .35,
26163                 strokeWeight: 2,
26164                 fillColor: "#0000FF",
26165                 fillOpacity: .2
26166             });
26167             
26168             options.map = this.gMapContext.map;
26169             options.radius = radius;
26170             options.center = center;
26171             this.gMapContext.circle = new google.maps.Circle(options);
26172             return this.gMapContext.circle;
26173         }
26174         
26175         return null;
26176     },
26177     
26178     setPosition: function(location) 
26179     {
26180         this.gMapContext.location = location;
26181         this.gMapContext.marker.setPosition(location);
26182         this.gMapContext.map.panTo(location);
26183         this.drawCircle(location, this.gMapContext.radius, {});
26184         
26185         var _this = this;
26186         
26187         if (this.gMapContext.settings.enableReverseGeocode) {
26188             this.gMapContext.geodecoder.geocode({
26189                 latLng: this.gMapContext.location
26190             }, function(results, status) {
26191                 
26192                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26193                     _this.gMapContext.locationName = results[0].formatted_address;
26194                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26195                     
26196                     _this.fireEvent('positionchanged', this, location);
26197                 }
26198             });
26199             
26200             return;
26201         }
26202         
26203         this.fireEvent('positionchanged', this, location);
26204     },
26205     
26206     resize: function()
26207     {
26208         google.maps.event.trigger(this.gMapContext.map, "resize");
26209         
26210         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26211         
26212         this.fireEvent('resize', this);
26213     },
26214     
26215     setPositionByLatLng: function(latitude, longitude)
26216     {
26217         this.setPosition(new google.maps.LatLng(latitude, longitude));
26218     },
26219     
26220     getCurrentPosition: function() 
26221     {
26222         return {
26223             latitude: this.gMapContext.location.lat(),
26224             longitude: this.gMapContext.location.lng()
26225         };
26226     },
26227     
26228     getAddressName: function() 
26229     {
26230         return this.gMapContext.locationName;
26231     },
26232     
26233     getAddressComponents: function() 
26234     {
26235         return this.gMapContext.addressComponents;
26236     },
26237     
26238     address_component_from_google_geocode: function(address_components) 
26239     {
26240         var result = {};
26241         
26242         for (var i = 0; i < address_components.length; i++) {
26243             var component = address_components[i];
26244             if (component.types.indexOf("postal_code") >= 0) {
26245                 result.postalCode = component.short_name;
26246             } else if (component.types.indexOf("street_number") >= 0) {
26247                 result.streetNumber = component.short_name;
26248             } else if (component.types.indexOf("route") >= 0) {
26249                 result.streetName = component.short_name;
26250             } else if (component.types.indexOf("neighborhood") >= 0) {
26251                 result.city = component.short_name;
26252             } else if (component.types.indexOf("locality") >= 0) {
26253                 result.city = component.short_name;
26254             } else if (component.types.indexOf("sublocality") >= 0) {
26255                 result.district = component.short_name;
26256             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26257                 result.stateOrProvince = component.short_name;
26258             } else if (component.types.indexOf("country") >= 0) {
26259                 result.country = component.short_name;
26260             }
26261         }
26262         
26263         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26264         result.addressLine2 = "";
26265         return result;
26266     },
26267     
26268     setZoomLevel: function(zoom)
26269     {
26270         this.gMapContext.map.setZoom(zoom);
26271     },
26272     
26273     show: function()
26274     {
26275         if(!this.el){
26276             return;
26277         }
26278         
26279         this.el.show();
26280         
26281         this.resize();
26282         
26283         this.fireEvent('show', this);
26284     },
26285     
26286     hide: function()
26287     {
26288         if(!this.el){
26289             return;
26290         }
26291         
26292         this.el.hide();
26293         
26294         this.fireEvent('hide', this);
26295     }
26296     
26297 });
26298
26299 Roo.apply(Roo.bootstrap.LocationPicker, {
26300     
26301     OverlayView : function(map, options)
26302     {
26303         options = options || {};
26304         
26305         this.setMap(map);
26306     }
26307     
26308     
26309 });/*
26310  * - LGPL
26311  *
26312  * Alert
26313  * 
26314  */
26315
26316 /**
26317  * @class Roo.bootstrap.Alert
26318  * @extends Roo.bootstrap.Component
26319  * Bootstrap Alert class
26320  * @cfg {String} title The title of alert
26321  * @cfg {String} html The content of alert
26322  * @cfg {String} weight (  success | info | warning | danger )
26323  * @cfg {String} faicon font-awesomeicon
26324  * 
26325  * @constructor
26326  * Create a new alert
26327  * @param {Object} config The config object
26328  */
26329
26330
26331 Roo.bootstrap.Alert = function(config){
26332     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26333     
26334 };
26335
26336 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26337     
26338     title: '',
26339     html: '',
26340     weight: false,
26341     faicon: false,
26342     
26343     getAutoCreate : function()
26344     {
26345         
26346         var cfg = {
26347             tag : 'div',
26348             cls : 'alert',
26349             cn : [
26350                 {
26351                     tag : 'i',
26352                     cls : 'roo-alert-icon'
26353                     
26354                 },
26355                 {
26356                     tag : 'b',
26357                     cls : 'roo-alert-title',
26358                     html : this.title
26359                 },
26360                 {
26361                     tag : 'span',
26362                     cls : 'roo-alert-text',
26363                     html : this.html
26364                 }
26365             ]
26366         };
26367         
26368         if(this.faicon){
26369             cfg.cn[0].cls += ' fa ' + this.faicon;
26370         }
26371         
26372         if(this.weight){
26373             cfg.cls += ' alert-' + this.weight;
26374         }
26375         
26376         return cfg;
26377     },
26378     
26379     initEvents: function() 
26380     {
26381         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26382     },
26383     
26384     setTitle : function(str)
26385     {
26386         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26387     },
26388     
26389     setText : function(str)
26390     {
26391         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26392     },
26393     
26394     setWeight : function(weight)
26395     {
26396         if(this.weight){
26397             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26398         }
26399         
26400         this.weight = weight;
26401         
26402         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26403     },
26404     
26405     setIcon : function(icon)
26406     {
26407         if(this.faicon){
26408             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26409         }
26410         
26411         this.faicon = icon;
26412         
26413         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26414     },
26415     
26416     hide: function() 
26417     {
26418         this.el.hide();   
26419     },
26420     
26421     show: function() 
26422     {  
26423         this.el.show();   
26424     }
26425     
26426 });
26427
26428  
26429 /*
26430 * Licence: LGPL
26431 */
26432
26433 /**
26434  * @class Roo.bootstrap.UploadCropbox
26435  * @extends Roo.bootstrap.Component
26436  * Bootstrap UploadCropbox class
26437  * @cfg {String} emptyText show when image has been loaded
26438  * @cfg {String} rotateNotify show when image too small to rotate
26439  * @cfg {Number} errorTimeout default 3000
26440  * @cfg {Number} minWidth default 300
26441  * @cfg {Number} minHeight default 300
26442  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26443  * @cfg {Boolean} isDocument (true|false) default false
26444  * @cfg {String} url action url
26445  * @cfg {String} paramName default 'imageUpload'
26446  * @cfg {String} method default POST
26447  * @cfg {Boolean} loadMask (true|false) default true
26448  * @cfg {Boolean} loadingText default 'Loading...'
26449  * 
26450  * @constructor
26451  * Create a new UploadCropbox
26452  * @param {Object} config The config object
26453  */
26454
26455 Roo.bootstrap.UploadCropbox = function(config){
26456     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26457     
26458     this.addEvents({
26459         /**
26460          * @event beforeselectfile
26461          * Fire before select file
26462          * @param {Roo.bootstrap.UploadCropbox} this
26463          */
26464         "beforeselectfile" : true,
26465         /**
26466          * @event initial
26467          * Fire after initEvent
26468          * @param {Roo.bootstrap.UploadCropbox} this
26469          */
26470         "initial" : true,
26471         /**
26472          * @event crop
26473          * Fire after initEvent
26474          * @param {Roo.bootstrap.UploadCropbox} this
26475          * @param {String} data
26476          */
26477         "crop" : true,
26478         /**
26479          * @event prepare
26480          * Fire when preparing the file data
26481          * @param {Roo.bootstrap.UploadCropbox} this
26482          * @param {Object} file
26483          */
26484         "prepare" : true,
26485         /**
26486          * @event exception
26487          * Fire when get exception
26488          * @param {Roo.bootstrap.UploadCropbox} this
26489          * @param {XMLHttpRequest} xhr
26490          */
26491         "exception" : true,
26492         /**
26493          * @event beforeloadcanvas
26494          * Fire before load the canvas
26495          * @param {Roo.bootstrap.UploadCropbox} this
26496          * @param {String} src
26497          */
26498         "beforeloadcanvas" : true,
26499         /**
26500          * @event trash
26501          * Fire when trash image
26502          * @param {Roo.bootstrap.UploadCropbox} this
26503          */
26504         "trash" : true,
26505         /**
26506          * @event download
26507          * Fire when download the image
26508          * @param {Roo.bootstrap.UploadCropbox} this
26509          */
26510         "download" : true,
26511         /**
26512          * @event footerbuttonclick
26513          * Fire when footerbuttonclick
26514          * @param {Roo.bootstrap.UploadCropbox} this
26515          * @param {String} type
26516          */
26517         "footerbuttonclick" : true,
26518         /**
26519          * @event resize
26520          * Fire when resize
26521          * @param {Roo.bootstrap.UploadCropbox} this
26522          */
26523         "resize" : true,
26524         /**
26525          * @event rotate
26526          * Fire when rotate the image
26527          * @param {Roo.bootstrap.UploadCropbox} this
26528          * @param {String} pos
26529          */
26530         "rotate" : true,
26531         /**
26532          * @event inspect
26533          * Fire when inspect the file
26534          * @param {Roo.bootstrap.UploadCropbox} this
26535          * @param {Object} file
26536          */
26537         "inspect" : true,
26538         /**
26539          * @event upload
26540          * Fire when xhr upload the file
26541          * @param {Roo.bootstrap.UploadCropbox} this
26542          * @param {Object} data
26543          */
26544         "upload" : true,
26545         /**
26546          * @event arrange
26547          * Fire when arrange the file data
26548          * @param {Roo.bootstrap.UploadCropbox} this
26549          * @param {Object} formData
26550          */
26551         "arrange" : true
26552     });
26553     
26554     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26555 };
26556
26557 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
26558     
26559     emptyText : 'Click to upload image',
26560     rotateNotify : 'Image is too small to rotate',
26561     errorTimeout : 3000,
26562     scale : 0,
26563     baseScale : 1,
26564     rotate : 0,
26565     dragable : false,
26566     pinching : false,
26567     mouseX : 0,
26568     mouseY : 0,
26569     cropData : false,
26570     minWidth : 300,
26571     minHeight : 300,
26572     file : false,
26573     exif : {},
26574     baseRotate : 1,
26575     cropType : 'image/jpeg',
26576     buttons : false,
26577     canvasLoaded : false,
26578     isDocument : false,
26579     method : 'POST',
26580     paramName : 'imageUpload',
26581     loadMask : true,
26582     loadingText : 'Loading...',
26583     maskEl : false,
26584     
26585     getAutoCreate : function()
26586     {
26587         var cfg = {
26588             tag : 'div',
26589             cls : 'roo-upload-cropbox',
26590             cn : [
26591                 {
26592                     tag : 'input',
26593                     cls : 'roo-upload-cropbox-selector',
26594                     type : 'file'
26595                 },
26596                 {
26597                     tag : 'div',
26598                     cls : 'roo-upload-cropbox-body',
26599                     style : 'cursor:pointer',
26600                     cn : [
26601                         {
26602                             tag : 'div',
26603                             cls : 'roo-upload-cropbox-preview'
26604                         },
26605                         {
26606                             tag : 'div',
26607                             cls : 'roo-upload-cropbox-thumb'
26608                         },
26609                         {
26610                             tag : 'div',
26611                             cls : 'roo-upload-cropbox-empty-notify',
26612                             html : this.emptyText
26613                         },
26614                         {
26615                             tag : 'div',
26616                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
26617                             html : this.rotateNotify
26618                         }
26619                     ]
26620                 },
26621                 {
26622                     tag : 'div',
26623                     cls : 'roo-upload-cropbox-footer',
26624                     cn : {
26625                         tag : 'div',
26626                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
26627                         cn : []
26628                     }
26629                 }
26630             ]
26631         };
26632         
26633         return cfg;
26634     },
26635     
26636     onRender : function(ct, position)
26637     {
26638         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
26639         
26640         if (this.buttons.length) {
26641             
26642             Roo.each(this.buttons, function(bb) {
26643                 
26644                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
26645                 
26646                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
26647                 
26648             }, this);
26649         }
26650         
26651         if(this.loadMask){
26652             this.maskEl = this.el;
26653         }
26654     },
26655     
26656     initEvents : function()
26657     {
26658         this.urlAPI = (window.createObjectURL && window) || 
26659                                 (window.URL && URL.revokeObjectURL && URL) || 
26660                                 (window.webkitURL && webkitURL);
26661                         
26662         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
26663         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26664         
26665         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
26666         this.selectorEl.hide();
26667         
26668         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
26669         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26670         
26671         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
26672         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26673         this.thumbEl.hide();
26674         
26675         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
26676         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26677         
26678         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
26679         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26680         this.errorEl.hide();
26681         
26682         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
26683         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26684         this.footerEl.hide();
26685         
26686         this.setThumbBoxSize();
26687         
26688         this.bind();
26689         
26690         this.resize();
26691         
26692         this.fireEvent('initial', this);
26693     },
26694
26695     bind : function()
26696     {
26697         var _this = this;
26698         
26699         window.addEventListener("resize", function() { _this.resize(); } );
26700         
26701         this.bodyEl.on('click', this.beforeSelectFile, this);
26702         
26703         if(Roo.isTouch){
26704             this.bodyEl.on('touchstart', this.onTouchStart, this);
26705             this.bodyEl.on('touchmove', this.onTouchMove, this);
26706             this.bodyEl.on('touchend', this.onTouchEnd, this);
26707         }
26708         
26709         if(!Roo.isTouch){
26710             this.bodyEl.on('mousedown', this.onMouseDown, this);
26711             this.bodyEl.on('mousemove', this.onMouseMove, this);
26712             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
26713             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
26714             Roo.get(document).on('mouseup', this.onMouseUp, this);
26715         }
26716         
26717         this.selectorEl.on('change', this.onFileSelected, this);
26718     },
26719     
26720     reset : function()
26721     {    
26722         this.scale = 0;
26723         this.baseScale = 1;
26724         this.rotate = 0;
26725         this.baseRotate = 1;
26726         this.dragable = false;
26727         this.pinching = false;
26728         this.mouseX = 0;
26729         this.mouseY = 0;
26730         this.cropData = false;
26731         this.notifyEl.dom.innerHTML = this.emptyText;
26732         
26733         this.selectorEl.dom.value = '';
26734         
26735     },
26736     
26737     resize : function()
26738     {
26739         if(this.fireEvent('resize', this) != false){
26740             this.setThumbBoxPosition();
26741             this.setCanvasPosition();
26742         }
26743     },
26744     
26745     onFooterButtonClick : function(e, el, o, type)
26746     {
26747         switch (type) {
26748             case 'rotate-left' :
26749                 this.onRotateLeft(e);
26750                 break;
26751             case 'rotate-right' :
26752                 this.onRotateRight(e);
26753                 break;
26754             case 'picture' :
26755                 this.beforeSelectFile(e);
26756                 break;
26757             case 'trash' :
26758                 this.trash(e);
26759                 break;
26760             case 'crop' :
26761                 this.crop(e);
26762                 break;
26763             case 'download' :
26764                 this.download(e);
26765                 break;
26766             default :
26767                 break;
26768         }
26769         
26770         this.fireEvent('footerbuttonclick', this, type);
26771     },
26772     
26773     beforeSelectFile : function(e)
26774     {
26775         e.preventDefault();
26776         
26777         if(this.fireEvent('beforeselectfile', this) != false){
26778             this.selectorEl.dom.click();
26779         }
26780     },
26781     
26782     onFileSelected : function(e)
26783     {
26784         e.preventDefault();
26785         
26786         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26787             return;
26788         }
26789         
26790         var file = this.selectorEl.dom.files[0];
26791         
26792         if(this.fireEvent('inspect', this, file) != false){
26793             this.prepare(file);
26794         }
26795         
26796     },
26797     
26798     trash : function(e)
26799     {
26800         this.fireEvent('trash', this);
26801     },
26802     
26803     download : function(e)
26804     {
26805         this.fireEvent('download', this);
26806     },
26807     
26808     loadCanvas : function(src)
26809     {   
26810         if(this.fireEvent('beforeloadcanvas', this, src) != false){
26811             
26812             this.reset();
26813             
26814             this.imageEl = document.createElement('img');
26815             
26816             var _this = this;
26817             
26818             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
26819             
26820             this.imageEl.src = src;
26821         }
26822     },
26823     
26824     onLoadCanvas : function()
26825     {   
26826         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
26827         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
26828         
26829         this.bodyEl.un('click', this.beforeSelectFile, this);
26830         
26831         this.notifyEl.hide();
26832         this.thumbEl.show();
26833         this.footerEl.show();
26834         
26835         this.baseRotateLevel();
26836         
26837         if(this.isDocument){
26838             this.setThumbBoxSize();
26839         }
26840         
26841         this.setThumbBoxPosition();
26842         
26843         this.baseScaleLevel();
26844         
26845         this.draw();
26846         
26847         this.resize();
26848         
26849         this.canvasLoaded = true;
26850         
26851         if(this.loadMask){
26852             this.maskEl.unmask();
26853         }
26854         
26855     },
26856     
26857     setCanvasPosition : function()
26858     {   
26859         if(!this.canvasEl){
26860             return;
26861         }
26862         
26863         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
26864         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
26865         
26866         this.previewEl.setLeft(pw);
26867         this.previewEl.setTop(ph);
26868         
26869     },
26870     
26871     onMouseDown : function(e)
26872     {   
26873         e.stopEvent();
26874         
26875         this.dragable = true;
26876         this.pinching = false;
26877         
26878         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
26879             this.dragable = false;
26880             return;
26881         }
26882         
26883         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26884         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26885         
26886     },
26887     
26888     onMouseMove : function(e)
26889     {   
26890         e.stopEvent();
26891         
26892         if(!this.canvasLoaded){
26893             return;
26894         }
26895         
26896         if (!this.dragable){
26897             return;
26898         }
26899         
26900         var minX = Math.ceil(this.thumbEl.getLeft(true));
26901         var minY = Math.ceil(this.thumbEl.getTop(true));
26902         
26903         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
26904         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
26905         
26906         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26907         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26908         
26909         x = x - this.mouseX;
26910         y = y - this.mouseY;
26911         
26912         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
26913         var bgY = Math.ceil(y + this.previewEl.getTop(true));
26914         
26915         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
26916         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
26917         
26918         this.previewEl.setLeft(bgX);
26919         this.previewEl.setTop(bgY);
26920         
26921         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26922         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26923     },
26924     
26925     onMouseUp : function(e)
26926     {   
26927         e.stopEvent();
26928         
26929         this.dragable = false;
26930     },
26931     
26932     onMouseWheel : function(e)
26933     {   
26934         e.stopEvent();
26935         
26936         this.startScale = this.scale;
26937         
26938         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
26939         
26940         if(!this.zoomable()){
26941             this.scale = this.startScale;
26942             return;
26943         }
26944         
26945         this.draw();
26946         
26947         return;
26948     },
26949     
26950     zoomable : function()
26951     {
26952         var minScale = this.thumbEl.getWidth() / this.minWidth;
26953         
26954         if(this.minWidth < this.minHeight){
26955             minScale = this.thumbEl.getHeight() / this.minHeight;
26956         }
26957         
26958         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
26959         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
26960         
26961         if(
26962                 this.isDocument &&
26963                 (this.rotate == 0 || this.rotate == 180) && 
26964                 (
26965                     width > this.imageEl.OriginWidth || 
26966                     height > this.imageEl.OriginHeight ||
26967                     (width < this.minWidth && height < this.minHeight)
26968                 )
26969         ){
26970             return false;
26971         }
26972         
26973         if(
26974                 this.isDocument &&
26975                 (this.rotate == 90 || this.rotate == 270) && 
26976                 (
26977                     width > this.imageEl.OriginWidth || 
26978                     height > this.imageEl.OriginHeight ||
26979                     (width < this.minHeight && height < this.minWidth)
26980                 )
26981         ){
26982             return false;
26983         }
26984         
26985         if(
26986                 !this.isDocument &&
26987                 (this.rotate == 0 || this.rotate == 180) && 
26988                 (
26989                     width < this.minWidth || 
26990                     width > this.imageEl.OriginWidth || 
26991                     height < this.minHeight || 
26992                     height > this.imageEl.OriginHeight
26993                 )
26994         ){
26995             return false;
26996         }
26997         
26998         if(
26999                 !this.isDocument &&
27000                 (this.rotate == 90 || this.rotate == 270) && 
27001                 (
27002                     width < this.minHeight || 
27003                     width > this.imageEl.OriginWidth || 
27004                     height < this.minWidth || 
27005                     height > this.imageEl.OriginHeight
27006                 )
27007         ){
27008             return false;
27009         }
27010         
27011         return true;
27012         
27013     },
27014     
27015     onRotateLeft : function(e)
27016     {   
27017         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27018             
27019             var minScale = this.thumbEl.getWidth() / this.minWidth;
27020             
27021             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27022             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27023             
27024             this.startScale = this.scale;
27025             
27026             while (this.getScaleLevel() < minScale){
27027             
27028                 this.scale = this.scale + 1;
27029                 
27030                 if(!this.zoomable()){
27031                     break;
27032                 }
27033                 
27034                 if(
27035                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27036                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27037                 ){
27038                     continue;
27039                 }
27040                 
27041                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27042
27043                 this.draw();
27044                 
27045                 return;
27046             }
27047             
27048             this.scale = this.startScale;
27049             
27050             this.onRotateFail();
27051             
27052             return false;
27053         }
27054         
27055         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27056
27057         if(this.isDocument){
27058             this.setThumbBoxSize();
27059             this.setThumbBoxPosition();
27060             this.setCanvasPosition();
27061         }
27062         
27063         this.draw();
27064         
27065         this.fireEvent('rotate', this, 'left');
27066         
27067     },
27068     
27069     onRotateRight : function(e)
27070     {
27071         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27072             
27073             var minScale = this.thumbEl.getWidth() / this.minWidth;
27074         
27075             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27076             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27077             
27078             this.startScale = this.scale;
27079             
27080             while (this.getScaleLevel() < minScale){
27081             
27082                 this.scale = this.scale + 1;
27083                 
27084                 if(!this.zoomable()){
27085                     break;
27086                 }
27087                 
27088                 if(
27089                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27090                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27091                 ){
27092                     continue;
27093                 }
27094                 
27095                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27096
27097                 this.draw();
27098                 
27099                 return;
27100             }
27101             
27102             this.scale = this.startScale;
27103             
27104             this.onRotateFail();
27105             
27106             return false;
27107         }
27108         
27109         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27110
27111         if(this.isDocument){
27112             this.setThumbBoxSize();
27113             this.setThumbBoxPosition();
27114             this.setCanvasPosition();
27115         }
27116         
27117         this.draw();
27118         
27119         this.fireEvent('rotate', this, 'right');
27120     },
27121     
27122     onRotateFail : function()
27123     {
27124         this.errorEl.show(true);
27125         
27126         var _this = this;
27127         
27128         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27129     },
27130     
27131     draw : function()
27132     {
27133         this.previewEl.dom.innerHTML = '';
27134         
27135         var canvasEl = document.createElement("canvas");
27136         
27137         var contextEl = canvasEl.getContext("2d");
27138         
27139         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27140         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27141         var center = this.imageEl.OriginWidth / 2;
27142         
27143         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27144             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27145             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27146             center = this.imageEl.OriginHeight / 2;
27147         }
27148         
27149         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27150         
27151         contextEl.translate(center, center);
27152         contextEl.rotate(this.rotate * Math.PI / 180);
27153
27154         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27155         
27156         this.canvasEl = document.createElement("canvas");
27157         
27158         this.contextEl = this.canvasEl.getContext("2d");
27159         
27160         switch (this.rotate) {
27161             case 0 :
27162                 
27163                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27164                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27165                 
27166                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27167                 
27168                 break;
27169             case 90 : 
27170                 
27171                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27172                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27173                 
27174                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27175                     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);
27176                     break;
27177                 }
27178                 
27179                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27180                 
27181                 break;
27182             case 180 :
27183                 
27184                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27185                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27186                 
27187                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27188                     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);
27189                     break;
27190                 }
27191                 
27192                 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);
27193                 
27194                 break;
27195             case 270 :
27196                 
27197                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27198                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27199         
27200                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27201                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27202                     break;
27203                 }
27204                 
27205                 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);
27206                 
27207                 break;
27208             default : 
27209                 break;
27210         }
27211         
27212         this.previewEl.appendChild(this.canvasEl);
27213         
27214         this.setCanvasPosition();
27215     },
27216     
27217     crop : function()
27218     {
27219         if(!this.canvasLoaded){
27220             return;
27221         }
27222         
27223         var imageCanvas = document.createElement("canvas");
27224         
27225         var imageContext = imageCanvas.getContext("2d");
27226         
27227         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27228         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27229         
27230         var center = imageCanvas.width / 2;
27231         
27232         imageContext.translate(center, center);
27233         
27234         imageContext.rotate(this.rotate * Math.PI / 180);
27235         
27236         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27237         
27238         var canvas = document.createElement("canvas");
27239         
27240         var context = canvas.getContext("2d");
27241                 
27242         canvas.width = this.minWidth;
27243         canvas.height = this.minHeight;
27244
27245         switch (this.rotate) {
27246             case 0 :
27247                 
27248                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27249                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27250                 
27251                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27252                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27253                 
27254                 var targetWidth = this.minWidth - 2 * x;
27255                 var targetHeight = this.minHeight - 2 * y;
27256                 
27257                 var scale = 1;
27258                 
27259                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27260                     scale = targetWidth / width;
27261                 }
27262                 
27263                 if(x > 0 && y == 0){
27264                     scale = targetHeight / height;
27265                 }
27266                 
27267                 if(x > 0 && y > 0){
27268                     scale = targetWidth / width;
27269                     
27270                     if(width < height){
27271                         scale = targetHeight / height;
27272                     }
27273                 }
27274                 
27275                 context.scale(scale, scale);
27276                 
27277                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27278                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27279
27280                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27281                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27282
27283                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27284                 
27285                 break;
27286             case 90 : 
27287                 
27288                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27289                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27290                 
27291                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27292                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27293                 
27294                 var targetWidth = this.minWidth - 2 * x;
27295                 var targetHeight = this.minHeight - 2 * y;
27296                 
27297                 var scale = 1;
27298                 
27299                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27300                     scale = targetWidth / width;
27301                 }
27302                 
27303                 if(x > 0 && y == 0){
27304                     scale = targetHeight / height;
27305                 }
27306                 
27307                 if(x > 0 && y > 0){
27308                     scale = targetWidth / width;
27309                     
27310                     if(width < height){
27311                         scale = targetHeight / height;
27312                     }
27313                 }
27314                 
27315                 context.scale(scale, scale);
27316                 
27317                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27318                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27319
27320                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27321                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27322                 
27323                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27324                 
27325                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27326                 
27327                 break;
27328             case 180 :
27329                 
27330                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27331                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27332                 
27333                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27334                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27335                 
27336                 var targetWidth = this.minWidth - 2 * x;
27337                 var targetHeight = this.minHeight - 2 * y;
27338                 
27339                 var scale = 1;
27340                 
27341                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27342                     scale = targetWidth / width;
27343                 }
27344                 
27345                 if(x > 0 && y == 0){
27346                     scale = targetHeight / height;
27347                 }
27348                 
27349                 if(x > 0 && y > 0){
27350                     scale = targetWidth / width;
27351                     
27352                     if(width < height){
27353                         scale = targetHeight / height;
27354                     }
27355                 }
27356                 
27357                 context.scale(scale, scale);
27358                 
27359                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27360                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27361
27362                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27363                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27364
27365                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27366                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27367                 
27368                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27369                 
27370                 break;
27371             case 270 :
27372                 
27373                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27374                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27375                 
27376                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27377                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27378                 
27379                 var targetWidth = this.minWidth - 2 * x;
27380                 var targetHeight = this.minHeight - 2 * y;
27381                 
27382                 var scale = 1;
27383                 
27384                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27385                     scale = targetWidth / width;
27386                 }
27387                 
27388                 if(x > 0 && y == 0){
27389                     scale = targetHeight / height;
27390                 }
27391                 
27392                 if(x > 0 && y > 0){
27393                     scale = targetWidth / width;
27394                     
27395                     if(width < height){
27396                         scale = targetHeight / height;
27397                     }
27398                 }
27399                 
27400                 context.scale(scale, scale);
27401                 
27402                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27403                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27404
27405                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27406                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27407                 
27408                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27409                 
27410                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27411                 
27412                 break;
27413             default : 
27414                 break;
27415         }
27416         
27417         this.cropData = canvas.toDataURL(this.cropType);
27418         
27419         if(this.fireEvent('crop', this, this.cropData) !== false){
27420             this.process(this.file, this.cropData);
27421         }
27422         
27423         return;
27424         
27425     },
27426     
27427     setThumbBoxSize : function()
27428     {
27429         var width, height;
27430         
27431         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27432             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27433             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27434             
27435             this.minWidth = width;
27436             this.minHeight = height;
27437             
27438             if(this.rotate == 90 || this.rotate == 270){
27439                 this.minWidth = height;
27440                 this.minHeight = width;
27441             }
27442         }
27443         
27444         height = 300;
27445         width = Math.ceil(this.minWidth * height / this.minHeight);
27446         
27447         if(this.minWidth > this.minHeight){
27448             width = 300;
27449             height = Math.ceil(this.minHeight * width / this.minWidth);
27450         }
27451         
27452         this.thumbEl.setStyle({
27453             width : width + 'px',
27454             height : height + 'px'
27455         });
27456
27457         return;
27458             
27459     },
27460     
27461     setThumbBoxPosition : function()
27462     {
27463         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27464         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27465         
27466         this.thumbEl.setLeft(x);
27467         this.thumbEl.setTop(y);
27468         
27469     },
27470     
27471     baseRotateLevel : function()
27472     {
27473         this.baseRotate = 1;
27474         
27475         if(
27476                 typeof(this.exif) != 'undefined' &&
27477                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27478                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27479         ){
27480             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27481         }
27482         
27483         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27484         
27485     },
27486     
27487     baseScaleLevel : function()
27488     {
27489         var width, height;
27490         
27491         if(this.isDocument){
27492             
27493             if(this.baseRotate == 6 || this.baseRotate == 8){
27494             
27495                 height = this.thumbEl.getHeight();
27496                 this.baseScale = height / this.imageEl.OriginWidth;
27497
27498                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27499                     width = this.thumbEl.getWidth();
27500                     this.baseScale = width / this.imageEl.OriginHeight;
27501                 }
27502
27503                 return;
27504             }
27505
27506             height = this.thumbEl.getHeight();
27507             this.baseScale = height / this.imageEl.OriginHeight;
27508
27509             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27510                 width = this.thumbEl.getWidth();
27511                 this.baseScale = width / this.imageEl.OriginWidth;
27512             }
27513
27514             return;
27515         }
27516         
27517         if(this.baseRotate == 6 || this.baseRotate == 8){
27518             
27519             width = this.thumbEl.getHeight();
27520             this.baseScale = width / this.imageEl.OriginHeight;
27521             
27522             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27523                 height = this.thumbEl.getWidth();
27524                 this.baseScale = height / this.imageEl.OriginHeight;
27525             }
27526             
27527             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27528                 height = this.thumbEl.getWidth();
27529                 this.baseScale = height / this.imageEl.OriginHeight;
27530                 
27531                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27532                     width = this.thumbEl.getHeight();
27533                     this.baseScale = width / this.imageEl.OriginWidth;
27534                 }
27535             }
27536             
27537             return;
27538         }
27539         
27540         width = this.thumbEl.getWidth();
27541         this.baseScale = width / this.imageEl.OriginWidth;
27542         
27543         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27544             height = this.thumbEl.getHeight();
27545             this.baseScale = height / this.imageEl.OriginHeight;
27546         }
27547         
27548         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27549             
27550             height = this.thumbEl.getHeight();
27551             this.baseScale = height / this.imageEl.OriginHeight;
27552             
27553             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27554                 width = this.thumbEl.getWidth();
27555                 this.baseScale = width / this.imageEl.OriginWidth;
27556             }
27557             
27558         }
27559         
27560         return;
27561     },
27562     
27563     getScaleLevel : function()
27564     {
27565         return this.baseScale * Math.pow(1.1, this.scale);
27566     },
27567     
27568     onTouchStart : function(e)
27569     {
27570         if(!this.canvasLoaded){
27571             this.beforeSelectFile(e);
27572             return;
27573         }
27574         
27575         var touches = e.browserEvent.touches;
27576         
27577         if(!touches){
27578             return;
27579         }
27580         
27581         if(touches.length == 1){
27582             this.onMouseDown(e);
27583             return;
27584         }
27585         
27586         if(touches.length != 2){
27587             return;
27588         }
27589         
27590         var coords = [];
27591         
27592         for(var i = 0, finger; finger = touches[i]; i++){
27593             coords.push(finger.pageX, finger.pageY);
27594         }
27595         
27596         var x = Math.pow(coords[0] - coords[2], 2);
27597         var y = Math.pow(coords[1] - coords[3], 2);
27598         
27599         this.startDistance = Math.sqrt(x + y);
27600         
27601         this.startScale = this.scale;
27602         
27603         this.pinching = true;
27604         this.dragable = false;
27605         
27606     },
27607     
27608     onTouchMove : function(e)
27609     {
27610         if(!this.pinching && !this.dragable){
27611             return;
27612         }
27613         
27614         var touches = e.browserEvent.touches;
27615         
27616         if(!touches){
27617             return;
27618         }
27619         
27620         if(this.dragable){
27621             this.onMouseMove(e);
27622             return;
27623         }
27624         
27625         var coords = [];
27626         
27627         for(var i = 0, finger; finger = touches[i]; i++){
27628             coords.push(finger.pageX, finger.pageY);
27629         }
27630         
27631         var x = Math.pow(coords[0] - coords[2], 2);
27632         var y = Math.pow(coords[1] - coords[3], 2);
27633         
27634         this.endDistance = Math.sqrt(x + y);
27635         
27636         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
27637         
27638         if(!this.zoomable()){
27639             this.scale = this.startScale;
27640             return;
27641         }
27642         
27643         this.draw();
27644         
27645     },
27646     
27647     onTouchEnd : function(e)
27648     {
27649         this.pinching = false;
27650         this.dragable = false;
27651         
27652     },
27653     
27654     process : function(file, crop)
27655     {
27656         if(this.loadMask){
27657             this.maskEl.mask(this.loadingText);
27658         }
27659         
27660         this.xhr = new XMLHttpRequest();
27661         
27662         file.xhr = this.xhr;
27663
27664         this.xhr.open(this.method, this.url, true);
27665         
27666         var headers = {
27667             "Accept": "application/json",
27668             "Cache-Control": "no-cache",
27669             "X-Requested-With": "XMLHttpRequest"
27670         };
27671         
27672         for (var headerName in headers) {
27673             var headerValue = headers[headerName];
27674             if (headerValue) {
27675                 this.xhr.setRequestHeader(headerName, headerValue);
27676             }
27677         }
27678         
27679         var _this = this;
27680         
27681         this.xhr.onload = function()
27682         {
27683             _this.xhrOnLoad(_this.xhr);
27684         }
27685         
27686         this.xhr.onerror = function()
27687         {
27688             _this.xhrOnError(_this.xhr);
27689         }
27690         
27691         var formData = new FormData();
27692
27693         formData.append('returnHTML', 'NO');
27694         
27695         if(crop){
27696             formData.append('crop', crop);
27697         }
27698         
27699         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
27700             formData.append(this.paramName, file, file.name);
27701         }
27702         
27703         if(typeof(file.filename) != 'undefined'){
27704             formData.append('filename', file.filename);
27705         }
27706         
27707         if(typeof(file.mimetype) != 'undefined'){
27708             formData.append('mimetype', file.mimetype);
27709         }
27710         
27711         if(this.fireEvent('arrange', this, formData) != false){
27712             this.xhr.send(formData);
27713         };
27714     },
27715     
27716     xhrOnLoad : function(xhr)
27717     {
27718         if(this.loadMask){
27719             this.maskEl.unmask();
27720         }
27721         
27722         if (xhr.readyState !== 4) {
27723             this.fireEvent('exception', this, xhr);
27724             return;
27725         }
27726
27727         var response = Roo.decode(xhr.responseText);
27728         
27729         if(!response.success){
27730             this.fireEvent('exception', this, xhr);
27731             return;
27732         }
27733         
27734         var response = Roo.decode(xhr.responseText);
27735         
27736         this.fireEvent('upload', this, response);
27737         
27738     },
27739     
27740     xhrOnError : function()
27741     {
27742         if(this.loadMask){
27743             this.maskEl.unmask();
27744         }
27745         
27746         Roo.log('xhr on error');
27747         
27748         var response = Roo.decode(xhr.responseText);
27749           
27750         Roo.log(response);
27751         
27752     },
27753     
27754     prepare : function(file)
27755     {   
27756         if(this.loadMask){
27757             this.maskEl.mask(this.loadingText);
27758         }
27759         
27760         this.file = false;
27761         this.exif = {};
27762         
27763         if(typeof(file) === 'string'){
27764             this.loadCanvas(file);
27765             return;
27766         }
27767         
27768         if(!file || !this.urlAPI){
27769             return;
27770         }
27771         
27772         this.file = file;
27773         this.cropType = file.type;
27774         
27775         var _this = this;
27776         
27777         if(this.fireEvent('prepare', this, this.file) != false){
27778             
27779             var reader = new FileReader();
27780             
27781             reader.onload = function (e) {
27782                 if (e.target.error) {
27783                     Roo.log(e.target.error);
27784                     return;
27785                 }
27786                 
27787                 var buffer = e.target.result,
27788                     dataView = new DataView(buffer),
27789                     offset = 2,
27790                     maxOffset = dataView.byteLength - 4,
27791                     markerBytes,
27792                     markerLength;
27793                 
27794                 if (dataView.getUint16(0) === 0xffd8) {
27795                     while (offset < maxOffset) {
27796                         markerBytes = dataView.getUint16(offset);
27797                         
27798                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
27799                             markerLength = dataView.getUint16(offset + 2) + 2;
27800                             if (offset + markerLength > dataView.byteLength) {
27801                                 Roo.log('Invalid meta data: Invalid segment size.');
27802                                 break;
27803                             }
27804                             
27805                             if(markerBytes == 0xffe1){
27806                                 _this.parseExifData(
27807                                     dataView,
27808                                     offset,
27809                                     markerLength
27810                                 );
27811                             }
27812                             
27813                             offset += markerLength;
27814                             
27815                             continue;
27816                         }
27817                         
27818                         break;
27819                     }
27820                     
27821                 }
27822                 
27823                 var url = _this.urlAPI.createObjectURL(_this.file);
27824                 
27825                 _this.loadCanvas(url);
27826                 
27827                 return;
27828             }
27829             
27830             reader.readAsArrayBuffer(this.file);
27831             
27832         }
27833         
27834     },
27835     
27836     parseExifData : function(dataView, offset, length)
27837     {
27838         var tiffOffset = offset + 10,
27839             littleEndian,
27840             dirOffset;
27841     
27842         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27843             // No Exif data, might be XMP data instead
27844             return;
27845         }
27846         
27847         // Check for the ASCII code for "Exif" (0x45786966):
27848         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27849             // No Exif data, might be XMP data instead
27850             return;
27851         }
27852         if (tiffOffset + 8 > dataView.byteLength) {
27853             Roo.log('Invalid Exif data: Invalid segment size.');
27854             return;
27855         }
27856         // Check for the two null bytes:
27857         if (dataView.getUint16(offset + 8) !== 0x0000) {
27858             Roo.log('Invalid Exif data: Missing byte alignment offset.');
27859             return;
27860         }
27861         // Check the byte alignment:
27862         switch (dataView.getUint16(tiffOffset)) {
27863         case 0x4949:
27864             littleEndian = true;
27865             break;
27866         case 0x4D4D:
27867             littleEndian = false;
27868             break;
27869         default:
27870             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
27871             return;
27872         }
27873         // Check for the TIFF tag marker (0x002A):
27874         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
27875             Roo.log('Invalid Exif data: Missing TIFF marker.');
27876             return;
27877         }
27878         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
27879         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
27880         
27881         this.parseExifTags(
27882             dataView,
27883             tiffOffset,
27884             tiffOffset + dirOffset,
27885             littleEndian
27886         );
27887     },
27888     
27889     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
27890     {
27891         var tagsNumber,
27892             dirEndOffset,
27893             i;
27894         if (dirOffset + 6 > dataView.byteLength) {
27895             Roo.log('Invalid Exif data: Invalid directory offset.');
27896             return;
27897         }
27898         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
27899         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
27900         if (dirEndOffset + 4 > dataView.byteLength) {
27901             Roo.log('Invalid Exif data: Invalid directory size.');
27902             return;
27903         }
27904         for (i = 0; i < tagsNumber; i += 1) {
27905             this.parseExifTag(
27906                 dataView,
27907                 tiffOffset,
27908                 dirOffset + 2 + 12 * i, // tag offset
27909                 littleEndian
27910             );
27911         }
27912         // Return the offset to the next directory:
27913         return dataView.getUint32(dirEndOffset, littleEndian);
27914     },
27915     
27916     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
27917     {
27918         var tag = dataView.getUint16(offset, littleEndian);
27919         
27920         this.exif[tag] = this.getExifValue(
27921             dataView,
27922             tiffOffset,
27923             offset,
27924             dataView.getUint16(offset + 2, littleEndian), // tag type
27925             dataView.getUint32(offset + 4, littleEndian), // tag length
27926             littleEndian
27927         );
27928     },
27929     
27930     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
27931     {
27932         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
27933             tagSize,
27934             dataOffset,
27935             values,
27936             i,
27937             str,
27938             c;
27939     
27940         if (!tagType) {
27941             Roo.log('Invalid Exif data: Invalid tag type.');
27942             return;
27943         }
27944         
27945         tagSize = tagType.size * length;
27946         // Determine if the value is contained in the dataOffset bytes,
27947         // or if the value at the dataOffset is a pointer to the actual data:
27948         dataOffset = tagSize > 4 ?
27949                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
27950         if (dataOffset + tagSize > dataView.byteLength) {
27951             Roo.log('Invalid Exif data: Invalid data offset.');
27952             return;
27953         }
27954         if (length === 1) {
27955             return tagType.getValue(dataView, dataOffset, littleEndian);
27956         }
27957         values = [];
27958         for (i = 0; i < length; i += 1) {
27959             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
27960         }
27961         
27962         if (tagType.ascii) {
27963             str = '';
27964             // Concatenate the chars:
27965             for (i = 0; i < values.length; i += 1) {
27966                 c = values[i];
27967                 // Ignore the terminating NULL byte(s):
27968                 if (c === '\u0000') {
27969                     break;
27970                 }
27971                 str += c;
27972             }
27973             return str;
27974         }
27975         return values;
27976     }
27977     
27978 });
27979
27980 Roo.apply(Roo.bootstrap.UploadCropbox, {
27981     tags : {
27982         'Orientation': 0x0112
27983     },
27984     
27985     Orientation: {
27986             1: 0, //'top-left',
27987 //            2: 'top-right',
27988             3: 180, //'bottom-right',
27989 //            4: 'bottom-left',
27990 //            5: 'left-top',
27991             6: 90, //'right-top',
27992 //            7: 'right-bottom',
27993             8: 270 //'left-bottom'
27994     },
27995     
27996     exifTagTypes : {
27997         // byte, 8-bit unsigned int:
27998         1: {
27999             getValue: function (dataView, dataOffset) {
28000                 return dataView.getUint8(dataOffset);
28001             },
28002             size: 1
28003         },
28004         // ascii, 8-bit byte:
28005         2: {
28006             getValue: function (dataView, dataOffset) {
28007                 return String.fromCharCode(dataView.getUint8(dataOffset));
28008             },
28009             size: 1,
28010             ascii: true
28011         },
28012         // short, 16 bit int:
28013         3: {
28014             getValue: function (dataView, dataOffset, littleEndian) {
28015                 return dataView.getUint16(dataOffset, littleEndian);
28016             },
28017             size: 2
28018         },
28019         // long, 32 bit int:
28020         4: {
28021             getValue: function (dataView, dataOffset, littleEndian) {
28022                 return dataView.getUint32(dataOffset, littleEndian);
28023             },
28024             size: 4
28025         },
28026         // rational = two long values, first is numerator, second is denominator:
28027         5: {
28028             getValue: function (dataView, dataOffset, littleEndian) {
28029                 return dataView.getUint32(dataOffset, littleEndian) /
28030                     dataView.getUint32(dataOffset + 4, littleEndian);
28031             },
28032             size: 8
28033         },
28034         // slong, 32 bit signed int:
28035         9: {
28036             getValue: function (dataView, dataOffset, littleEndian) {
28037                 return dataView.getInt32(dataOffset, littleEndian);
28038             },
28039             size: 4
28040         },
28041         // srational, two slongs, first is numerator, second is denominator:
28042         10: {
28043             getValue: function (dataView, dataOffset, littleEndian) {
28044                 return dataView.getInt32(dataOffset, littleEndian) /
28045                     dataView.getInt32(dataOffset + 4, littleEndian);
28046             },
28047             size: 8
28048         }
28049     },
28050     
28051     footer : {
28052         STANDARD : [
28053             {
28054                 tag : 'div',
28055                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28056                 action : 'rotate-left',
28057                 cn : [
28058                     {
28059                         tag : 'button',
28060                         cls : 'btn btn-default',
28061                         html : '<i class="fa fa-undo"></i>'
28062                     }
28063                 ]
28064             },
28065             {
28066                 tag : 'div',
28067                 cls : 'btn-group roo-upload-cropbox-picture',
28068                 action : 'picture',
28069                 cn : [
28070                     {
28071                         tag : 'button',
28072                         cls : 'btn btn-default',
28073                         html : '<i class="fa fa-picture-o"></i>'
28074                     }
28075                 ]
28076             },
28077             {
28078                 tag : 'div',
28079                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28080                 action : 'rotate-right',
28081                 cn : [
28082                     {
28083                         tag : 'button',
28084                         cls : 'btn btn-default',
28085                         html : '<i class="fa fa-repeat"></i>'
28086                     }
28087                 ]
28088             }
28089         ],
28090         DOCUMENT : [
28091             {
28092                 tag : 'div',
28093                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28094                 action : 'rotate-left',
28095                 cn : [
28096                     {
28097                         tag : 'button',
28098                         cls : 'btn btn-default',
28099                         html : '<i class="fa fa-undo"></i>'
28100                     }
28101                 ]
28102             },
28103             {
28104                 tag : 'div',
28105                 cls : 'btn-group roo-upload-cropbox-download',
28106                 action : 'download',
28107                 cn : [
28108                     {
28109                         tag : 'button',
28110                         cls : 'btn btn-default',
28111                         html : '<i class="fa fa-download"></i>'
28112                     }
28113                 ]
28114             },
28115             {
28116                 tag : 'div',
28117                 cls : 'btn-group roo-upload-cropbox-crop',
28118                 action : 'crop',
28119                 cn : [
28120                     {
28121                         tag : 'button',
28122                         cls : 'btn btn-default',
28123                         html : '<i class="fa fa-crop"></i>'
28124                     }
28125                 ]
28126             },
28127             {
28128                 tag : 'div',
28129                 cls : 'btn-group roo-upload-cropbox-trash',
28130                 action : 'trash',
28131                 cn : [
28132                     {
28133                         tag : 'button',
28134                         cls : 'btn btn-default',
28135                         html : '<i class="fa fa-trash"></i>'
28136                     }
28137                 ]
28138             },
28139             {
28140                 tag : 'div',
28141                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28142                 action : 'rotate-right',
28143                 cn : [
28144                     {
28145                         tag : 'button',
28146                         cls : 'btn btn-default',
28147                         html : '<i class="fa fa-repeat"></i>'
28148                     }
28149                 ]
28150             }
28151         ],
28152         ROTATOR : [
28153             {
28154                 tag : 'div',
28155                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28156                 action : 'rotate-left',
28157                 cn : [
28158                     {
28159                         tag : 'button',
28160                         cls : 'btn btn-default',
28161                         html : '<i class="fa fa-undo"></i>'
28162                     }
28163                 ]
28164             },
28165             {
28166                 tag : 'div',
28167                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28168                 action : 'rotate-right',
28169                 cn : [
28170                     {
28171                         tag : 'button',
28172                         cls : 'btn btn-default',
28173                         html : '<i class="fa fa-repeat"></i>'
28174                     }
28175                 ]
28176             }
28177         ]
28178     }
28179 });
28180
28181 /*
28182 * Licence: LGPL
28183 */
28184
28185 /**
28186  * @class Roo.bootstrap.DocumentManager
28187  * @extends Roo.bootstrap.Component
28188  * Bootstrap DocumentManager class
28189  * @cfg {String} paramName default 'imageUpload'
28190  * @cfg {String} toolTipName default 'filename'
28191  * @cfg {String} method default POST
28192  * @cfg {String} url action url
28193  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28194  * @cfg {Boolean} multiple multiple upload default true
28195  * @cfg {Number} thumbSize default 300
28196  * @cfg {String} fieldLabel
28197  * @cfg {Number} labelWidth default 4
28198  * @cfg {String} labelAlign (left|top) default left
28199  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28200 * @cfg {Number} labellg set the width of label (1-12)
28201  * @cfg {Number} labelmd set the width of label (1-12)
28202  * @cfg {Number} labelsm set the width of label (1-12)
28203  * @cfg {Number} labelxs set the width of label (1-12)
28204  * 
28205  * @constructor
28206  * Create a new DocumentManager
28207  * @param {Object} config The config object
28208  */
28209
28210 Roo.bootstrap.DocumentManager = function(config){
28211     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28212     
28213     this.files = [];
28214     this.delegates = [];
28215     
28216     this.addEvents({
28217         /**
28218          * @event initial
28219          * Fire when initial the DocumentManager
28220          * @param {Roo.bootstrap.DocumentManager} this
28221          */
28222         "initial" : true,
28223         /**
28224          * @event inspect
28225          * inspect selected file
28226          * @param {Roo.bootstrap.DocumentManager} this
28227          * @param {File} file
28228          */
28229         "inspect" : true,
28230         /**
28231          * @event exception
28232          * Fire when xhr load exception
28233          * @param {Roo.bootstrap.DocumentManager} this
28234          * @param {XMLHttpRequest} xhr
28235          */
28236         "exception" : true,
28237         /**
28238          * @event afterupload
28239          * Fire when xhr load exception
28240          * @param {Roo.bootstrap.DocumentManager} this
28241          * @param {XMLHttpRequest} xhr
28242          */
28243         "afterupload" : true,
28244         /**
28245          * @event prepare
28246          * prepare the form data
28247          * @param {Roo.bootstrap.DocumentManager} this
28248          * @param {Object} formData
28249          */
28250         "prepare" : true,
28251         /**
28252          * @event remove
28253          * Fire when remove the file
28254          * @param {Roo.bootstrap.DocumentManager} this
28255          * @param {Object} file
28256          */
28257         "remove" : true,
28258         /**
28259          * @event refresh
28260          * Fire after refresh the file
28261          * @param {Roo.bootstrap.DocumentManager} this
28262          */
28263         "refresh" : true,
28264         /**
28265          * @event click
28266          * Fire after click the image
28267          * @param {Roo.bootstrap.DocumentManager} this
28268          * @param {Object} file
28269          */
28270         "click" : true,
28271         /**
28272          * @event edit
28273          * Fire when upload a image and editable set to true
28274          * @param {Roo.bootstrap.DocumentManager} this
28275          * @param {Object} file
28276          */
28277         "edit" : true,
28278         /**
28279          * @event beforeselectfile
28280          * Fire before select file
28281          * @param {Roo.bootstrap.DocumentManager} this
28282          */
28283         "beforeselectfile" : true,
28284         /**
28285          * @event process
28286          * Fire before process file
28287          * @param {Roo.bootstrap.DocumentManager} this
28288          * @param {Object} file
28289          */
28290         "process" : true
28291         
28292     });
28293 };
28294
28295 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28296     
28297     boxes : 0,
28298     inputName : '',
28299     thumbSize : 300,
28300     multiple : true,
28301     files : false,
28302     method : 'POST',
28303     url : '',
28304     paramName : 'imageUpload',
28305     toolTipName : 'filename',
28306     fieldLabel : '',
28307     labelWidth : 4,
28308     labelAlign : 'left',
28309     editable : true,
28310     delegates : false,
28311     xhr : false, 
28312     
28313     labellg : 0,
28314     labelmd : 0,
28315     labelsm : 0,
28316     labelxs : 0,
28317     
28318     getAutoCreate : function()
28319     {   
28320         var managerWidget = {
28321             tag : 'div',
28322             cls : 'roo-document-manager',
28323             cn : [
28324                 {
28325                     tag : 'input',
28326                     cls : 'roo-document-manager-selector',
28327                     type : 'file'
28328                 },
28329                 {
28330                     tag : 'div',
28331                     cls : 'roo-document-manager-uploader',
28332                     cn : [
28333                         {
28334                             tag : 'div',
28335                             cls : 'roo-document-manager-upload-btn',
28336                             html : '<i class="fa fa-plus"></i>'
28337                         }
28338                     ]
28339                     
28340                 }
28341             ]
28342         };
28343         
28344         var content = [
28345             {
28346                 tag : 'div',
28347                 cls : 'column col-md-12',
28348                 cn : managerWidget
28349             }
28350         ];
28351         
28352         if(this.fieldLabel.length){
28353             
28354             content = [
28355                 {
28356                     tag : 'div',
28357                     cls : 'column col-md-12',
28358                     html : this.fieldLabel
28359                 },
28360                 {
28361                     tag : 'div',
28362                     cls : 'column col-md-12',
28363                     cn : managerWidget
28364                 }
28365             ];
28366
28367             if(this.labelAlign == 'left'){
28368                 content = [
28369                     {
28370                         tag : 'div',
28371                         cls : 'column',
28372                         html : this.fieldLabel
28373                     },
28374                     {
28375                         tag : 'div',
28376                         cls : 'column',
28377                         cn : managerWidget
28378                     }
28379                 ];
28380                 
28381                 if(this.labelWidth > 12){
28382                     content[0].style = "width: " + this.labelWidth + 'px';
28383                 }
28384
28385                 if(this.labelWidth < 13 && this.labelmd == 0){
28386                     this.labelmd = this.labelWidth;
28387                 }
28388
28389                 if(this.labellg > 0){
28390                     content[0].cls += ' col-lg-' + this.labellg;
28391                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28392                 }
28393
28394                 if(this.labelmd > 0){
28395                     content[0].cls += ' col-md-' + this.labelmd;
28396                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28397                 }
28398
28399                 if(this.labelsm > 0){
28400                     content[0].cls += ' col-sm-' + this.labelsm;
28401                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28402                 }
28403
28404                 if(this.labelxs > 0){
28405                     content[0].cls += ' col-xs-' + this.labelxs;
28406                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28407                 }
28408                 
28409             }
28410         }
28411         
28412         var cfg = {
28413             tag : 'div',
28414             cls : 'row clearfix',
28415             cn : content
28416         };
28417         
28418         return cfg;
28419         
28420     },
28421     
28422     initEvents : function()
28423     {
28424         this.managerEl = this.el.select('.roo-document-manager', true).first();
28425         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28426         
28427         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28428         this.selectorEl.hide();
28429         
28430         if(this.multiple){
28431             this.selectorEl.attr('multiple', 'multiple');
28432         }
28433         
28434         this.selectorEl.on('change', this.onFileSelected, this);
28435         
28436         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28437         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28438         
28439         this.uploader.on('click', this.onUploaderClick, this);
28440         
28441         this.renderProgressDialog();
28442         
28443         var _this = this;
28444         
28445         window.addEventListener("resize", function() { _this.refresh(); } );
28446         
28447         this.fireEvent('initial', this);
28448     },
28449     
28450     renderProgressDialog : function()
28451     {
28452         var _this = this;
28453         
28454         this.progressDialog = new Roo.bootstrap.Modal({
28455             cls : 'roo-document-manager-progress-dialog',
28456             allow_close : false,
28457             title : '',
28458             buttons : [
28459                 {
28460                     name  :'cancel',
28461                     weight : 'danger',
28462                     html : 'Cancel'
28463                 }
28464             ], 
28465             listeners : { 
28466                 btnclick : function() {
28467                     _this.uploadCancel();
28468                     this.hide();
28469                 }
28470             }
28471         });
28472          
28473         this.progressDialog.render(Roo.get(document.body));
28474          
28475         this.progress = new Roo.bootstrap.Progress({
28476             cls : 'roo-document-manager-progress',
28477             active : true,
28478             striped : true
28479         });
28480         
28481         this.progress.render(this.progressDialog.getChildContainer());
28482         
28483         this.progressBar = new Roo.bootstrap.ProgressBar({
28484             cls : 'roo-document-manager-progress-bar',
28485             aria_valuenow : 0,
28486             aria_valuemin : 0,
28487             aria_valuemax : 12,
28488             panel : 'success'
28489         });
28490         
28491         this.progressBar.render(this.progress.getChildContainer());
28492     },
28493     
28494     onUploaderClick : function(e)
28495     {
28496         e.preventDefault();
28497      
28498         if(this.fireEvent('beforeselectfile', this) != false){
28499             this.selectorEl.dom.click();
28500         }
28501         
28502     },
28503     
28504     onFileSelected : function(e)
28505     {
28506         e.preventDefault();
28507         
28508         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28509             return;
28510         }
28511         
28512         Roo.each(this.selectorEl.dom.files, function(file){
28513             if(this.fireEvent('inspect', this, file) != false){
28514                 this.files.push(file);
28515             }
28516         }, this);
28517         
28518         this.queue();
28519         
28520     },
28521     
28522     queue : function()
28523     {
28524         this.selectorEl.dom.value = '';
28525         
28526         if(!this.files.length){
28527             return;
28528         }
28529         
28530         if(this.boxes > 0 && this.files.length > this.boxes){
28531             this.files = this.files.slice(0, this.boxes);
28532         }
28533         
28534         this.uploader.show();
28535         
28536         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28537             this.uploader.hide();
28538         }
28539         
28540         var _this = this;
28541         
28542         var files = [];
28543         
28544         var docs = [];
28545         
28546         Roo.each(this.files, function(file){
28547             
28548             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28549                 var f = this.renderPreview(file);
28550                 files.push(f);
28551                 return;
28552             }
28553             
28554             if(file.type.indexOf('image') != -1){
28555                 this.delegates.push(
28556                     (function(){
28557                         _this.process(file);
28558                     }).createDelegate(this)
28559                 );
28560         
28561                 return;
28562             }
28563             
28564             docs.push(
28565                 (function(){
28566                     _this.process(file);
28567                 }).createDelegate(this)
28568             );
28569             
28570         }, this);
28571         
28572         this.files = files;
28573         
28574         this.delegates = this.delegates.concat(docs);
28575         
28576         if(!this.delegates.length){
28577             this.refresh();
28578             return;
28579         }
28580         
28581         this.progressBar.aria_valuemax = this.delegates.length;
28582         
28583         this.arrange();
28584         
28585         return;
28586     },
28587     
28588     arrange : function()
28589     {
28590         if(!this.delegates.length){
28591             this.progressDialog.hide();
28592             this.refresh();
28593             return;
28594         }
28595         
28596         var delegate = this.delegates.shift();
28597         
28598         this.progressDialog.show();
28599         
28600         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
28601         
28602         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
28603         
28604         delegate();
28605     },
28606     
28607     refresh : function()
28608     {
28609         this.uploader.show();
28610         
28611         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28612             this.uploader.hide();
28613         }
28614         
28615         Roo.isTouch ? this.closable(false) : this.closable(true);
28616         
28617         this.fireEvent('refresh', this);
28618     },
28619     
28620     onRemove : function(e, el, o)
28621     {
28622         e.preventDefault();
28623         
28624         this.fireEvent('remove', this, o);
28625         
28626     },
28627     
28628     remove : function(o)
28629     {
28630         var files = [];
28631         
28632         Roo.each(this.files, function(file){
28633             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
28634                 files.push(file);
28635                 return;
28636             }
28637
28638             o.target.remove();
28639
28640         }, this);
28641         
28642         this.files = files;
28643         
28644         this.refresh();
28645     },
28646     
28647     clear : function()
28648     {
28649         Roo.each(this.files, function(file){
28650             if(!file.target){
28651                 return;
28652             }
28653             
28654             file.target.remove();
28655
28656         }, this);
28657         
28658         this.files = [];
28659         
28660         this.refresh();
28661     },
28662     
28663     onClick : function(e, el, o)
28664     {
28665         e.preventDefault();
28666         
28667         this.fireEvent('click', this, o);
28668         
28669     },
28670     
28671     closable : function(closable)
28672     {
28673         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
28674             
28675             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28676             
28677             if(closable){
28678                 el.show();
28679                 return;
28680             }
28681             
28682             el.hide();
28683             
28684         }, this);
28685     },
28686     
28687     xhrOnLoad : function(xhr)
28688     {
28689         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28690             el.remove();
28691         }, this);
28692         
28693         if (xhr.readyState !== 4) {
28694             this.arrange();
28695             this.fireEvent('exception', this, xhr);
28696             return;
28697         }
28698
28699         var response = Roo.decode(xhr.responseText);
28700         
28701         if(!response.success){
28702             this.arrange();
28703             this.fireEvent('exception', this, xhr);
28704             return;
28705         }
28706         
28707         var file = this.renderPreview(response.data);
28708         
28709         this.files.push(file);
28710         
28711         this.arrange();
28712         
28713         this.fireEvent('afterupload', this, xhr);
28714         
28715     },
28716     
28717     xhrOnError : function(xhr)
28718     {
28719         Roo.log('xhr on error');
28720         
28721         var response = Roo.decode(xhr.responseText);
28722           
28723         Roo.log(response);
28724         
28725         this.arrange();
28726     },
28727     
28728     process : function(file)
28729     {
28730         if(this.fireEvent('process', this, file) !== false){
28731             if(this.editable && file.type.indexOf('image') != -1){
28732                 this.fireEvent('edit', this, file);
28733                 return;
28734             }
28735
28736             this.uploadStart(file, false);
28737
28738             return;
28739         }
28740         
28741     },
28742     
28743     uploadStart : function(file, crop)
28744     {
28745         this.xhr = new XMLHttpRequest();
28746         
28747         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28748             this.arrange();
28749             return;
28750         }
28751         
28752         file.xhr = this.xhr;
28753             
28754         this.managerEl.createChild({
28755             tag : 'div',
28756             cls : 'roo-document-manager-loading',
28757             cn : [
28758                 {
28759                     tag : 'div',
28760                     tooltip : file.name,
28761                     cls : 'roo-document-manager-thumb',
28762                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28763                 }
28764             ]
28765
28766         });
28767
28768         this.xhr.open(this.method, this.url, true);
28769         
28770         var headers = {
28771             "Accept": "application/json",
28772             "Cache-Control": "no-cache",
28773             "X-Requested-With": "XMLHttpRequest"
28774         };
28775         
28776         for (var headerName in headers) {
28777             var headerValue = headers[headerName];
28778             if (headerValue) {
28779                 this.xhr.setRequestHeader(headerName, headerValue);
28780             }
28781         }
28782         
28783         var _this = this;
28784         
28785         this.xhr.onload = function()
28786         {
28787             _this.xhrOnLoad(_this.xhr);
28788         }
28789         
28790         this.xhr.onerror = function()
28791         {
28792             _this.xhrOnError(_this.xhr);
28793         }
28794         
28795         var formData = new FormData();
28796
28797         formData.append('returnHTML', 'NO');
28798         
28799         if(crop){
28800             formData.append('crop', crop);
28801         }
28802         
28803         formData.append(this.paramName, file, file.name);
28804         
28805         var options = {
28806             file : file, 
28807             manually : false
28808         };
28809         
28810         if(this.fireEvent('prepare', this, formData, options) != false){
28811             
28812             if(options.manually){
28813                 return;
28814             }
28815             
28816             this.xhr.send(formData);
28817             return;
28818         };
28819         
28820         this.uploadCancel();
28821     },
28822     
28823     uploadCancel : function()
28824     {
28825         if (this.xhr) {
28826             this.xhr.abort();
28827         }
28828         
28829         this.delegates = [];
28830         
28831         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28832             el.remove();
28833         }, this);
28834         
28835         this.arrange();
28836     },
28837     
28838     renderPreview : function(file)
28839     {
28840         if(typeof(file.target) != 'undefined' && file.target){
28841             return file;
28842         }
28843         
28844         var previewEl = this.managerEl.createChild({
28845             tag : 'div',
28846             cls : 'roo-document-manager-preview',
28847             cn : [
28848                 {
28849                     tag : 'div',
28850                     tooltip : file[this.toolTipName],
28851                     cls : 'roo-document-manager-thumb',
28852                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
28853                 },
28854                 {
28855                     tag : 'button',
28856                     cls : 'close',
28857                     html : '<i class="fa fa-times-circle"></i>'
28858                 }
28859             ]
28860         });
28861
28862         var close = previewEl.select('button.close', true).first();
28863
28864         close.on('click', this.onRemove, this, file);
28865
28866         file.target = previewEl;
28867
28868         var image = previewEl.select('img', true).first();
28869         
28870         var _this = this;
28871         
28872         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
28873         
28874         image.on('click', this.onClick, this, file);
28875         
28876         return file;
28877         
28878     },
28879     
28880     onPreviewLoad : function(file, image)
28881     {
28882         if(typeof(file.target) == 'undefined' || !file.target){
28883             return;
28884         }
28885         
28886         var width = image.dom.naturalWidth || image.dom.width;
28887         var height = image.dom.naturalHeight || image.dom.height;
28888         
28889         if(width > height){
28890             file.target.addClass('wide');
28891             return;
28892         }
28893         
28894         file.target.addClass('tall');
28895         return;
28896         
28897     },
28898     
28899     uploadFromSource : function(file, crop)
28900     {
28901         this.xhr = new XMLHttpRequest();
28902         
28903         this.managerEl.createChild({
28904             tag : 'div',
28905             cls : 'roo-document-manager-loading',
28906             cn : [
28907                 {
28908                     tag : 'div',
28909                     tooltip : file.name,
28910                     cls : 'roo-document-manager-thumb',
28911                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28912                 }
28913             ]
28914
28915         });
28916
28917         this.xhr.open(this.method, this.url, true);
28918         
28919         var headers = {
28920             "Accept": "application/json",
28921             "Cache-Control": "no-cache",
28922             "X-Requested-With": "XMLHttpRequest"
28923         };
28924         
28925         for (var headerName in headers) {
28926             var headerValue = headers[headerName];
28927             if (headerValue) {
28928                 this.xhr.setRequestHeader(headerName, headerValue);
28929             }
28930         }
28931         
28932         var _this = this;
28933         
28934         this.xhr.onload = function()
28935         {
28936             _this.xhrOnLoad(_this.xhr);
28937         }
28938         
28939         this.xhr.onerror = function()
28940         {
28941             _this.xhrOnError(_this.xhr);
28942         }
28943         
28944         var formData = new FormData();
28945
28946         formData.append('returnHTML', 'NO');
28947         
28948         formData.append('crop', crop);
28949         
28950         if(typeof(file.filename) != 'undefined'){
28951             formData.append('filename', file.filename);
28952         }
28953         
28954         if(typeof(file.mimetype) != 'undefined'){
28955             formData.append('mimetype', file.mimetype);
28956         }
28957         
28958         Roo.log(formData);
28959         
28960         if(this.fireEvent('prepare', this, formData) != false){
28961             this.xhr.send(formData);
28962         };
28963     }
28964 });
28965
28966 /*
28967 * Licence: LGPL
28968 */
28969
28970 /**
28971  * @class Roo.bootstrap.DocumentViewer
28972  * @extends Roo.bootstrap.Component
28973  * Bootstrap DocumentViewer class
28974  * @cfg {Boolean} showDownload (true|false) show download button (default true)
28975  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
28976  * 
28977  * @constructor
28978  * Create a new DocumentViewer
28979  * @param {Object} config The config object
28980  */
28981
28982 Roo.bootstrap.DocumentViewer = function(config){
28983     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
28984     
28985     this.addEvents({
28986         /**
28987          * @event initial
28988          * Fire after initEvent
28989          * @param {Roo.bootstrap.DocumentViewer} this
28990          */
28991         "initial" : true,
28992         /**
28993          * @event click
28994          * Fire after click
28995          * @param {Roo.bootstrap.DocumentViewer} this
28996          */
28997         "click" : true,
28998         /**
28999          * @event download
29000          * Fire after download button
29001          * @param {Roo.bootstrap.DocumentViewer} this
29002          */
29003         "download" : true,
29004         /**
29005          * @event trash
29006          * Fire after trash button
29007          * @param {Roo.bootstrap.DocumentViewer} this
29008          */
29009         "trash" : true
29010         
29011     });
29012 };
29013
29014 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29015     
29016     showDownload : true,
29017     
29018     showTrash : true,
29019     
29020     getAutoCreate : function()
29021     {
29022         var cfg = {
29023             tag : 'div',
29024             cls : 'roo-document-viewer',
29025             cn : [
29026                 {
29027                     tag : 'div',
29028                     cls : 'roo-document-viewer-body',
29029                     cn : [
29030                         {
29031                             tag : 'div',
29032                             cls : 'roo-document-viewer-thumb',
29033                             cn : [
29034                                 {
29035                                     tag : 'img',
29036                                     cls : 'roo-document-viewer-image'
29037                                 }
29038                             ]
29039                         }
29040                     ]
29041                 },
29042                 {
29043                     tag : 'div',
29044                     cls : 'roo-document-viewer-footer',
29045                     cn : {
29046                         tag : 'div',
29047                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29048                         cn : [
29049                             {
29050                                 tag : 'div',
29051                                 cls : 'btn-group roo-document-viewer-download',
29052                                 cn : [
29053                                     {
29054                                         tag : 'button',
29055                                         cls : 'btn btn-default',
29056                                         html : '<i class="fa fa-download"></i>'
29057                                     }
29058                                 ]
29059                             },
29060                             {
29061                                 tag : 'div',
29062                                 cls : 'btn-group roo-document-viewer-trash',
29063                                 cn : [
29064                                     {
29065                                         tag : 'button',
29066                                         cls : 'btn btn-default',
29067                                         html : '<i class="fa fa-trash"></i>'
29068                                     }
29069                                 ]
29070                             }
29071                         ]
29072                     }
29073                 }
29074             ]
29075         };
29076         
29077         return cfg;
29078     },
29079     
29080     initEvents : function()
29081     {
29082         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29083         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29084         
29085         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29086         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29087         
29088         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29089         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29090         
29091         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29092         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29093         
29094         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29095         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29096         
29097         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29098         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29099         
29100         this.bodyEl.on('click', this.onClick, this);
29101         this.downloadBtn.on('click', this.onDownload, this);
29102         this.trashBtn.on('click', this.onTrash, this);
29103         
29104         this.downloadBtn.hide();
29105         this.trashBtn.hide();
29106         
29107         if(this.showDownload){
29108             this.downloadBtn.show();
29109         }
29110         
29111         if(this.showTrash){
29112             this.trashBtn.show();
29113         }
29114         
29115         if(!this.showDownload && !this.showTrash) {
29116             this.footerEl.hide();
29117         }
29118         
29119     },
29120     
29121     initial : function()
29122     {
29123         this.fireEvent('initial', this);
29124         
29125     },
29126     
29127     onClick : function(e)
29128     {
29129         e.preventDefault();
29130         
29131         this.fireEvent('click', this);
29132     },
29133     
29134     onDownload : function(e)
29135     {
29136         e.preventDefault();
29137         
29138         this.fireEvent('download', this);
29139     },
29140     
29141     onTrash : function(e)
29142     {
29143         e.preventDefault();
29144         
29145         this.fireEvent('trash', this);
29146     }
29147     
29148 });
29149 /*
29150  * - LGPL
29151  *
29152  * nav progress bar
29153  * 
29154  */
29155
29156 /**
29157  * @class Roo.bootstrap.NavProgressBar
29158  * @extends Roo.bootstrap.Component
29159  * Bootstrap NavProgressBar class
29160  * 
29161  * @constructor
29162  * Create a new nav progress bar
29163  * @param {Object} config The config object
29164  */
29165
29166 Roo.bootstrap.NavProgressBar = function(config){
29167     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29168
29169     this.bullets = this.bullets || [];
29170    
29171 //    Roo.bootstrap.NavProgressBar.register(this);
29172      this.addEvents({
29173         /**
29174              * @event changed
29175              * Fires when the active item changes
29176              * @param {Roo.bootstrap.NavProgressBar} this
29177              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29178              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29179          */
29180         'changed': true
29181      });
29182     
29183 };
29184
29185 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29186     
29187     bullets : [],
29188     barItems : [],
29189     
29190     getAutoCreate : function()
29191     {
29192         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29193         
29194         cfg = {
29195             tag : 'div',
29196             cls : 'roo-navigation-bar-group',
29197             cn : [
29198                 {
29199                     tag : 'div',
29200                     cls : 'roo-navigation-top-bar'
29201                 },
29202                 {
29203                     tag : 'div',
29204                     cls : 'roo-navigation-bullets-bar',
29205                     cn : [
29206                         {
29207                             tag : 'ul',
29208                             cls : 'roo-navigation-bar'
29209                         }
29210                     ]
29211                 },
29212                 
29213                 {
29214                     tag : 'div',
29215                     cls : 'roo-navigation-bottom-bar'
29216                 }
29217             ]
29218             
29219         };
29220         
29221         return cfg;
29222         
29223     },
29224     
29225     initEvents: function() 
29226     {
29227         
29228     },
29229     
29230     onRender : function(ct, position) 
29231     {
29232         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29233         
29234         if(this.bullets.length){
29235             Roo.each(this.bullets, function(b){
29236                this.addItem(b);
29237             }, this);
29238         }
29239         
29240         this.format();
29241         
29242     },
29243     
29244     addItem : function(cfg)
29245     {
29246         var item = new Roo.bootstrap.NavProgressItem(cfg);
29247         
29248         item.parentId = this.id;
29249         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29250         
29251         if(cfg.html){
29252             var top = new Roo.bootstrap.Element({
29253                 tag : 'div',
29254                 cls : 'roo-navigation-bar-text'
29255             });
29256             
29257             var bottom = new Roo.bootstrap.Element({
29258                 tag : 'div',
29259                 cls : 'roo-navigation-bar-text'
29260             });
29261             
29262             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29263             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29264             
29265             var topText = new Roo.bootstrap.Element({
29266                 tag : 'span',
29267                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29268             });
29269             
29270             var bottomText = new Roo.bootstrap.Element({
29271                 tag : 'span',
29272                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29273             });
29274             
29275             topText.onRender(top.el, null);
29276             bottomText.onRender(bottom.el, null);
29277             
29278             item.topEl = top;
29279             item.bottomEl = bottom;
29280         }
29281         
29282         this.barItems.push(item);
29283         
29284         return item;
29285     },
29286     
29287     getActive : function()
29288     {
29289         var active = false;
29290         
29291         Roo.each(this.barItems, function(v){
29292             
29293             if (!v.isActive()) {
29294                 return;
29295             }
29296             
29297             active = v;
29298             return false;
29299             
29300         });
29301         
29302         return active;
29303     },
29304     
29305     setActiveItem : function(item)
29306     {
29307         var prev = false;
29308         
29309         Roo.each(this.barItems, function(v){
29310             if (v.rid == item.rid) {
29311                 return ;
29312             }
29313             
29314             if (v.isActive()) {
29315                 v.setActive(false);
29316                 prev = v;
29317             }
29318         });
29319
29320         item.setActive(true);
29321         
29322         this.fireEvent('changed', this, item, prev);
29323     },
29324     
29325     getBarItem: function(rid)
29326     {
29327         var ret = false;
29328         
29329         Roo.each(this.barItems, function(e) {
29330             if (e.rid != rid) {
29331                 return;
29332             }
29333             
29334             ret =  e;
29335             return false;
29336         });
29337         
29338         return ret;
29339     },
29340     
29341     indexOfItem : function(item)
29342     {
29343         var index = false;
29344         
29345         Roo.each(this.barItems, function(v, i){
29346             
29347             if (v.rid != item.rid) {
29348                 return;
29349             }
29350             
29351             index = i;
29352             return false
29353         });
29354         
29355         return index;
29356     },
29357     
29358     setActiveNext : function()
29359     {
29360         var i = this.indexOfItem(this.getActive());
29361         
29362         if (i > this.barItems.length) {
29363             return;
29364         }
29365         
29366         this.setActiveItem(this.barItems[i+1]);
29367     },
29368     
29369     setActivePrev : function()
29370     {
29371         var i = this.indexOfItem(this.getActive());
29372         
29373         if (i  < 1) {
29374             return;
29375         }
29376         
29377         this.setActiveItem(this.barItems[i-1]);
29378     },
29379     
29380     format : function()
29381     {
29382         if(!this.barItems.length){
29383             return;
29384         }
29385      
29386         var width = 100 / this.barItems.length;
29387         
29388         Roo.each(this.barItems, function(i){
29389             i.el.setStyle('width', width + '%');
29390             i.topEl.el.setStyle('width', width + '%');
29391             i.bottomEl.el.setStyle('width', width + '%');
29392         }, this);
29393         
29394     }
29395     
29396 });
29397 /*
29398  * - LGPL
29399  *
29400  * Nav Progress Item
29401  * 
29402  */
29403
29404 /**
29405  * @class Roo.bootstrap.NavProgressItem
29406  * @extends Roo.bootstrap.Component
29407  * Bootstrap NavProgressItem class
29408  * @cfg {String} rid the reference id
29409  * @cfg {Boolean} active (true|false) Is item active default false
29410  * @cfg {Boolean} disabled (true|false) Is item active default false
29411  * @cfg {String} html
29412  * @cfg {String} position (top|bottom) text position default bottom
29413  * @cfg {String} icon show icon instead of number
29414  * 
29415  * @constructor
29416  * Create a new NavProgressItem
29417  * @param {Object} config The config object
29418  */
29419 Roo.bootstrap.NavProgressItem = function(config){
29420     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29421     this.addEvents({
29422         // raw events
29423         /**
29424          * @event click
29425          * The raw click event for the entire grid.
29426          * @param {Roo.bootstrap.NavProgressItem} this
29427          * @param {Roo.EventObject} e
29428          */
29429         "click" : true
29430     });
29431    
29432 };
29433
29434 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29435     
29436     rid : '',
29437     active : false,
29438     disabled : false,
29439     html : '',
29440     position : 'bottom',
29441     icon : false,
29442     
29443     getAutoCreate : function()
29444     {
29445         var iconCls = 'roo-navigation-bar-item-icon';
29446         
29447         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29448         
29449         var cfg = {
29450             tag: 'li',
29451             cls: 'roo-navigation-bar-item',
29452             cn : [
29453                 {
29454                     tag : 'i',
29455                     cls : iconCls
29456                 }
29457             ]
29458         };
29459         
29460         if(this.active){
29461             cfg.cls += ' active';
29462         }
29463         if(this.disabled){
29464             cfg.cls += ' disabled';
29465         }
29466         
29467         return cfg;
29468     },
29469     
29470     disable : function()
29471     {
29472         this.setDisabled(true);
29473     },
29474     
29475     enable : function()
29476     {
29477         this.setDisabled(false);
29478     },
29479     
29480     initEvents: function() 
29481     {
29482         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29483         
29484         this.iconEl.on('click', this.onClick, this);
29485     },
29486     
29487     onClick : function(e)
29488     {
29489         e.preventDefault();
29490         
29491         if(this.disabled){
29492             return;
29493         }
29494         
29495         if(this.fireEvent('click', this, e) === false){
29496             return;
29497         };
29498         
29499         this.parent().setActiveItem(this);
29500     },
29501     
29502     isActive: function () 
29503     {
29504         return this.active;
29505     },
29506     
29507     setActive : function(state)
29508     {
29509         if(this.active == state){
29510             return;
29511         }
29512         
29513         this.active = state;
29514         
29515         if (state) {
29516             this.el.addClass('active');
29517             return;
29518         }
29519         
29520         this.el.removeClass('active');
29521         
29522         return;
29523     },
29524     
29525     setDisabled : function(state)
29526     {
29527         if(this.disabled == state){
29528             return;
29529         }
29530         
29531         this.disabled = state;
29532         
29533         if (state) {
29534             this.el.addClass('disabled');
29535             return;
29536         }
29537         
29538         this.el.removeClass('disabled');
29539     },
29540     
29541     tooltipEl : function()
29542     {
29543         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29544     }
29545 });
29546  
29547
29548  /*
29549  * - LGPL
29550  *
29551  * FieldLabel
29552  * 
29553  */
29554
29555 /**
29556  * @class Roo.bootstrap.FieldLabel
29557  * @extends Roo.bootstrap.Component
29558  * Bootstrap FieldLabel class
29559  * @cfg {String} html contents of the element
29560  * @cfg {String} tag tag of the element default label
29561  * @cfg {String} cls class of the element
29562  * @cfg {String} target label target 
29563  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29564  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
29565  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
29566  * @cfg {String} iconTooltip default "This field is required"
29567  * 
29568  * @constructor
29569  * Create a new FieldLabel
29570  * @param {Object} config The config object
29571  */
29572
29573 Roo.bootstrap.FieldLabel = function(config){
29574     Roo.bootstrap.Element.superclass.constructor.call(this, config);
29575     
29576     this.addEvents({
29577             /**
29578              * @event invalid
29579              * Fires after the field has been marked as invalid.
29580              * @param {Roo.form.FieldLabel} this
29581              * @param {String} msg The validation message
29582              */
29583             invalid : true,
29584             /**
29585              * @event valid
29586              * Fires after the field has been validated with no errors.
29587              * @param {Roo.form.FieldLabel} this
29588              */
29589             valid : true
29590         });
29591 };
29592
29593 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
29594     
29595     tag: 'label',
29596     cls: '',
29597     html: '',
29598     target: '',
29599     allowBlank : true,
29600     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
29601     validClass : 'text-success fa fa-lg fa-check',
29602     iconTooltip : 'This field is required',
29603     
29604     getAutoCreate : function(){
29605         
29606         var cfg = {
29607             tag : this.tag,
29608             cls : 'roo-bootstrap-field-label ' + this.cls,
29609             for : this.target,
29610             cn : [
29611                 {
29612                     tag : 'i',
29613                     cls : '',
29614                     tooltip : this.iconTooltip
29615                 },
29616                 {
29617                     tag : 'span',
29618                     html : this.html
29619                 }
29620             ] 
29621         };
29622         
29623         return cfg;
29624     },
29625     
29626     initEvents: function() 
29627     {
29628         Roo.bootstrap.Element.superclass.initEvents.call(this);
29629         
29630         this.iconEl = this.el.select('i', true).first();
29631         
29632         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
29633         
29634         Roo.bootstrap.FieldLabel.register(this);
29635     },
29636     
29637     /**
29638      * Mark this field as valid
29639      */
29640     markValid : function()
29641     {
29642         this.iconEl.show();
29643         
29644         this.iconEl.removeClass(this.invalidClass);
29645         
29646         this.iconEl.addClass(this.validClass);
29647         
29648         this.fireEvent('valid', this);
29649     },
29650     
29651     /**
29652      * Mark this field as invalid
29653      * @param {String} msg The validation message
29654      */
29655     markInvalid : function(msg)
29656     {
29657         this.iconEl.show();
29658         
29659         this.iconEl.removeClass(this.validClass);
29660         
29661         this.iconEl.addClass(this.invalidClass);
29662         
29663         this.fireEvent('invalid', this, msg);
29664     }
29665     
29666    
29667 });
29668
29669 Roo.apply(Roo.bootstrap.FieldLabel, {
29670     
29671     groups: {},
29672     
29673      /**
29674     * register a FieldLabel Group
29675     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
29676     */
29677     register : function(label)
29678     {
29679         if(this.groups.hasOwnProperty(label.target)){
29680             return;
29681         }
29682      
29683         this.groups[label.target] = label;
29684         
29685     },
29686     /**
29687     * fetch a FieldLabel Group based on the target
29688     * @param {string} target
29689     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
29690     */
29691     get: function(target) {
29692         if (typeof(this.groups[target]) == 'undefined') {
29693             return false;
29694         }
29695         
29696         return this.groups[target] ;
29697     }
29698 });
29699
29700  
29701
29702  /*
29703  * - LGPL
29704  *
29705  * page DateSplitField.
29706  * 
29707  */
29708
29709
29710 /**
29711  * @class Roo.bootstrap.DateSplitField
29712  * @extends Roo.bootstrap.Component
29713  * Bootstrap DateSplitField class
29714  * @cfg {string} fieldLabel - the label associated
29715  * @cfg {Number} labelWidth set the width of label (0-12)
29716  * @cfg {String} labelAlign (top|left)
29717  * @cfg {Boolean} dayAllowBlank (true|false) default false
29718  * @cfg {Boolean} monthAllowBlank (true|false) default false
29719  * @cfg {Boolean} yearAllowBlank (true|false) default false
29720  * @cfg {string} dayPlaceholder 
29721  * @cfg {string} monthPlaceholder
29722  * @cfg {string} yearPlaceholder
29723  * @cfg {string} dayFormat default 'd'
29724  * @cfg {string} monthFormat default 'm'
29725  * @cfg {string} yearFormat default 'Y'
29726  * @cfg {Number} labellg set the width of label (1-12)
29727  * @cfg {Number} labelmd set the width of label (1-12)
29728  * @cfg {Number} labelsm set the width of label (1-12)
29729  * @cfg {Number} labelxs set the width of label (1-12)
29730
29731  *     
29732  * @constructor
29733  * Create a new DateSplitField
29734  * @param {Object} config The config object
29735  */
29736
29737 Roo.bootstrap.DateSplitField = function(config){
29738     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
29739     
29740     this.addEvents({
29741         // raw events
29742          /**
29743          * @event years
29744          * getting the data of years
29745          * @param {Roo.bootstrap.DateSplitField} this
29746          * @param {Object} years
29747          */
29748         "years" : true,
29749         /**
29750          * @event days
29751          * getting the data of days
29752          * @param {Roo.bootstrap.DateSplitField} this
29753          * @param {Object} days
29754          */
29755         "days" : true,
29756         /**
29757          * @event invalid
29758          * Fires after the field has been marked as invalid.
29759          * @param {Roo.form.Field} this
29760          * @param {String} msg The validation message
29761          */
29762         invalid : true,
29763        /**
29764          * @event valid
29765          * Fires after the field has been validated with no errors.
29766          * @param {Roo.form.Field} this
29767          */
29768         valid : true
29769     });
29770 };
29771
29772 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
29773     
29774     fieldLabel : '',
29775     labelAlign : 'top',
29776     labelWidth : 3,
29777     dayAllowBlank : false,
29778     monthAllowBlank : false,
29779     yearAllowBlank : false,
29780     dayPlaceholder : '',
29781     monthPlaceholder : '',
29782     yearPlaceholder : '',
29783     dayFormat : 'd',
29784     monthFormat : 'm',
29785     yearFormat : 'Y',
29786     isFormField : true,
29787     labellg : 0,
29788     labelmd : 0,
29789     labelsm : 0,
29790     labelxs : 0,
29791     
29792     getAutoCreate : function()
29793     {
29794         var cfg = {
29795             tag : 'div',
29796             cls : 'row roo-date-split-field-group',
29797             cn : [
29798                 {
29799                     tag : 'input',
29800                     type : 'hidden',
29801                     cls : 'form-hidden-field roo-date-split-field-group-value',
29802                     name : this.name
29803                 }
29804             ]
29805         };
29806         
29807         var labelCls = 'col-md-12';
29808         var contentCls = 'col-md-4';
29809         
29810         if(this.fieldLabel){
29811             
29812             var label = {
29813                 tag : 'div',
29814                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
29815                 cn : [
29816                     {
29817                         tag : 'label',
29818                         html : this.fieldLabel
29819                     }
29820                 ]
29821             };
29822             
29823             if(this.labelAlign == 'left'){
29824             
29825                 if(this.labelWidth > 12){
29826                     label.style = "width: " + this.labelWidth + 'px';
29827                 }
29828
29829                 if(this.labelWidth < 13 && this.labelmd == 0){
29830                     this.labelmd = this.labelWidth;
29831                 }
29832
29833                 if(this.labellg > 0){
29834                     labelCls = ' col-lg-' + this.labellg;
29835                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
29836                 }
29837
29838                 if(this.labelmd > 0){
29839                     labelCls = ' col-md-' + this.labelmd;
29840                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
29841                 }
29842
29843                 if(this.labelsm > 0){
29844                     labelCls = ' col-sm-' + this.labelsm;
29845                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
29846                 }
29847
29848                 if(this.labelxs > 0){
29849                     labelCls = ' col-xs-' + this.labelxs;
29850                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
29851                 }
29852             }
29853             
29854             label.cls += ' ' + labelCls;
29855             
29856             cfg.cn.push(label);
29857         }
29858         
29859         Roo.each(['day', 'month', 'year'], function(t){
29860             cfg.cn.push({
29861                 tag : 'div',
29862                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
29863             });
29864         }, this);
29865         
29866         return cfg;
29867     },
29868     
29869     inputEl: function ()
29870     {
29871         return this.el.select('.roo-date-split-field-group-value', true).first();
29872     },
29873     
29874     onRender : function(ct, position) 
29875     {
29876         var _this = this;
29877         
29878         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29879         
29880         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
29881         
29882         this.dayField = new Roo.bootstrap.ComboBox({
29883             allowBlank : this.dayAllowBlank,
29884             alwaysQuery : true,
29885             displayField : 'value',
29886             editable : false,
29887             fieldLabel : '',
29888             forceSelection : true,
29889             mode : 'local',
29890             placeholder : this.dayPlaceholder,
29891             selectOnFocus : true,
29892             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29893             triggerAction : 'all',
29894             typeAhead : true,
29895             valueField : 'value',
29896             store : new Roo.data.SimpleStore({
29897                 data : (function() {    
29898                     var days = [];
29899                     _this.fireEvent('days', _this, days);
29900                     return days;
29901                 })(),
29902                 fields : [ 'value' ]
29903             }),
29904             listeners : {
29905                 select : function (_self, record, index)
29906                 {
29907                     _this.setValue(_this.getValue());
29908                 }
29909             }
29910         });
29911
29912         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
29913         
29914         this.monthField = new Roo.bootstrap.MonthField({
29915             after : '<i class=\"fa fa-calendar\"></i>',
29916             allowBlank : this.monthAllowBlank,
29917             placeholder : this.monthPlaceholder,
29918             readOnly : true,
29919             listeners : {
29920                 render : function (_self)
29921                 {
29922                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
29923                         e.preventDefault();
29924                         _self.focus();
29925                     });
29926                 },
29927                 select : function (_self, oldvalue, newvalue)
29928                 {
29929                     _this.setValue(_this.getValue());
29930                 }
29931             }
29932         });
29933         
29934         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
29935         
29936         this.yearField = new Roo.bootstrap.ComboBox({
29937             allowBlank : this.yearAllowBlank,
29938             alwaysQuery : true,
29939             displayField : 'value',
29940             editable : false,
29941             fieldLabel : '',
29942             forceSelection : true,
29943             mode : 'local',
29944             placeholder : this.yearPlaceholder,
29945             selectOnFocus : true,
29946             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29947             triggerAction : 'all',
29948             typeAhead : true,
29949             valueField : 'value',
29950             store : new Roo.data.SimpleStore({
29951                 data : (function() {
29952                     var years = [];
29953                     _this.fireEvent('years', _this, years);
29954                     return years;
29955                 })(),
29956                 fields : [ 'value' ]
29957             }),
29958             listeners : {
29959                 select : function (_self, record, index)
29960                 {
29961                     _this.setValue(_this.getValue());
29962                 }
29963             }
29964         });
29965
29966         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
29967     },
29968     
29969     setValue : function(v, format)
29970     {
29971         this.inputEl.dom.value = v;
29972         
29973         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
29974         
29975         var d = Date.parseDate(v, f);
29976         
29977         if(!d){
29978             this.validate();
29979             return;
29980         }
29981         
29982         this.setDay(d.format(this.dayFormat));
29983         this.setMonth(d.format(this.monthFormat));
29984         this.setYear(d.format(this.yearFormat));
29985         
29986         this.validate();
29987         
29988         return;
29989     },
29990     
29991     setDay : function(v)
29992     {
29993         this.dayField.setValue(v);
29994         this.inputEl.dom.value = this.getValue();
29995         this.validate();
29996         return;
29997     },
29998     
29999     setMonth : function(v)
30000     {
30001         this.monthField.setValue(v, true);
30002         this.inputEl.dom.value = this.getValue();
30003         this.validate();
30004         return;
30005     },
30006     
30007     setYear : function(v)
30008     {
30009         this.yearField.setValue(v);
30010         this.inputEl.dom.value = this.getValue();
30011         this.validate();
30012         return;
30013     },
30014     
30015     getDay : function()
30016     {
30017         return this.dayField.getValue();
30018     },
30019     
30020     getMonth : function()
30021     {
30022         return this.monthField.getValue();
30023     },
30024     
30025     getYear : function()
30026     {
30027         return this.yearField.getValue();
30028     },
30029     
30030     getValue : function()
30031     {
30032         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30033         
30034         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30035         
30036         return date;
30037     },
30038     
30039     reset : function()
30040     {
30041         this.setDay('');
30042         this.setMonth('');
30043         this.setYear('');
30044         this.inputEl.dom.value = '';
30045         this.validate();
30046         return;
30047     },
30048     
30049     validate : function()
30050     {
30051         var d = this.dayField.validate();
30052         var m = this.monthField.validate();
30053         var y = this.yearField.validate();
30054         
30055         var valid = true;
30056         
30057         if(
30058                 (!this.dayAllowBlank && !d) ||
30059                 (!this.monthAllowBlank && !m) ||
30060                 (!this.yearAllowBlank && !y)
30061         ){
30062             valid = false;
30063         }
30064         
30065         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30066             return valid;
30067         }
30068         
30069         if(valid){
30070             this.markValid();
30071             return valid;
30072         }
30073         
30074         this.markInvalid();
30075         
30076         return valid;
30077     },
30078     
30079     markValid : function()
30080     {
30081         
30082         var label = this.el.select('label', true).first();
30083         var icon = this.el.select('i.fa-star', true).first();
30084
30085         if(label && icon){
30086             icon.remove();
30087         }
30088         
30089         this.fireEvent('valid', this);
30090     },
30091     
30092      /**
30093      * Mark this field as invalid
30094      * @param {String} msg The validation message
30095      */
30096     markInvalid : function(msg)
30097     {
30098         
30099         var label = this.el.select('label', true).first();
30100         var icon = this.el.select('i.fa-star', true).first();
30101
30102         if(label && !icon){
30103             this.el.select('.roo-date-split-field-label', true).createChild({
30104                 tag : 'i',
30105                 cls : 'text-danger fa fa-lg fa-star',
30106                 tooltip : 'This field is required',
30107                 style : 'margin-right:5px;'
30108             }, label, true);
30109         }
30110         
30111         this.fireEvent('invalid', this, msg);
30112     },
30113     
30114     clearInvalid : function()
30115     {
30116         var label = this.el.select('label', true).first();
30117         var icon = this.el.select('i.fa-star', true).first();
30118
30119         if(label && icon){
30120             icon.remove();
30121         }
30122         
30123         this.fireEvent('valid', this);
30124     },
30125     
30126     getName: function()
30127     {
30128         return this.name;
30129     }
30130     
30131 });
30132
30133  /**
30134  *
30135  * This is based on 
30136  * http://masonry.desandro.com
30137  *
30138  * The idea is to render all the bricks based on vertical width...
30139  *
30140  * The original code extends 'outlayer' - we might need to use that....
30141  * 
30142  */
30143
30144
30145 /**
30146  * @class Roo.bootstrap.LayoutMasonry
30147  * @extends Roo.bootstrap.Component
30148  * Bootstrap Layout Masonry class
30149  * 
30150  * @constructor
30151  * Create a new Element
30152  * @param {Object} config The config object
30153  */
30154
30155 Roo.bootstrap.LayoutMasonry = function(config){
30156     
30157     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30158     
30159     this.bricks = [];
30160     
30161     Roo.bootstrap.LayoutMasonry.register(this);
30162     
30163     this.addEvents({
30164         // raw events
30165         /**
30166          * @event layout
30167          * Fire after layout the items
30168          * @param {Roo.bootstrap.LayoutMasonry} this
30169          * @param {Roo.EventObject} e
30170          */
30171         "layout" : true
30172     });
30173     
30174 };
30175
30176 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30177     
30178     /**
30179      * @cfg {Boolean} isLayoutInstant = no animation?
30180      */   
30181     isLayoutInstant : false, // needed?
30182    
30183     /**
30184      * @cfg {Number} boxWidth  width of the columns
30185      */   
30186     boxWidth : 450,
30187     
30188       /**
30189      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30190      */   
30191     boxHeight : 0,
30192     
30193     /**
30194      * @cfg {Number} padWidth padding below box..
30195      */   
30196     padWidth : 10, 
30197     
30198     /**
30199      * @cfg {Number} gutter gutter width..
30200      */   
30201     gutter : 10,
30202     
30203      /**
30204      * @cfg {Number} maxCols maximum number of columns
30205      */   
30206     
30207     maxCols: 0,
30208     
30209     /**
30210      * @cfg {Boolean} isAutoInitial defalut true
30211      */   
30212     isAutoInitial : true, 
30213     
30214     containerWidth: 0,
30215     
30216     /**
30217      * @cfg {Boolean} isHorizontal defalut false
30218      */   
30219     isHorizontal : false, 
30220
30221     currentSize : null,
30222     
30223     tag: 'div',
30224     
30225     cls: '',
30226     
30227     bricks: null, //CompositeElement
30228     
30229     cols : 1,
30230     
30231     _isLayoutInited : false,
30232     
30233 //    isAlternative : false, // only use for vertical layout...
30234     
30235     /**
30236      * @cfg {Number} alternativePadWidth padding below box..
30237      */   
30238     alternativePadWidth : 50,
30239     
30240     selectedBrick : [],
30241     
30242     getAutoCreate : function(){
30243         
30244         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30245         
30246         var cfg = {
30247             tag: this.tag,
30248             cls: 'blog-masonary-wrapper ' + this.cls,
30249             cn : {
30250                 cls : 'mas-boxes masonary'
30251             }
30252         };
30253         
30254         return cfg;
30255     },
30256     
30257     getChildContainer: function( )
30258     {
30259         if (this.boxesEl) {
30260             return this.boxesEl;
30261         }
30262         
30263         this.boxesEl = this.el.select('.mas-boxes').first();
30264         
30265         return this.boxesEl;
30266     },
30267     
30268     
30269     initEvents : function()
30270     {
30271         var _this = this;
30272         
30273         if(this.isAutoInitial){
30274             Roo.log('hook children rendered');
30275             this.on('childrenrendered', function() {
30276                 Roo.log('children rendered');
30277                 _this.initial();
30278             } ,this);
30279         }
30280     },
30281     
30282     initial : function()
30283     {
30284         this.selectedBrick = [];
30285         
30286         this.currentSize = this.el.getBox(true);
30287         
30288         Roo.EventManager.onWindowResize(this.resize, this); 
30289
30290         if(!this.isAutoInitial){
30291             this.layout();
30292             return;
30293         }
30294         
30295         this.layout();
30296         
30297         return;
30298         //this.layout.defer(500,this);
30299         
30300     },
30301     
30302     resize : function()
30303     {
30304         var cs = this.el.getBox(true);
30305         
30306         if (
30307                 this.currentSize.width == cs.width && 
30308                 this.currentSize.x == cs.x && 
30309                 this.currentSize.height == cs.height && 
30310                 this.currentSize.y == cs.y 
30311         ) {
30312             Roo.log("no change in with or X or Y");
30313             return;
30314         }
30315         
30316         this.currentSize = cs;
30317         
30318         this.layout();
30319         
30320     },
30321     
30322     layout : function()
30323     {   
30324         this._resetLayout();
30325         
30326         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30327         
30328         this.layoutItems( isInstant );
30329       
30330         this._isLayoutInited = true;
30331         
30332         this.fireEvent('layout', this);
30333         
30334     },
30335     
30336     _resetLayout : function()
30337     {
30338         if(this.isHorizontal){
30339             this.horizontalMeasureColumns();
30340             return;
30341         }
30342         
30343         this.verticalMeasureColumns();
30344         
30345     },
30346     
30347     verticalMeasureColumns : function()
30348     {
30349         this.getContainerWidth();
30350         
30351 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30352 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30353 //            return;
30354 //        }
30355         
30356         var boxWidth = this.boxWidth + this.padWidth;
30357         
30358         if(this.containerWidth < this.boxWidth){
30359             boxWidth = this.containerWidth
30360         }
30361         
30362         var containerWidth = this.containerWidth;
30363         
30364         var cols = Math.floor(containerWidth / boxWidth);
30365         
30366         this.cols = Math.max( cols, 1 );
30367         
30368         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30369         
30370         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30371         
30372         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30373         
30374         this.colWidth = boxWidth + avail - this.padWidth;
30375         
30376         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30377         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30378     },
30379     
30380     horizontalMeasureColumns : function()
30381     {
30382         this.getContainerWidth();
30383         
30384         var boxWidth = this.boxWidth;
30385         
30386         if(this.containerWidth < boxWidth){
30387             boxWidth = this.containerWidth;
30388         }
30389         
30390         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30391         
30392         this.el.setHeight(boxWidth);
30393         
30394     },
30395     
30396     getContainerWidth : function()
30397     {
30398         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30399     },
30400     
30401     layoutItems : function( isInstant )
30402     {
30403         Roo.log(this.bricks);
30404         
30405         var items = Roo.apply([], this.bricks);
30406         
30407         if(this.isHorizontal){
30408             this._horizontalLayoutItems( items , isInstant );
30409             return;
30410         }
30411         
30412 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30413 //            this._verticalAlternativeLayoutItems( items , isInstant );
30414 //            return;
30415 //        }
30416         
30417         this._verticalLayoutItems( items , isInstant );
30418         
30419     },
30420     
30421     _verticalLayoutItems : function ( items , isInstant)
30422     {
30423         if ( !items || !items.length ) {
30424             return;
30425         }
30426         
30427         var standard = [
30428             ['xs', 'xs', 'xs', 'tall'],
30429             ['xs', 'xs', 'tall'],
30430             ['xs', 'xs', 'sm'],
30431             ['xs', 'xs', 'xs'],
30432             ['xs', 'tall'],
30433             ['xs', 'sm'],
30434             ['xs', 'xs'],
30435             ['xs'],
30436             
30437             ['sm', 'xs', 'xs'],
30438             ['sm', 'xs'],
30439             ['sm'],
30440             
30441             ['tall', 'xs', 'xs', 'xs'],
30442             ['tall', 'xs', 'xs'],
30443             ['tall', 'xs'],
30444             ['tall']
30445             
30446         ];
30447         
30448         var queue = [];
30449         
30450         var boxes = [];
30451         
30452         var box = [];
30453         
30454         Roo.each(items, function(item, k){
30455             
30456             switch (item.size) {
30457                 // these layouts take up a full box,
30458                 case 'md' :
30459                 case 'md-left' :
30460                 case 'md-right' :
30461                 case 'wide' :
30462                     
30463                     if(box.length){
30464                         boxes.push(box);
30465                         box = [];
30466                     }
30467                     
30468                     boxes.push([item]);
30469                     
30470                     break;
30471                     
30472                 case 'xs' :
30473                 case 'sm' :
30474                 case 'tall' :
30475                     
30476                     box.push(item);
30477                     
30478                     break;
30479                 default :
30480                     break;
30481                     
30482             }
30483             
30484         }, this);
30485         
30486         if(box.length){
30487             boxes.push(box);
30488             box = [];
30489         }
30490         
30491         var filterPattern = function(box, length)
30492         {
30493             if(!box.length){
30494                 return;
30495             }
30496             
30497             var match = false;
30498             
30499             var pattern = box.slice(0, length);
30500             
30501             var format = [];
30502             
30503             Roo.each(pattern, function(i){
30504                 format.push(i.size);
30505             }, this);
30506             
30507             Roo.each(standard, function(s){
30508                 
30509                 if(String(s) != String(format)){
30510                     return;
30511                 }
30512                 
30513                 match = true;
30514                 return false;
30515                 
30516             }, this);
30517             
30518             if(!match && length == 1){
30519                 return;
30520             }
30521             
30522             if(!match){
30523                 filterPattern(box, length - 1);
30524                 return;
30525             }
30526                 
30527             queue.push(pattern);
30528
30529             box = box.slice(length, box.length);
30530
30531             filterPattern(box, 4);
30532
30533             return;
30534             
30535         }
30536         
30537         Roo.each(boxes, function(box, k){
30538             
30539             if(!box.length){
30540                 return;
30541             }
30542             
30543             if(box.length == 1){
30544                 queue.push(box);
30545                 return;
30546             }
30547             
30548             filterPattern(box, 4);
30549             
30550         }, this);
30551         
30552         this._processVerticalLayoutQueue( queue, isInstant );
30553         
30554     },
30555     
30556 //    _verticalAlternativeLayoutItems : function( items , isInstant )
30557 //    {
30558 //        if ( !items || !items.length ) {
30559 //            return;
30560 //        }
30561 //
30562 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
30563 //        
30564 //    },
30565     
30566     _horizontalLayoutItems : function ( items , isInstant)
30567     {
30568         if ( !items || !items.length || items.length < 3) {
30569             return;
30570         }
30571         
30572         items.reverse();
30573         
30574         var eItems = items.slice(0, 3);
30575         
30576         items = items.slice(3, items.length);
30577         
30578         var standard = [
30579             ['xs', 'xs', 'xs', 'wide'],
30580             ['xs', 'xs', 'wide'],
30581             ['xs', 'xs', 'sm'],
30582             ['xs', 'xs', 'xs'],
30583             ['xs', 'wide'],
30584             ['xs', 'sm'],
30585             ['xs', 'xs'],
30586             ['xs'],
30587             
30588             ['sm', 'xs', 'xs'],
30589             ['sm', 'xs'],
30590             ['sm'],
30591             
30592             ['wide', 'xs', 'xs', 'xs'],
30593             ['wide', 'xs', 'xs'],
30594             ['wide', 'xs'],
30595             ['wide'],
30596             
30597             ['wide-thin']
30598         ];
30599         
30600         var queue = [];
30601         
30602         var boxes = [];
30603         
30604         var box = [];
30605         
30606         Roo.each(items, function(item, k){
30607             
30608             switch (item.size) {
30609                 case 'md' :
30610                 case 'md-left' :
30611                 case 'md-right' :
30612                 case 'tall' :
30613                     
30614                     if(box.length){
30615                         boxes.push(box);
30616                         box = [];
30617                     }
30618                     
30619                     boxes.push([item]);
30620                     
30621                     break;
30622                     
30623                 case 'xs' :
30624                 case 'sm' :
30625                 case 'wide' :
30626                 case 'wide-thin' :
30627                     
30628                     box.push(item);
30629                     
30630                     break;
30631                 default :
30632                     break;
30633                     
30634             }
30635             
30636         }, this);
30637         
30638         if(box.length){
30639             boxes.push(box);
30640             box = [];
30641         }
30642         
30643         var filterPattern = function(box, length)
30644         {
30645             if(!box.length){
30646                 return;
30647             }
30648             
30649             var match = false;
30650             
30651             var pattern = box.slice(0, length);
30652             
30653             var format = [];
30654             
30655             Roo.each(pattern, function(i){
30656                 format.push(i.size);
30657             }, this);
30658             
30659             Roo.each(standard, function(s){
30660                 
30661                 if(String(s) != String(format)){
30662                     return;
30663                 }
30664                 
30665                 match = true;
30666                 return false;
30667                 
30668             }, this);
30669             
30670             if(!match && length == 1){
30671                 return;
30672             }
30673             
30674             if(!match){
30675                 filterPattern(box, length - 1);
30676                 return;
30677             }
30678                 
30679             queue.push(pattern);
30680
30681             box = box.slice(length, box.length);
30682
30683             filterPattern(box, 4);
30684
30685             return;
30686             
30687         }
30688         
30689         Roo.each(boxes, function(box, k){
30690             
30691             if(!box.length){
30692                 return;
30693             }
30694             
30695             if(box.length == 1){
30696                 queue.push(box);
30697                 return;
30698             }
30699             
30700             filterPattern(box, 4);
30701             
30702         }, this);
30703         
30704         
30705         var prune = [];
30706         
30707         var pos = this.el.getBox(true);
30708         
30709         var minX = pos.x;
30710         
30711         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30712         
30713         var hit_end = false;
30714         
30715         Roo.each(queue, function(box){
30716             
30717             if(hit_end){
30718                 
30719                 Roo.each(box, function(b){
30720                 
30721                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30722                     b.el.hide();
30723
30724                 }, this);
30725
30726                 return;
30727             }
30728             
30729             var mx = 0;
30730             
30731             Roo.each(box, function(b){
30732                 
30733                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
30734                 b.el.show();
30735
30736                 mx = Math.max(mx, b.x);
30737                 
30738             }, this);
30739             
30740             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
30741             
30742             if(maxX < minX){
30743                 
30744                 Roo.each(box, function(b){
30745                 
30746                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30747                     b.el.hide();
30748                     
30749                 }, this);
30750                 
30751                 hit_end = true;
30752                 
30753                 return;
30754             }
30755             
30756             prune.push(box);
30757             
30758         }, this);
30759         
30760         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
30761     },
30762     
30763     /** Sets position of item in DOM
30764     * @param {Element} item
30765     * @param {Number} x - horizontal position
30766     * @param {Number} y - vertical position
30767     * @param {Boolean} isInstant - disables transitions
30768     */
30769     _processVerticalLayoutQueue : function( queue, isInstant )
30770     {
30771         var pos = this.el.getBox(true);
30772         var x = pos.x;
30773         var y = pos.y;
30774         var maxY = [];
30775         
30776         for (var i = 0; i < this.cols; i++){
30777             maxY[i] = pos.y;
30778         }
30779         
30780         Roo.each(queue, function(box, k){
30781             
30782             var col = k % this.cols;
30783             
30784             Roo.each(box, function(b,kk){
30785                 
30786                 b.el.position('absolute');
30787                 
30788                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30789                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30790                 
30791                 if(b.size == 'md-left' || b.size == 'md-right'){
30792                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30793                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30794                 }
30795                 
30796                 b.el.setWidth(width);
30797                 b.el.setHeight(height);
30798                 // iframe?
30799                 b.el.select('iframe',true).setSize(width,height);
30800                 
30801             }, this);
30802             
30803             for (var i = 0; i < this.cols; i++){
30804                 
30805                 if(maxY[i] < maxY[col]){
30806                     col = i;
30807                     continue;
30808                 }
30809                 
30810                 col = Math.min(col, i);
30811                 
30812             }
30813             
30814             x = pos.x + col * (this.colWidth + this.padWidth);
30815             
30816             y = maxY[col];
30817             
30818             var positions = [];
30819             
30820             switch (box.length){
30821                 case 1 :
30822                     positions = this.getVerticalOneBoxColPositions(x, y, box);
30823                     break;
30824                 case 2 :
30825                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
30826                     break;
30827                 case 3 :
30828                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
30829                     break;
30830                 case 4 :
30831                     positions = this.getVerticalFourBoxColPositions(x, y, box);
30832                     break;
30833                 default :
30834                     break;
30835             }
30836             
30837             Roo.each(box, function(b,kk){
30838                 
30839                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30840                 
30841                 var sz = b.el.getSize();
30842                 
30843                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
30844                 
30845             }, this);
30846             
30847         }, this);
30848         
30849         var mY = 0;
30850         
30851         for (var i = 0; i < this.cols; i++){
30852             mY = Math.max(mY, maxY[i]);
30853         }
30854         
30855         this.el.setHeight(mY - pos.y);
30856         
30857     },
30858     
30859 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
30860 //    {
30861 //        var pos = this.el.getBox(true);
30862 //        var x = pos.x;
30863 //        var y = pos.y;
30864 //        var maxX = pos.right;
30865 //        
30866 //        var maxHeight = 0;
30867 //        
30868 //        Roo.each(items, function(item, k){
30869 //            
30870 //            var c = k % 2;
30871 //            
30872 //            item.el.position('absolute');
30873 //                
30874 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
30875 //
30876 //            item.el.setWidth(width);
30877 //
30878 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
30879 //
30880 //            item.el.setHeight(height);
30881 //            
30882 //            if(c == 0){
30883 //                item.el.setXY([x, y], isInstant ? false : true);
30884 //            } else {
30885 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
30886 //            }
30887 //            
30888 //            y = y + height + this.alternativePadWidth;
30889 //            
30890 //            maxHeight = maxHeight + height + this.alternativePadWidth;
30891 //            
30892 //        }, this);
30893 //        
30894 //        this.el.setHeight(maxHeight);
30895 //        
30896 //    },
30897     
30898     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
30899     {
30900         var pos = this.el.getBox(true);
30901         
30902         var minX = pos.x;
30903         var minY = pos.y;
30904         
30905         var maxX = pos.right;
30906         
30907         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
30908         
30909         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30910         
30911         Roo.each(queue, function(box, k){
30912             
30913             Roo.each(box, function(b, kk){
30914                 
30915                 b.el.position('absolute');
30916                 
30917                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30918                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30919                 
30920                 if(b.size == 'md-left' || b.size == 'md-right'){
30921                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30922                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30923                 }
30924                 
30925                 b.el.setWidth(width);
30926                 b.el.setHeight(height);
30927                 
30928             }, this);
30929             
30930             if(!box.length){
30931                 return;
30932             }
30933             
30934             var positions = [];
30935             
30936             switch (box.length){
30937                 case 1 :
30938                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
30939                     break;
30940                 case 2 :
30941                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
30942                     break;
30943                 case 3 :
30944                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
30945                     break;
30946                 case 4 :
30947                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
30948                     break;
30949                 default :
30950                     break;
30951             }
30952             
30953             Roo.each(box, function(b,kk){
30954                 
30955                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30956                 
30957                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
30958                 
30959             }, this);
30960             
30961         }, this);
30962         
30963     },
30964     
30965     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
30966     {
30967         Roo.each(eItems, function(b,k){
30968             
30969             b.size = (k == 0) ? 'sm' : 'xs';
30970             b.x = (k == 0) ? 2 : 1;
30971             b.y = (k == 0) ? 2 : 1;
30972             
30973             b.el.position('absolute');
30974             
30975             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30976                 
30977             b.el.setWidth(width);
30978             
30979             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30980             
30981             b.el.setHeight(height);
30982             
30983         }, this);
30984
30985         var positions = [];
30986         
30987         positions.push({
30988             x : maxX - this.unitWidth * 2 - this.gutter,
30989             y : minY
30990         });
30991         
30992         positions.push({
30993             x : maxX - this.unitWidth,
30994             y : minY + (this.unitWidth + this.gutter) * 2
30995         });
30996         
30997         positions.push({
30998             x : maxX - this.unitWidth * 3 - this.gutter * 2,
30999             y : minY
31000         });
31001         
31002         Roo.each(eItems, function(b,k){
31003             
31004             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31005
31006         }, this);
31007         
31008     },
31009     
31010     getVerticalOneBoxColPositions : function(x, y, box)
31011     {
31012         var pos = [];
31013         
31014         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31015         
31016         if(box[0].size == 'md-left'){
31017             rand = 0;
31018         }
31019         
31020         if(box[0].size == 'md-right'){
31021             rand = 1;
31022         }
31023         
31024         pos.push({
31025             x : x + (this.unitWidth + this.gutter) * rand,
31026             y : y
31027         });
31028         
31029         return pos;
31030     },
31031     
31032     getVerticalTwoBoxColPositions : function(x, y, box)
31033     {
31034         var pos = [];
31035         
31036         if(box[0].size == 'xs'){
31037             
31038             pos.push({
31039                 x : x,
31040                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31041             });
31042
31043             pos.push({
31044                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31045                 y : y
31046             });
31047             
31048             return pos;
31049             
31050         }
31051         
31052         pos.push({
31053             x : x,
31054             y : y
31055         });
31056
31057         pos.push({
31058             x : x + (this.unitWidth + this.gutter) * 2,
31059             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31060         });
31061         
31062         return pos;
31063         
31064     },
31065     
31066     getVerticalThreeBoxColPositions : function(x, y, box)
31067     {
31068         var pos = [];
31069         
31070         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31071             
31072             pos.push({
31073                 x : x,
31074                 y : y
31075             });
31076
31077             pos.push({
31078                 x : x + (this.unitWidth + this.gutter) * 1,
31079                 y : y
31080             });
31081             
31082             pos.push({
31083                 x : x + (this.unitWidth + this.gutter) * 2,
31084                 y : y
31085             });
31086             
31087             return pos;
31088             
31089         }
31090         
31091         if(box[0].size == 'xs' && box[1].size == 'xs'){
31092             
31093             pos.push({
31094                 x : x,
31095                 y : y
31096             });
31097
31098             pos.push({
31099                 x : x,
31100                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31101             });
31102             
31103             pos.push({
31104                 x : x + (this.unitWidth + this.gutter) * 1,
31105                 y : y
31106             });
31107             
31108             return pos;
31109             
31110         }
31111         
31112         pos.push({
31113             x : x,
31114             y : y
31115         });
31116
31117         pos.push({
31118             x : x + (this.unitWidth + this.gutter) * 2,
31119             y : y
31120         });
31121
31122         pos.push({
31123             x : x + (this.unitWidth + this.gutter) * 2,
31124             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31125         });
31126             
31127         return pos;
31128         
31129     },
31130     
31131     getVerticalFourBoxColPositions : function(x, y, box)
31132     {
31133         var pos = [];
31134         
31135         if(box[0].size == 'xs'){
31136             
31137             pos.push({
31138                 x : x,
31139                 y : y
31140             });
31141
31142             pos.push({
31143                 x : x,
31144                 y : y + (this.unitHeight + this.gutter) * 1
31145             });
31146             
31147             pos.push({
31148                 x : x,
31149                 y : y + (this.unitHeight + this.gutter) * 2
31150             });
31151             
31152             pos.push({
31153                 x : x + (this.unitWidth + this.gutter) * 1,
31154                 y : y
31155             });
31156             
31157             return pos;
31158             
31159         }
31160         
31161         pos.push({
31162             x : x,
31163             y : y
31164         });
31165
31166         pos.push({
31167             x : x + (this.unitWidth + this.gutter) * 2,
31168             y : y
31169         });
31170
31171         pos.push({
31172             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31173             y : y + (this.unitHeight + this.gutter) * 1
31174         });
31175
31176         pos.push({
31177             x : x + (this.unitWidth + this.gutter) * 2,
31178             y : y + (this.unitWidth + this.gutter) * 2
31179         });
31180
31181         return pos;
31182         
31183     },
31184     
31185     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31186     {
31187         var pos = [];
31188         
31189         if(box[0].size == 'md-left'){
31190             pos.push({
31191                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31192                 y : minY
31193             });
31194             
31195             return pos;
31196         }
31197         
31198         if(box[0].size == 'md-right'){
31199             pos.push({
31200                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31201                 y : minY + (this.unitWidth + this.gutter) * 1
31202             });
31203             
31204             return pos;
31205         }
31206         
31207         var rand = Math.floor(Math.random() * (4 - box[0].y));
31208         
31209         pos.push({
31210             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31211             y : minY + (this.unitWidth + this.gutter) * rand
31212         });
31213         
31214         return pos;
31215         
31216     },
31217     
31218     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31219     {
31220         var pos = [];
31221         
31222         if(box[0].size == 'xs'){
31223             
31224             pos.push({
31225                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31226                 y : minY
31227             });
31228
31229             pos.push({
31230                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31231                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31232             });
31233             
31234             return pos;
31235             
31236         }
31237         
31238         pos.push({
31239             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31240             y : minY
31241         });
31242
31243         pos.push({
31244             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31245             y : minY + (this.unitWidth + this.gutter) * 2
31246         });
31247         
31248         return pos;
31249         
31250     },
31251     
31252     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31253     {
31254         var pos = [];
31255         
31256         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31257             
31258             pos.push({
31259                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31260                 y : minY
31261             });
31262
31263             pos.push({
31264                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31265                 y : minY + (this.unitWidth + this.gutter) * 1
31266             });
31267             
31268             pos.push({
31269                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31270                 y : minY + (this.unitWidth + this.gutter) * 2
31271             });
31272             
31273             return pos;
31274             
31275         }
31276         
31277         if(box[0].size == 'xs' && box[1].size == 'xs'){
31278             
31279             pos.push({
31280                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31281                 y : minY
31282             });
31283
31284             pos.push({
31285                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31286                 y : minY
31287             });
31288             
31289             pos.push({
31290                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31291                 y : minY + (this.unitWidth + this.gutter) * 1
31292             });
31293             
31294             return pos;
31295             
31296         }
31297         
31298         pos.push({
31299             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31300             y : minY
31301         });
31302
31303         pos.push({
31304             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31305             y : minY + (this.unitWidth + this.gutter) * 2
31306         });
31307
31308         pos.push({
31309             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31310             y : minY + (this.unitWidth + this.gutter) * 2
31311         });
31312             
31313         return pos;
31314         
31315     },
31316     
31317     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31318     {
31319         var pos = [];
31320         
31321         if(box[0].size == 'xs'){
31322             
31323             pos.push({
31324                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].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),
31330                 y : minY
31331             });
31332             
31333             pos.push({
31334                 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),
31335                 y : minY
31336             });
31337             
31338             pos.push({
31339                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31340                 y : minY + (this.unitWidth + this.gutter) * 1
31341             });
31342             
31343             return pos;
31344             
31345         }
31346         
31347         pos.push({
31348             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31349             y : minY
31350         });
31351         
31352         pos.push({
31353             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].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),
31359             y : minY + (this.unitWidth + this.gutter) * 2
31360         });
31361         
31362         pos.push({
31363             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),
31364             y : minY + (this.unitWidth + this.gutter) * 2
31365         });
31366
31367         return pos;
31368         
31369     },
31370     
31371     /**
31372     * remove a Masonry Brick
31373     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31374     */
31375     removeBrick : function(brick_id)
31376     {
31377         if (!brick_id) {
31378             return;
31379         }
31380         
31381         for (var i = 0; i<this.bricks.length; i++) {
31382             if (this.bricks[i].id == brick_id) {
31383                 this.bricks.splice(i,1);
31384                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31385                 this.initial();
31386             }
31387         }
31388     },
31389     
31390     /**
31391     * adds a Masonry Brick
31392     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31393     */
31394     addBrick : function(cfg)
31395     {
31396         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31397         //this.register(cn);
31398         cn.parentId = this.id;
31399         cn.onRender(this.el, null);
31400         return cn;
31401     },
31402     
31403     /**
31404     * register a Masonry Brick
31405     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31406     */
31407     
31408     register : function(brick)
31409     {
31410         this.bricks.push(brick);
31411         brick.masonryId = this.id;
31412     },
31413     
31414     /**
31415     * clear all the Masonry Brick
31416     */
31417     clearAll : function()
31418     {
31419         this.bricks = [];
31420         //this.getChildContainer().dom.innerHTML = "";
31421         this.el.dom.innerHTML = '';
31422     },
31423     
31424     getSelected : function()
31425     {
31426         if (!this.selectedBrick) {
31427             return false;
31428         }
31429         
31430         return this.selectedBrick;
31431     }
31432 });
31433
31434 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31435     
31436     groups: {},
31437      /**
31438     * register a Masonry Layout
31439     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31440     */
31441     
31442     register : function(layout)
31443     {
31444         this.groups[layout.id] = layout;
31445     },
31446     /**
31447     * fetch a  Masonry Layout based on the masonry layout ID
31448     * @param {string} the masonry layout to add
31449     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31450     */
31451     
31452     get: function(layout_id) {
31453         if (typeof(this.groups[layout_id]) == 'undefined') {
31454             return false;
31455         }
31456         return this.groups[layout_id] ;
31457     }
31458     
31459     
31460     
31461 });
31462
31463  
31464
31465  /**
31466  *
31467  * This is based on 
31468  * http://masonry.desandro.com
31469  *
31470  * The idea is to render all the bricks based on vertical width...
31471  *
31472  * The original code extends 'outlayer' - we might need to use that....
31473  * 
31474  */
31475
31476
31477 /**
31478  * @class Roo.bootstrap.LayoutMasonryAuto
31479  * @extends Roo.bootstrap.Component
31480  * Bootstrap Layout Masonry class
31481  * 
31482  * @constructor
31483  * Create a new Element
31484  * @param {Object} config The config object
31485  */
31486
31487 Roo.bootstrap.LayoutMasonryAuto = function(config){
31488     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31489 };
31490
31491 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
31492     
31493       /**
31494      * @cfg {Boolean} isFitWidth  - resize the width..
31495      */   
31496     isFitWidth : false,  // options..
31497     /**
31498      * @cfg {Boolean} isOriginLeft = left align?
31499      */   
31500     isOriginLeft : true,
31501     /**
31502      * @cfg {Boolean} isOriginTop = top align?
31503      */   
31504     isOriginTop : false,
31505     /**
31506      * @cfg {Boolean} isLayoutInstant = no animation?
31507      */   
31508     isLayoutInstant : false, // needed?
31509     /**
31510      * @cfg {Boolean} isResizingContainer = not sure if this is used..
31511      */   
31512     isResizingContainer : true,
31513     /**
31514      * @cfg {Number} columnWidth  width of the columns 
31515      */   
31516     
31517     columnWidth : 0,
31518     
31519     /**
31520      * @cfg {Number} maxCols maximum number of columns
31521      */   
31522     
31523     maxCols: 0,
31524     /**
31525      * @cfg {Number} padHeight padding below box..
31526      */   
31527     
31528     padHeight : 10, 
31529     
31530     /**
31531      * @cfg {Boolean} isAutoInitial defalut true
31532      */   
31533     
31534     isAutoInitial : true, 
31535     
31536     // private?
31537     gutter : 0,
31538     
31539     containerWidth: 0,
31540     initialColumnWidth : 0,
31541     currentSize : null,
31542     
31543     colYs : null, // array.
31544     maxY : 0,
31545     padWidth: 10,
31546     
31547     
31548     tag: 'div',
31549     cls: '',
31550     bricks: null, //CompositeElement
31551     cols : 0, // array?
31552     // element : null, // wrapped now this.el
31553     _isLayoutInited : null, 
31554     
31555     
31556     getAutoCreate : function(){
31557         
31558         var cfg = {
31559             tag: this.tag,
31560             cls: 'blog-masonary-wrapper ' + this.cls,
31561             cn : {
31562                 cls : 'mas-boxes masonary'
31563             }
31564         };
31565         
31566         return cfg;
31567     },
31568     
31569     getChildContainer: function( )
31570     {
31571         if (this.boxesEl) {
31572             return this.boxesEl;
31573         }
31574         
31575         this.boxesEl = this.el.select('.mas-boxes').first();
31576         
31577         return this.boxesEl;
31578     },
31579     
31580     
31581     initEvents : function()
31582     {
31583         var _this = this;
31584         
31585         if(this.isAutoInitial){
31586             Roo.log('hook children rendered');
31587             this.on('childrenrendered', function() {
31588                 Roo.log('children rendered');
31589                 _this.initial();
31590             } ,this);
31591         }
31592         
31593     },
31594     
31595     initial : function()
31596     {
31597         this.reloadItems();
31598
31599         this.currentSize = this.el.getBox(true);
31600
31601         /// was window resize... - let's see if this works..
31602         Roo.EventManager.onWindowResize(this.resize, this); 
31603
31604         if(!this.isAutoInitial){
31605             this.layout();
31606             return;
31607         }
31608         
31609         this.layout.defer(500,this);
31610     },
31611     
31612     reloadItems: function()
31613     {
31614         this.bricks = this.el.select('.masonry-brick', true);
31615         
31616         this.bricks.each(function(b) {
31617             //Roo.log(b.getSize());
31618             if (!b.attr('originalwidth')) {
31619                 b.attr('originalwidth',  b.getSize().width);
31620             }
31621             
31622         });
31623         
31624         Roo.log(this.bricks.elements.length);
31625     },
31626     
31627     resize : function()
31628     {
31629         Roo.log('resize');
31630         var cs = this.el.getBox(true);
31631         
31632         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
31633             Roo.log("no change in with or X");
31634             return;
31635         }
31636         this.currentSize = cs;
31637         this.layout();
31638     },
31639     
31640     layout : function()
31641     {
31642          Roo.log('layout');
31643         this._resetLayout();
31644         //this._manageStamps();
31645       
31646         // don't animate first layout
31647         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31648         this.layoutItems( isInstant );
31649       
31650         // flag for initalized
31651         this._isLayoutInited = true;
31652     },
31653     
31654     layoutItems : function( isInstant )
31655     {
31656         //var items = this._getItemsForLayout( this.items );
31657         // original code supports filtering layout items.. we just ignore it..
31658         
31659         this._layoutItems( this.bricks , isInstant );
31660       
31661         this._postLayout();
31662     },
31663     _layoutItems : function ( items , isInstant)
31664     {
31665        //this.fireEvent( 'layout', this, items );
31666     
31667
31668         if ( !items || !items.elements.length ) {
31669           // no items, emit event with empty array
31670             return;
31671         }
31672
31673         var queue = [];
31674         items.each(function(item) {
31675             Roo.log("layout item");
31676             Roo.log(item);
31677             // get x/y object from method
31678             var position = this._getItemLayoutPosition( item );
31679             // enqueue
31680             position.item = item;
31681             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
31682             queue.push( position );
31683         }, this);
31684       
31685         this._processLayoutQueue( queue );
31686     },
31687     /** Sets position of item in DOM
31688     * @param {Element} item
31689     * @param {Number} x - horizontal position
31690     * @param {Number} y - vertical position
31691     * @param {Boolean} isInstant - disables transitions
31692     */
31693     _processLayoutQueue : function( queue )
31694     {
31695         for ( var i=0, len = queue.length; i < len; i++ ) {
31696             var obj = queue[i];
31697             obj.item.position('absolute');
31698             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
31699         }
31700     },
31701       
31702     
31703     /**
31704     * Any logic you want to do after each layout,
31705     * i.e. size the container
31706     */
31707     _postLayout : function()
31708     {
31709         this.resizeContainer();
31710     },
31711     
31712     resizeContainer : function()
31713     {
31714         if ( !this.isResizingContainer ) {
31715             return;
31716         }
31717         var size = this._getContainerSize();
31718         if ( size ) {
31719             this.el.setSize(size.width,size.height);
31720             this.boxesEl.setSize(size.width,size.height);
31721         }
31722     },
31723     
31724     
31725     
31726     _resetLayout : function()
31727     {
31728         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
31729         this.colWidth = this.el.getWidth();
31730         //this.gutter = this.el.getWidth(); 
31731         
31732         this.measureColumns();
31733
31734         // reset column Y
31735         var i = this.cols;
31736         this.colYs = [];
31737         while (i--) {
31738             this.colYs.push( 0 );
31739         }
31740     
31741         this.maxY = 0;
31742     },
31743
31744     measureColumns : function()
31745     {
31746         this.getContainerWidth();
31747       // if columnWidth is 0, default to outerWidth of first item
31748         if ( !this.columnWidth ) {
31749             var firstItem = this.bricks.first();
31750             Roo.log(firstItem);
31751             this.columnWidth  = this.containerWidth;
31752             if (firstItem && firstItem.attr('originalwidth') ) {
31753                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
31754             }
31755             // columnWidth fall back to item of first element
31756             Roo.log("set column width?");
31757                         this.initialColumnWidth = this.columnWidth  ;
31758
31759             // if first elem has no width, default to size of container
31760             
31761         }
31762         
31763         
31764         if (this.initialColumnWidth) {
31765             this.columnWidth = this.initialColumnWidth;
31766         }
31767         
31768         
31769             
31770         // column width is fixed at the top - however if container width get's smaller we should
31771         // reduce it...
31772         
31773         // this bit calcs how man columns..
31774             
31775         var columnWidth = this.columnWidth += this.gutter;
31776       
31777         // calculate columns
31778         var containerWidth = this.containerWidth + this.gutter;
31779         
31780         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
31781         // fix rounding errors, typically with gutters
31782         var excess = columnWidth - containerWidth % columnWidth;
31783         
31784         
31785         // if overshoot is less than a pixel, round up, otherwise floor it
31786         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
31787         cols = Math[ mathMethod ]( cols );
31788         this.cols = Math.max( cols, 1 );
31789         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31790         
31791          // padding positioning..
31792         var totalColWidth = this.cols * this.columnWidth;
31793         var padavail = this.containerWidth - totalColWidth;
31794         // so for 2 columns - we need 3 'pads'
31795         
31796         var padNeeded = (1+this.cols) * this.padWidth;
31797         
31798         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
31799         
31800         this.columnWidth += padExtra
31801         //this.padWidth = Math.floor(padavail /  ( this.cols));
31802         
31803         // adjust colum width so that padding is fixed??
31804         
31805         // we have 3 columns ... total = width * 3
31806         // we have X left over... that should be used by 
31807         
31808         //if (this.expandC) {
31809             
31810         //}
31811         
31812         
31813         
31814     },
31815     
31816     getContainerWidth : function()
31817     {
31818        /* // container is parent if fit width
31819         var container = this.isFitWidth ? this.element.parentNode : this.element;
31820         // check that this.size and size are there
31821         // IE8 triggers resize on body size change, so they might not be
31822         
31823         var size = getSize( container );  //FIXME
31824         this.containerWidth = size && size.innerWidth; //FIXME
31825         */
31826          
31827         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31828         
31829     },
31830     
31831     _getItemLayoutPosition : function( item )  // what is item?
31832     {
31833         // we resize the item to our columnWidth..
31834       
31835         item.setWidth(this.columnWidth);
31836         item.autoBoxAdjust  = false;
31837         
31838         var sz = item.getSize();
31839  
31840         // how many columns does this brick span
31841         var remainder = this.containerWidth % this.columnWidth;
31842         
31843         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
31844         // round if off by 1 pixel, otherwise use ceil
31845         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
31846         colSpan = Math.min( colSpan, this.cols );
31847         
31848         // normally this should be '1' as we dont' currently allow multi width columns..
31849         
31850         var colGroup = this._getColGroup( colSpan );
31851         // get the minimum Y value from the columns
31852         var minimumY = Math.min.apply( Math, colGroup );
31853         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
31854         
31855         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
31856          
31857         // position the brick
31858         var position = {
31859             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
31860             y: this.currentSize.y + minimumY + this.padHeight
31861         };
31862         
31863         Roo.log(position);
31864         // apply setHeight to necessary columns
31865         var setHeight = minimumY + sz.height + this.padHeight;
31866         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
31867         
31868         var setSpan = this.cols + 1 - colGroup.length;
31869         for ( var i = 0; i < setSpan; i++ ) {
31870           this.colYs[ shortColIndex + i ] = setHeight ;
31871         }
31872       
31873         return position;
31874     },
31875     
31876     /**
31877      * @param {Number} colSpan - number of columns the element spans
31878      * @returns {Array} colGroup
31879      */
31880     _getColGroup : function( colSpan )
31881     {
31882         if ( colSpan < 2 ) {
31883           // if brick spans only one column, use all the column Ys
31884           return this.colYs;
31885         }
31886       
31887         var colGroup = [];
31888         // how many different places could this brick fit horizontally
31889         var groupCount = this.cols + 1 - colSpan;
31890         // for each group potential horizontal position
31891         for ( var i = 0; i < groupCount; i++ ) {
31892           // make an array of colY values for that one group
31893           var groupColYs = this.colYs.slice( i, i + colSpan );
31894           // and get the max value of the array
31895           colGroup[i] = Math.max.apply( Math, groupColYs );
31896         }
31897         return colGroup;
31898     },
31899     /*
31900     _manageStamp : function( stamp )
31901     {
31902         var stampSize =  stamp.getSize();
31903         var offset = stamp.getBox();
31904         // get the columns that this stamp affects
31905         var firstX = this.isOriginLeft ? offset.x : offset.right;
31906         var lastX = firstX + stampSize.width;
31907         var firstCol = Math.floor( firstX / this.columnWidth );
31908         firstCol = Math.max( 0, firstCol );
31909         
31910         var lastCol = Math.floor( lastX / this.columnWidth );
31911         // lastCol should not go over if multiple of columnWidth #425
31912         lastCol -= lastX % this.columnWidth ? 0 : 1;
31913         lastCol = Math.min( this.cols - 1, lastCol );
31914         
31915         // set colYs to bottom of the stamp
31916         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
31917             stampSize.height;
31918             
31919         for ( var i = firstCol; i <= lastCol; i++ ) {
31920           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
31921         }
31922     },
31923     */
31924     
31925     _getContainerSize : function()
31926     {
31927         this.maxY = Math.max.apply( Math, this.colYs );
31928         var size = {
31929             height: this.maxY
31930         };
31931       
31932         if ( this.isFitWidth ) {
31933             size.width = this._getContainerFitWidth();
31934         }
31935       
31936         return size;
31937     },
31938     
31939     _getContainerFitWidth : function()
31940     {
31941         var unusedCols = 0;
31942         // count unused columns
31943         var i = this.cols;
31944         while ( --i ) {
31945           if ( this.colYs[i] !== 0 ) {
31946             break;
31947           }
31948           unusedCols++;
31949         }
31950         // fit container to columns that have been used
31951         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
31952     },
31953     
31954     needsResizeLayout : function()
31955     {
31956         var previousWidth = this.containerWidth;
31957         this.getContainerWidth();
31958         return previousWidth !== this.containerWidth;
31959     }
31960  
31961 });
31962
31963  
31964
31965  /*
31966  * - LGPL
31967  *
31968  * element
31969  * 
31970  */
31971
31972 /**
31973  * @class Roo.bootstrap.MasonryBrick
31974  * @extends Roo.bootstrap.Component
31975  * Bootstrap MasonryBrick class
31976  * 
31977  * @constructor
31978  * Create a new MasonryBrick
31979  * @param {Object} config The config object
31980  */
31981
31982 Roo.bootstrap.MasonryBrick = function(config){
31983     
31984     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
31985     
31986     Roo.bootstrap.MasonryBrick.register(this);
31987     
31988     this.addEvents({
31989         // raw events
31990         /**
31991          * @event click
31992          * When a MasonryBrick is clcik
31993          * @param {Roo.bootstrap.MasonryBrick} this
31994          * @param {Roo.EventObject} e
31995          */
31996         "click" : true
31997     });
31998 };
31999
32000 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32001     
32002     /**
32003      * @cfg {String} title
32004      */   
32005     title : '',
32006     /**
32007      * @cfg {String} html
32008      */   
32009     html : '',
32010     /**
32011      * @cfg {String} bgimage
32012      */   
32013     bgimage : '',
32014     /**
32015      * @cfg {String} videourl
32016      */   
32017     videourl : '',
32018     /**
32019      * @cfg {String} cls
32020      */   
32021     cls : '',
32022     /**
32023      * @cfg {String} href
32024      */   
32025     href : '',
32026     /**
32027      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32028      */   
32029     size : 'xs',
32030     
32031     /**
32032      * @cfg {String} placetitle (center|bottom)
32033      */   
32034     placetitle : '',
32035     
32036     /**
32037      * @cfg {Boolean} isFitContainer defalut true
32038      */   
32039     isFitContainer : true, 
32040     
32041     /**
32042      * @cfg {Boolean} preventDefault defalut false
32043      */   
32044     preventDefault : false, 
32045     
32046     /**
32047      * @cfg {Boolean} inverse defalut false
32048      */   
32049     maskInverse : false, 
32050     
32051     getAutoCreate : function()
32052     {
32053         if(!this.isFitContainer){
32054             return this.getSplitAutoCreate();
32055         }
32056         
32057         var cls = 'masonry-brick masonry-brick-full';
32058         
32059         if(this.href.length){
32060             cls += ' masonry-brick-link';
32061         }
32062         
32063         if(this.bgimage.length){
32064             cls += ' masonry-brick-image';
32065         }
32066         
32067         if(this.maskInverse){
32068             cls += ' mask-inverse';
32069         }
32070         
32071         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32072             cls += ' enable-mask';
32073         }
32074         
32075         if(this.size){
32076             cls += ' masonry-' + this.size + '-brick';
32077         }
32078         
32079         if(this.placetitle.length){
32080             
32081             switch (this.placetitle) {
32082                 case 'center' :
32083                     cls += ' masonry-center-title';
32084                     break;
32085                 case 'bottom' :
32086                     cls += ' masonry-bottom-title';
32087                     break;
32088                 default:
32089                     break;
32090             }
32091             
32092         } else {
32093             if(!this.html.length && !this.bgimage.length){
32094                 cls += ' masonry-center-title';
32095             }
32096
32097             if(!this.html.length && this.bgimage.length){
32098                 cls += ' masonry-bottom-title';
32099             }
32100         }
32101         
32102         if(this.cls){
32103             cls += ' ' + this.cls;
32104         }
32105         
32106         var cfg = {
32107             tag: (this.href.length) ? 'a' : 'div',
32108             cls: cls,
32109             cn: [
32110                 {
32111                     tag: 'div',
32112                     cls: 'masonry-brick-mask'
32113                 },
32114                 {
32115                     tag: 'div',
32116                     cls: 'masonry-brick-paragraph',
32117                     cn: []
32118                 }
32119             ]
32120         };
32121         
32122         if(this.href.length){
32123             cfg.href = this.href;
32124         }
32125         
32126         var cn = cfg.cn[1].cn;
32127         
32128         if(this.title.length){
32129             cn.push({
32130                 tag: 'h4',
32131                 cls: 'masonry-brick-title',
32132                 html: this.title
32133             });
32134         }
32135         
32136         if(this.html.length){
32137             cn.push({
32138                 tag: 'p',
32139                 cls: 'masonry-brick-text',
32140                 html: this.html
32141             });
32142         }
32143         
32144         if (!this.title.length && !this.html.length) {
32145             cfg.cn[1].cls += ' hide';
32146         }
32147         
32148         if(this.bgimage.length){
32149             cfg.cn.push({
32150                 tag: 'img',
32151                 cls: 'masonry-brick-image-view',
32152                 src: this.bgimage
32153             });
32154         }
32155         
32156         if(this.videourl.length){
32157             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32158             // youtube support only?
32159             cfg.cn.push({
32160                 tag: 'iframe',
32161                 cls: 'masonry-brick-image-view',
32162                 src: vurl,
32163                 frameborder : 0,
32164                 allowfullscreen : true
32165             });
32166         }
32167         
32168         return cfg;
32169         
32170     },
32171     
32172     getSplitAutoCreate : function()
32173     {
32174         var cls = 'masonry-brick masonry-brick-split';
32175         
32176         if(this.href.length){
32177             cls += ' masonry-brick-link';
32178         }
32179         
32180         if(this.bgimage.length){
32181             cls += ' masonry-brick-image';
32182         }
32183         
32184         if(this.size){
32185             cls += ' masonry-' + this.size + '-brick';
32186         }
32187         
32188         switch (this.placetitle) {
32189             case 'center' :
32190                 cls += ' masonry-center-title';
32191                 break;
32192             case 'bottom' :
32193                 cls += ' masonry-bottom-title';
32194                 break;
32195             default:
32196                 if(!this.bgimage.length){
32197                     cls += ' masonry-center-title';
32198                 }
32199
32200                 if(this.bgimage.length){
32201                     cls += ' masonry-bottom-title';
32202                 }
32203                 break;
32204         }
32205         
32206         if(this.cls){
32207             cls += ' ' + this.cls;
32208         }
32209         
32210         var cfg = {
32211             tag: (this.href.length) ? 'a' : 'div',
32212             cls: cls,
32213             cn: [
32214                 {
32215                     tag: 'div',
32216                     cls: 'masonry-brick-split-head',
32217                     cn: [
32218                         {
32219                             tag: 'div',
32220                             cls: 'masonry-brick-paragraph',
32221                             cn: []
32222                         }
32223                     ]
32224                 },
32225                 {
32226                     tag: 'div',
32227                     cls: 'masonry-brick-split-body',
32228                     cn: []
32229                 }
32230             ]
32231         };
32232         
32233         if(this.href.length){
32234             cfg.href = this.href;
32235         }
32236         
32237         if(this.title.length){
32238             cfg.cn[0].cn[0].cn.push({
32239                 tag: 'h4',
32240                 cls: 'masonry-brick-title',
32241                 html: this.title
32242             });
32243         }
32244         
32245         if(this.html.length){
32246             cfg.cn[1].cn.push({
32247                 tag: 'p',
32248                 cls: 'masonry-brick-text',
32249                 html: this.html
32250             });
32251         }
32252
32253         if(this.bgimage.length){
32254             cfg.cn[0].cn.push({
32255                 tag: 'img',
32256                 cls: 'masonry-brick-image-view',
32257                 src: this.bgimage
32258             });
32259         }
32260         
32261         if(this.videourl.length){
32262             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32263             // youtube support only?
32264             cfg.cn[0].cn.cn.push({
32265                 tag: 'iframe',
32266                 cls: 'masonry-brick-image-view',
32267                 src: vurl,
32268                 frameborder : 0,
32269                 allowfullscreen : true
32270             });
32271         }
32272         
32273         return cfg;
32274     },
32275     
32276     initEvents: function() 
32277     {
32278         switch (this.size) {
32279             case 'xs' :
32280                 this.x = 1;
32281                 this.y = 1;
32282                 break;
32283             case 'sm' :
32284                 this.x = 2;
32285                 this.y = 2;
32286                 break;
32287             case 'md' :
32288             case 'md-left' :
32289             case 'md-right' :
32290                 this.x = 3;
32291                 this.y = 3;
32292                 break;
32293             case 'tall' :
32294                 this.x = 2;
32295                 this.y = 3;
32296                 break;
32297             case 'wide' :
32298                 this.x = 3;
32299                 this.y = 2;
32300                 break;
32301             case 'wide-thin' :
32302                 this.x = 3;
32303                 this.y = 1;
32304                 break;
32305                         
32306             default :
32307                 break;
32308         }
32309         
32310         if(Roo.isTouch){
32311             this.el.on('touchstart', this.onTouchStart, this);
32312             this.el.on('touchmove', this.onTouchMove, this);
32313             this.el.on('touchend', this.onTouchEnd, this);
32314             this.el.on('contextmenu', this.onContextMenu, this);
32315         } else {
32316             this.el.on('mouseenter'  ,this.enter, this);
32317             this.el.on('mouseleave', this.leave, this);
32318             this.el.on('click', this.onClick, this);
32319         }
32320         
32321         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32322             this.parent().bricks.push(this);   
32323         }
32324         
32325     },
32326     
32327     onClick: function(e, el)
32328     {
32329         var time = this.endTimer - this.startTimer;
32330         // Roo.log(e.preventDefault());
32331         if(Roo.isTouch){
32332             if(time > 1000){
32333                 e.preventDefault();
32334                 return;
32335             }
32336         }
32337         
32338         if(!this.preventDefault){
32339             return;
32340         }
32341         
32342         e.preventDefault();
32343         
32344         if (this.activcClass != '') {
32345             this.selectBrick();
32346         }
32347         
32348         this.fireEvent('click', this);
32349     },
32350     
32351     enter: function(e, el)
32352     {
32353         e.preventDefault();
32354         
32355         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32356             return;
32357         }
32358         
32359         if(this.bgimage.length && this.html.length){
32360             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32361         }
32362     },
32363     
32364     leave: function(e, el)
32365     {
32366         e.preventDefault();
32367         
32368         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32369             return;
32370         }
32371         
32372         if(this.bgimage.length && this.html.length){
32373             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32374         }
32375     },
32376     
32377     onTouchStart: function(e, el)
32378     {
32379 //        e.preventDefault();
32380         
32381         this.touchmoved = false;
32382         
32383         if(!this.isFitContainer){
32384             return;
32385         }
32386         
32387         if(!this.bgimage.length || !this.html.length){
32388             return;
32389         }
32390         
32391         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32392         
32393         this.timer = new Date().getTime();
32394         
32395     },
32396     
32397     onTouchMove: function(e, el)
32398     {
32399         this.touchmoved = true;
32400     },
32401     
32402     onContextMenu : function(e,el)
32403     {
32404         e.preventDefault();
32405         e.stopPropagation();
32406         return false;
32407     },
32408     
32409     onTouchEnd: function(e, el)
32410     {
32411 //        e.preventDefault();
32412         
32413         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32414         
32415             this.leave(e,el);
32416             
32417             return;
32418         }
32419         
32420         if(!this.bgimage.length || !this.html.length){
32421             
32422             if(this.href.length){
32423                 window.location.href = this.href;
32424             }
32425             
32426             return;
32427         }
32428         
32429         if(!this.isFitContainer){
32430             return;
32431         }
32432         
32433         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32434         
32435         window.location.href = this.href;
32436     },
32437     
32438     //selection on single brick only
32439     selectBrick : function() {
32440         
32441         if (!this.parentId) {
32442             return;
32443         }
32444         
32445         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32446         var index = m.selectedBrick.indexOf(this.id);
32447         
32448         if ( index > -1) {
32449             m.selectedBrick.splice(index,1);
32450             this.el.removeClass(this.activeClass);
32451             return;
32452         }
32453         
32454         for(var i = 0; i < m.selectedBrick.length; i++) {
32455             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32456             b.el.removeClass(b.activeClass);
32457         }
32458         
32459         m.selectedBrick = [];
32460         
32461         m.selectedBrick.push(this.id);
32462         this.el.addClass(this.activeClass);
32463         return;
32464     }
32465     
32466 });
32467
32468 Roo.apply(Roo.bootstrap.MasonryBrick, {
32469     
32470     //groups: {},
32471     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32472      /**
32473     * register a Masonry Brick
32474     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32475     */
32476     
32477     register : function(brick)
32478     {
32479         //this.groups[brick.id] = brick;
32480         this.groups.add(brick.id, brick);
32481     },
32482     /**
32483     * fetch a  masonry brick based on the masonry brick ID
32484     * @param {string} the masonry brick to add
32485     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32486     */
32487     
32488     get: function(brick_id) 
32489     {
32490         // if (typeof(this.groups[brick_id]) == 'undefined') {
32491         //     return false;
32492         // }
32493         // return this.groups[brick_id] ;
32494         
32495         if(this.groups.key(brick_id)) {
32496             return this.groups.key(brick_id);
32497         }
32498         
32499         return false;
32500     }
32501     
32502     
32503     
32504 });
32505
32506  /*
32507  * - LGPL
32508  *
32509  * element
32510  * 
32511  */
32512
32513 /**
32514  * @class Roo.bootstrap.Brick
32515  * @extends Roo.bootstrap.Component
32516  * Bootstrap Brick class
32517  * 
32518  * @constructor
32519  * Create a new Brick
32520  * @param {Object} config The config object
32521  */
32522
32523 Roo.bootstrap.Brick = function(config){
32524     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
32525     
32526     this.addEvents({
32527         // raw events
32528         /**
32529          * @event click
32530          * When a Brick is click
32531          * @param {Roo.bootstrap.Brick} this
32532          * @param {Roo.EventObject} e
32533          */
32534         "click" : true
32535     });
32536 };
32537
32538 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
32539     
32540     /**
32541      * @cfg {String} title
32542      */   
32543     title : '',
32544     /**
32545      * @cfg {String} html
32546      */   
32547     html : '',
32548     /**
32549      * @cfg {String} bgimage
32550      */   
32551     bgimage : '',
32552     /**
32553      * @cfg {String} cls
32554      */   
32555     cls : '',
32556     /**
32557      * @cfg {String} href
32558      */   
32559     href : '',
32560     /**
32561      * @cfg {String} video
32562      */   
32563     video : '',
32564     /**
32565      * @cfg {Boolean} square
32566      */   
32567     square : true,
32568     
32569     getAutoCreate : function()
32570     {
32571         var cls = 'roo-brick';
32572         
32573         if(this.href.length){
32574             cls += ' roo-brick-link';
32575         }
32576         
32577         if(this.bgimage.length){
32578             cls += ' roo-brick-image';
32579         }
32580         
32581         if(!this.html.length && !this.bgimage.length){
32582             cls += ' roo-brick-center-title';
32583         }
32584         
32585         if(!this.html.length && this.bgimage.length){
32586             cls += ' roo-brick-bottom-title';
32587         }
32588         
32589         if(this.cls){
32590             cls += ' ' + this.cls;
32591         }
32592         
32593         var cfg = {
32594             tag: (this.href.length) ? 'a' : 'div',
32595             cls: cls,
32596             cn: [
32597                 {
32598                     tag: 'div',
32599                     cls: 'roo-brick-paragraph',
32600                     cn: []
32601                 }
32602             ]
32603         };
32604         
32605         if(this.href.length){
32606             cfg.href = this.href;
32607         }
32608         
32609         var cn = cfg.cn[0].cn;
32610         
32611         if(this.title.length){
32612             cn.push({
32613                 tag: 'h4',
32614                 cls: 'roo-brick-title',
32615                 html: this.title
32616             });
32617         }
32618         
32619         if(this.html.length){
32620             cn.push({
32621                 tag: 'p',
32622                 cls: 'roo-brick-text',
32623                 html: this.html
32624             });
32625         } else {
32626             cn.cls += ' hide';
32627         }
32628         
32629         if(this.bgimage.length){
32630             cfg.cn.push({
32631                 tag: 'img',
32632                 cls: 'roo-brick-image-view',
32633                 src: this.bgimage
32634             });
32635         }
32636         
32637         return cfg;
32638     },
32639     
32640     initEvents: function() 
32641     {
32642         if(this.title.length || this.html.length){
32643             this.el.on('mouseenter'  ,this.enter, this);
32644             this.el.on('mouseleave', this.leave, this);
32645         }
32646         
32647         Roo.EventManager.onWindowResize(this.resize, this); 
32648         
32649         if(this.bgimage.length){
32650             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
32651             this.imageEl.on('load', this.onImageLoad, this);
32652             return;
32653         }
32654         
32655         this.resize();
32656     },
32657     
32658     onImageLoad : function()
32659     {
32660         this.resize();
32661     },
32662     
32663     resize : function()
32664     {
32665         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
32666         
32667         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
32668         
32669         if(this.bgimage.length){
32670             var image = this.el.select('.roo-brick-image-view', true).first();
32671             
32672             image.setWidth(paragraph.getWidth());
32673             
32674             if(this.square){
32675                 image.setHeight(paragraph.getWidth());
32676             }
32677             
32678             this.el.setHeight(image.getHeight());
32679             paragraph.setHeight(image.getHeight());
32680             
32681         }
32682         
32683     },
32684     
32685     enter: function(e, el)
32686     {
32687         e.preventDefault();
32688         
32689         if(this.bgimage.length){
32690             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
32691             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
32692         }
32693     },
32694     
32695     leave: function(e, el)
32696     {
32697         e.preventDefault();
32698         
32699         if(this.bgimage.length){
32700             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
32701             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
32702         }
32703     }
32704     
32705 });
32706
32707  
32708
32709  /*
32710  * - LGPL
32711  *
32712  * Input
32713  * 
32714  */
32715
32716 /**
32717  * @class Roo.bootstrap.NumberField
32718  * @extends Roo.bootstrap.Input
32719  * Bootstrap NumberField class
32720  * 
32721  * 
32722  * 
32723  * 
32724  * @constructor
32725  * Create a new NumberField
32726  * @param {Object} config The config object
32727  */
32728
32729 Roo.bootstrap.NumberField = function(config){
32730     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
32731 };
32732
32733 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
32734     
32735     /**
32736      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
32737      */
32738     allowDecimals : true,
32739     /**
32740      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
32741      */
32742     decimalSeparator : ".",
32743     /**
32744      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
32745      */
32746     decimalPrecision : 2,
32747     /**
32748      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
32749      */
32750     allowNegative : true,
32751     /**
32752      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
32753      */
32754     minValue : Number.NEGATIVE_INFINITY,
32755     /**
32756      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
32757      */
32758     maxValue : Number.MAX_VALUE,
32759     /**
32760      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
32761      */
32762     minText : "The minimum value for this field is {0}",
32763     /**
32764      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
32765      */
32766     maxText : "The maximum value for this field is {0}",
32767     /**
32768      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
32769      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
32770      */
32771     nanText : "{0} is not a valid number",
32772     /**
32773      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
32774      */
32775     castInt : true,
32776
32777     // private
32778     initEvents : function()
32779     {   
32780         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
32781         
32782         var allowed = "0123456789";
32783         
32784         if(this.allowDecimals){
32785             allowed += this.decimalSeparator;
32786         }
32787         
32788         if(this.allowNegative){
32789             allowed += "-";
32790         }
32791         
32792         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
32793         
32794         var keyPress = function(e){
32795             
32796             var k = e.getKey();
32797             
32798             var c = e.getCharCode();
32799             
32800             if(
32801                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
32802                     allowed.indexOf(String.fromCharCode(c)) === -1
32803             ){
32804                 e.stopEvent();
32805                 return;
32806             }
32807             
32808             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
32809                 return;
32810             }
32811             
32812             if(allowed.indexOf(String.fromCharCode(c)) === -1){
32813                 e.stopEvent();
32814             }
32815         };
32816         
32817         this.el.on("keypress", keyPress, this);
32818     },
32819     
32820     validateValue : function(value)
32821     {
32822         
32823         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
32824             return false;
32825         }
32826         
32827         var num = this.parseValue(value);
32828         
32829         if(isNaN(num)){
32830             this.markInvalid(String.format(this.nanText, value));
32831             return false;
32832         }
32833         
32834         if(num < this.minValue){
32835             this.markInvalid(String.format(this.minText, this.minValue));
32836             return false;
32837         }
32838         
32839         if(num > this.maxValue){
32840             this.markInvalid(String.format(this.maxText, this.maxValue));
32841             return false;
32842         }
32843         
32844         return true;
32845     },
32846
32847     getValue : function()
32848     {
32849         return this.fixPrecision(this.parseValue(Roo.bootstrap.NumberField.superclass.getValue.call(this)));
32850     },
32851
32852     parseValue : function(value)
32853     {
32854         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
32855         return isNaN(value) ? '' : value;
32856     },
32857
32858     fixPrecision : function(value)
32859     {
32860         var nan = isNaN(value);
32861         
32862         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
32863             return nan ? '' : value;
32864         }
32865         return parseFloat(value).toFixed(this.decimalPrecision);
32866     },
32867
32868     setValue : function(v)
32869     {
32870         v = this.fixPrecision(v);
32871         Roo.bootstrap.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
32872     },
32873
32874     decimalPrecisionFcn : function(v)
32875     {
32876         return Math.floor(v);
32877     },
32878
32879     beforeBlur : function()
32880     {
32881         if(!this.castInt){
32882             return;
32883         }
32884         
32885         var v = this.parseValue(this.getRawValue());
32886         if(v){
32887             this.setValue(v);
32888         }
32889     }
32890     
32891 });
32892
32893  
32894
32895 /*
32896 * Licence: LGPL
32897 */
32898
32899 /**
32900  * @class Roo.bootstrap.DocumentSlider
32901  * @extends Roo.bootstrap.Component
32902  * Bootstrap DocumentSlider class
32903  * 
32904  * @constructor
32905  * Create a new DocumentViewer
32906  * @param {Object} config The config object
32907  */
32908
32909 Roo.bootstrap.DocumentSlider = function(config){
32910     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
32911     
32912     this.files = [];
32913     
32914     this.addEvents({
32915         /**
32916          * @event initial
32917          * Fire after initEvent
32918          * @param {Roo.bootstrap.DocumentSlider} this
32919          */
32920         "initial" : true,
32921         /**
32922          * @event update
32923          * Fire after update
32924          * @param {Roo.bootstrap.DocumentSlider} this
32925          */
32926         "update" : true,
32927         /**
32928          * @event click
32929          * Fire after click
32930          * @param {Roo.bootstrap.DocumentSlider} this
32931          */
32932         "click" : true
32933     });
32934 };
32935
32936 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
32937     
32938     files : false,
32939     
32940     indicator : 0,
32941     
32942     getAutoCreate : function()
32943     {
32944         var cfg = {
32945             tag : 'div',
32946             cls : 'roo-document-slider',
32947             cn : [
32948                 {
32949                     tag : 'div',
32950                     cls : 'roo-document-slider-header',
32951                     cn : [
32952                         {
32953                             tag : 'div',
32954                             cls : 'roo-document-slider-header-title'
32955                         }
32956                     ]
32957                 },
32958                 {
32959                     tag : 'div',
32960                     cls : 'roo-document-slider-body',
32961                     cn : [
32962                         {
32963                             tag : 'div',
32964                             cls : 'roo-document-slider-prev',
32965                             cn : [
32966                                 {
32967                                     tag : 'i',
32968                                     cls : 'fa fa-chevron-left'
32969                                 }
32970                             ]
32971                         },
32972                         {
32973                             tag : 'div',
32974                             cls : 'roo-document-slider-thumb',
32975                             cn : [
32976                                 {
32977                                     tag : 'img',
32978                                     cls : 'roo-document-slider-image'
32979                                 }
32980                             ]
32981                         },
32982                         {
32983                             tag : 'div',
32984                             cls : 'roo-document-slider-next',
32985                             cn : [
32986                                 {
32987                                     tag : 'i',
32988                                     cls : 'fa fa-chevron-right'
32989                                 }
32990                             ]
32991                         }
32992                     ]
32993                 }
32994             ]
32995         };
32996         
32997         return cfg;
32998     },
32999     
33000     initEvents : function()
33001     {
33002         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33003         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33004         
33005         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33006         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33007         
33008         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33009         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33010         
33011         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33012         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33013         
33014         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33015         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33016         
33017         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33018         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33019         
33020         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33021         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33022         
33023         this.thumbEl.on('click', this.onClick, this);
33024         
33025         this.prevIndicator.on('click', this.prev, this);
33026         
33027         this.nextIndicator.on('click', this.next, this);
33028         
33029     },
33030     
33031     initial : function()
33032     {
33033         if(this.files.length){
33034             this.indicator = 1;
33035             this.update()
33036         }
33037         
33038         this.fireEvent('initial', this);
33039     },
33040     
33041     update : function()
33042     {
33043         this.imageEl.attr('src', this.files[this.indicator - 1]);
33044         
33045         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33046         
33047         this.prevIndicator.show();
33048         
33049         if(this.indicator == 1){
33050             this.prevIndicator.hide();
33051         }
33052         
33053         this.nextIndicator.show();
33054         
33055         if(this.indicator == this.files.length){
33056             this.nextIndicator.hide();
33057         }
33058         
33059         this.thumbEl.scrollTo('top');
33060         
33061         this.fireEvent('update', this);
33062     },
33063     
33064     onClick : function(e)
33065     {
33066         e.preventDefault();
33067         
33068         this.fireEvent('click', this);
33069     },
33070     
33071     prev : function(e)
33072     {
33073         e.preventDefault();
33074         
33075         this.indicator = Math.max(1, this.indicator - 1);
33076         
33077         this.update();
33078     },
33079     
33080     next : function(e)
33081     {
33082         e.preventDefault();
33083         
33084         this.indicator = Math.min(this.files.length, this.indicator + 1);
33085         
33086         this.update();
33087     }
33088 });
33089 /*
33090  * - LGPL
33091  *
33092  * RadioSet
33093  *
33094  *
33095  */
33096
33097 /**
33098  * @class Roo.bootstrap.RadioSet
33099  * @extends Roo.bootstrap.Input
33100  * Bootstrap RadioSet class
33101  * @cfg {String} indicatorpos (left|right) default left
33102  * @cfg {Boolean} inline (true|false) inline the element (default true)
33103  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33104  * @constructor
33105  * Create a new RadioSet
33106  * @param {Object} config The config object
33107  */
33108
33109 Roo.bootstrap.RadioSet = function(config){
33110     
33111     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33112     
33113     this.radioes = [];
33114     
33115     Roo.bootstrap.RadioSet.register(this);
33116     
33117     this.addEvents({
33118         /**
33119         * @event check
33120         * Fires when the element is checked or unchecked.
33121         * @param {Roo.bootstrap.RadioSet} this This radio
33122         * @param {Roo.bootstrap.Radio} item The checked item
33123         */
33124        check : true
33125     });
33126     
33127 };
33128
33129 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33130
33131     radioes : false,
33132     
33133     inline : true,
33134     
33135     weight : '',
33136     
33137     indicatorpos : 'left',
33138     
33139     getAutoCreate : function()
33140     {
33141         var label = {
33142             tag : 'label',
33143             cls : 'roo-radio-set-label',
33144             cn : [
33145                 {
33146                     tag : 'span',
33147                     html : this.fieldLabel
33148                 }
33149             ]
33150         };
33151         
33152         if(this.indicatorpos == 'left'){
33153             label.cn.unshift({
33154                 tag : 'i',
33155                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33156                 tooltip : 'This field is required'
33157             });
33158         } else {
33159             label.cn.push({
33160                 tag : 'i',
33161                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33162                 tooltip : 'This field is required'
33163             });
33164         }
33165         
33166         var items = {
33167             tag : 'div',
33168             cls : 'roo-radio-set-items'
33169         };
33170         
33171         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33172         
33173         if (align === 'left' && this.fieldLabel.length) {
33174             
33175             items = {
33176                 cls : "roo-radio-set-right", 
33177                 cn: [
33178                     items
33179                 ]
33180             };
33181             
33182             if(this.labelWidth > 12){
33183                 label.style = "width: " + this.labelWidth + 'px';
33184             }
33185             
33186             if(this.labelWidth < 13 && this.labelmd == 0){
33187                 this.labelmd = this.labelWidth;
33188             }
33189             
33190             if(this.labellg > 0){
33191                 label.cls += ' col-lg-' + this.labellg;
33192                 items.cls += ' col-lg-' + (12 - this.labellg);
33193             }
33194             
33195             if(this.labelmd > 0){
33196                 label.cls += ' col-md-' + this.labelmd;
33197                 items.cls += ' col-md-' + (12 - this.labelmd);
33198             }
33199             
33200             if(this.labelsm > 0){
33201                 label.cls += ' col-sm-' + this.labelsm;
33202                 items.cls += ' col-sm-' + (12 - this.labelsm);
33203             }
33204             
33205             if(this.labelxs > 0){
33206                 label.cls += ' col-xs-' + this.labelxs;
33207                 items.cls += ' col-xs-' + (12 - this.labelxs);
33208             }
33209         }
33210         
33211         var cfg = {
33212             tag : 'div',
33213             cls : 'roo-radio-set',
33214             cn : [
33215                 {
33216                     tag : 'input',
33217                     cls : 'roo-radio-set-input',
33218                     type : 'hidden',
33219                     name : this.name,
33220                     value : this.value ? this.value :  ''
33221                 },
33222                 label,
33223                 items
33224             ]
33225         };
33226         
33227         if(this.weight.length){
33228             cfg.cls += ' roo-radio-' + this.weight;
33229         }
33230         
33231         if(this.inline) {
33232             cfg.cls += ' roo-radio-set-inline';
33233         }
33234         
33235         return cfg;
33236         
33237     },
33238
33239     initEvents : function()
33240     {
33241         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33242         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33243         
33244         if(!this.fieldLabel.length){
33245             this.labelEl.hide();
33246         }
33247         
33248         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33249         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33250         
33251         this.indicatorEl().setVisibilityMode(Roo.Element.DISPLAY);
33252         this.indicatorEl().hide();
33253         
33254         this.originalValue = this.getValue();
33255         
33256     },
33257     
33258     inputEl: function ()
33259     {
33260         return this.el.select('.roo-radio-set-input', true).first();
33261     },
33262     
33263     getChildContainer : function()
33264     {
33265         return this.itemsEl;
33266     },
33267     
33268     register : function(item)
33269     {
33270         this.radioes.push(item);
33271         
33272     },
33273     
33274     validate : function()
33275     {   
33276         var valid = false;
33277         
33278         Roo.each(this.radioes, function(i){
33279             if(!i.checked){
33280                 return;
33281             }
33282             
33283             valid = true;
33284             return false;
33285         });
33286         
33287         if(this.allowBlank) {
33288             return true;
33289         }
33290         
33291         if(this.disabled || valid){
33292             this.markValid();
33293             return true;
33294         }
33295         
33296         this.markInvalid();
33297         return false;
33298         
33299     },
33300     
33301     markValid : function()
33302     {
33303         if(this.labelEl.isVisible(true)){
33304             this.indicatorEl().hide();
33305         }
33306         
33307         this.el.removeClass([this.invalidClass, this.validClass]);
33308         this.el.addClass(this.validClass);
33309         
33310         this.fireEvent('valid', this);
33311     },
33312     
33313     markInvalid : function(msg)
33314     {
33315         if(this.allowBlank || this.disabled){
33316             return;
33317         }
33318         
33319         if(this.labelEl.isVisible(true)){
33320             this.indicatorEl().show();
33321         }
33322         
33323         this.el.removeClass([this.invalidClass, this.validClass]);
33324         this.el.addClass(this.invalidClass);
33325         
33326         this.fireEvent('invalid', this, msg);
33327         
33328     },
33329     
33330     setValue : function(v, suppressEvent)
33331     {   
33332         this.value = v;
33333         if(this.rendered){
33334             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33335         }
33336         
33337         Roo.each(this.radioes, function(i){
33338             
33339             i.checked = false;
33340             i.el.removeClass('checked');
33341             
33342             if(i.value === v || i.value.toString() === v.toString()){
33343                 i.checked = true;
33344                 i.el.addClass('checked');
33345                 
33346                 if(suppressEvent !== true){
33347                     this.fireEvent('check', this, i);
33348                 }
33349             }
33350             
33351         }, this);
33352         
33353         this.validate();
33354     },
33355     
33356     clearInvalid : function(){
33357         
33358         if(!this.el || this.preventMark){
33359             return;
33360         }
33361         
33362         this.el.removeClass([this.invalidClass]);
33363         
33364         this.fireEvent('valid', this);
33365     }
33366     
33367 });
33368
33369 Roo.apply(Roo.bootstrap.RadioSet, {
33370     
33371     groups: {},
33372     
33373     register : function(set)
33374     {
33375         this.groups[set.name] = set;
33376     },
33377     
33378     get: function(name) 
33379     {
33380         if (typeof(this.groups[name]) == 'undefined') {
33381             return false;
33382         }
33383         
33384         return this.groups[name] ;
33385     }
33386     
33387 });
33388 /*
33389  * Based on:
33390  * Ext JS Library 1.1.1
33391  * Copyright(c) 2006-2007, Ext JS, LLC.
33392  *
33393  * Originally Released Under LGPL - original licence link has changed is not relivant.
33394  *
33395  * Fork - LGPL
33396  * <script type="text/javascript">
33397  */
33398
33399
33400 /**
33401  * @class Roo.bootstrap.SplitBar
33402  * @extends Roo.util.Observable
33403  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33404  * <br><br>
33405  * Usage:
33406  * <pre><code>
33407 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33408                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33409 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
33410 split.minSize = 100;
33411 split.maxSize = 600;
33412 split.animate = true;
33413 split.on('moved', splitterMoved);
33414 </code></pre>
33415  * @constructor
33416  * Create a new SplitBar
33417  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
33418  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
33419  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33420  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
33421                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
33422                         position of the SplitBar).
33423  */
33424 Roo.bootstrap.SplitBar = function(cfg){
33425     
33426     /** @private */
33427     
33428     //{
33429     //  dragElement : elm
33430     //  resizingElement: el,
33431         // optional..
33432     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
33433     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
33434         // existingProxy ???
33435     //}
33436     
33437     this.el = Roo.get(cfg.dragElement, true);
33438     this.el.dom.unselectable = "on";
33439     /** @private */
33440     this.resizingEl = Roo.get(cfg.resizingElement, true);
33441
33442     /**
33443      * @private
33444      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33445      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
33446      * @type Number
33447      */
33448     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
33449     
33450     /**
33451      * The minimum size of the resizing element. (Defaults to 0)
33452      * @type Number
33453      */
33454     this.minSize = 0;
33455     
33456     /**
33457      * The maximum size of the resizing element. (Defaults to 2000)
33458      * @type Number
33459      */
33460     this.maxSize = 2000;
33461     
33462     /**
33463      * Whether to animate the transition to the new size
33464      * @type Boolean
33465      */
33466     this.animate = false;
33467     
33468     /**
33469      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
33470      * @type Boolean
33471      */
33472     this.useShim = false;
33473     
33474     /** @private */
33475     this.shim = null;
33476     
33477     if(!cfg.existingProxy){
33478         /** @private */
33479         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
33480     }else{
33481         this.proxy = Roo.get(cfg.existingProxy).dom;
33482     }
33483     /** @private */
33484     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
33485     
33486     /** @private */
33487     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
33488     
33489     /** @private */
33490     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
33491     
33492     /** @private */
33493     this.dragSpecs = {};
33494     
33495     /**
33496      * @private The adapter to use to positon and resize elements
33497      */
33498     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33499     this.adapter.init(this);
33500     
33501     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33502         /** @private */
33503         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
33504         this.el.addClass("roo-splitbar-h");
33505     }else{
33506         /** @private */
33507         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
33508         this.el.addClass("roo-splitbar-v");
33509     }
33510     
33511     this.addEvents({
33512         /**
33513          * @event resize
33514          * Fires when the splitter is moved (alias for {@link #event-moved})
33515          * @param {Roo.bootstrap.SplitBar} this
33516          * @param {Number} newSize the new width or height
33517          */
33518         "resize" : true,
33519         /**
33520          * @event moved
33521          * Fires when the splitter is moved
33522          * @param {Roo.bootstrap.SplitBar} this
33523          * @param {Number} newSize the new width or height
33524          */
33525         "moved" : true,
33526         /**
33527          * @event beforeresize
33528          * Fires before the splitter is dragged
33529          * @param {Roo.bootstrap.SplitBar} this
33530          */
33531         "beforeresize" : true,
33532
33533         "beforeapply" : true
33534     });
33535
33536     Roo.util.Observable.call(this);
33537 };
33538
33539 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
33540     onStartProxyDrag : function(x, y){
33541         this.fireEvent("beforeresize", this);
33542         if(!this.overlay){
33543             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
33544             o.unselectable();
33545             o.enableDisplayMode("block");
33546             // all splitbars share the same overlay
33547             Roo.bootstrap.SplitBar.prototype.overlay = o;
33548         }
33549         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33550         this.overlay.show();
33551         Roo.get(this.proxy).setDisplayed("block");
33552         var size = this.adapter.getElementSize(this);
33553         this.activeMinSize = this.getMinimumSize();;
33554         this.activeMaxSize = this.getMaximumSize();;
33555         var c1 = size - this.activeMinSize;
33556         var c2 = Math.max(this.activeMaxSize - size, 0);
33557         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33558             this.dd.resetConstraints();
33559             this.dd.setXConstraint(
33560                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
33561                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
33562             );
33563             this.dd.setYConstraint(0, 0);
33564         }else{
33565             this.dd.resetConstraints();
33566             this.dd.setXConstraint(0, 0);
33567             this.dd.setYConstraint(
33568                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
33569                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
33570             );
33571          }
33572         this.dragSpecs.startSize = size;
33573         this.dragSpecs.startPoint = [x, y];
33574         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
33575     },
33576     
33577     /** 
33578      * @private Called after the drag operation by the DDProxy
33579      */
33580     onEndProxyDrag : function(e){
33581         Roo.get(this.proxy).setDisplayed(false);
33582         var endPoint = Roo.lib.Event.getXY(e);
33583         if(this.overlay){
33584             this.overlay.hide();
33585         }
33586         var newSize;
33587         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33588             newSize = this.dragSpecs.startSize + 
33589                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
33590                     endPoint[0] - this.dragSpecs.startPoint[0] :
33591                     this.dragSpecs.startPoint[0] - endPoint[0]
33592                 );
33593         }else{
33594             newSize = this.dragSpecs.startSize + 
33595                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
33596                     endPoint[1] - this.dragSpecs.startPoint[1] :
33597                     this.dragSpecs.startPoint[1] - endPoint[1]
33598                 );
33599         }
33600         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
33601         if(newSize != this.dragSpecs.startSize){
33602             if(this.fireEvent('beforeapply', this, newSize) !== false){
33603                 this.adapter.setElementSize(this, newSize);
33604                 this.fireEvent("moved", this, newSize);
33605                 this.fireEvent("resize", this, newSize);
33606             }
33607         }
33608     },
33609     
33610     /**
33611      * Get the adapter this SplitBar uses
33612      * @return The adapter object
33613      */
33614     getAdapter : function(){
33615         return this.adapter;
33616     },
33617     
33618     /**
33619      * Set the adapter this SplitBar uses
33620      * @param {Object} adapter A SplitBar adapter object
33621      */
33622     setAdapter : function(adapter){
33623         this.adapter = adapter;
33624         this.adapter.init(this);
33625     },
33626     
33627     /**
33628      * Gets the minimum size for the resizing element
33629      * @return {Number} The minimum size
33630      */
33631     getMinimumSize : function(){
33632         return this.minSize;
33633     },
33634     
33635     /**
33636      * Sets the minimum size for the resizing element
33637      * @param {Number} minSize The minimum size
33638      */
33639     setMinimumSize : function(minSize){
33640         this.minSize = minSize;
33641     },
33642     
33643     /**
33644      * Gets the maximum size for the resizing element
33645      * @return {Number} The maximum size
33646      */
33647     getMaximumSize : function(){
33648         return this.maxSize;
33649     },
33650     
33651     /**
33652      * Sets the maximum size for the resizing element
33653      * @param {Number} maxSize The maximum size
33654      */
33655     setMaximumSize : function(maxSize){
33656         this.maxSize = maxSize;
33657     },
33658     
33659     /**
33660      * Sets the initialize size for the resizing element
33661      * @param {Number} size The initial size
33662      */
33663     setCurrentSize : function(size){
33664         var oldAnimate = this.animate;
33665         this.animate = false;
33666         this.adapter.setElementSize(this, size);
33667         this.animate = oldAnimate;
33668     },
33669     
33670     /**
33671      * Destroy this splitbar. 
33672      * @param {Boolean} removeEl True to remove the element
33673      */
33674     destroy : function(removeEl){
33675         if(this.shim){
33676             this.shim.remove();
33677         }
33678         this.dd.unreg();
33679         this.proxy.parentNode.removeChild(this.proxy);
33680         if(removeEl){
33681             this.el.remove();
33682         }
33683     }
33684 });
33685
33686 /**
33687  * @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.
33688  */
33689 Roo.bootstrap.SplitBar.createProxy = function(dir){
33690     var proxy = new Roo.Element(document.createElement("div"));
33691     proxy.unselectable();
33692     var cls = 'roo-splitbar-proxy';
33693     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
33694     document.body.appendChild(proxy.dom);
33695     return proxy.dom;
33696 };
33697
33698 /** 
33699  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
33700  * Default Adapter. It assumes the splitter and resizing element are not positioned
33701  * elements and only gets/sets the width of the element. Generally used for table based layouts.
33702  */
33703 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
33704 };
33705
33706 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
33707     // do nothing for now
33708     init : function(s){
33709     
33710     },
33711     /**
33712      * Called before drag operations to get the current size of the resizing element. 
33713      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33714      */
33715      getElementSize : function(s){
33716         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33717             return s.resizingEl.getWidth();
33718         }else{
33719             return s.resizingEl.getHeight();
33720         }
33721     },
33722     
33723     /**
33724      * Called after drag operations to set the size of the resizing element.
33725      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33726      * @param {Number} newSize The new size to set
33727      * @param {Function} onComplete A function to be invoked when resizing is complete
33728      */
33729     setElementSize : function(s, newSize, onComplete){
33730         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33731             if(!s.animate){
33732                 s.resizingEl.setWidth(newSize);
33733                 if(onComplete){
33734                     onComplete(s, newSize);
33735                 }
33736             }else{
33737                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
33738             }
33739         }else{
33740             
33741             if(!s.animate){
33742                 s.resizingEl.setHeight(newSize);
33743                 if(onComplete){
33744                     onComplete(s, newSize);
33745                 }
33746             }else{
33747                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
33748             }
33749         }
33750     }
33751 };
33752
33753 /** 
33754  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
33755  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
33756  * Adapter that  moves the splitter element to align with the resized sizing element. 
33757  * Used with an absolute positioned SplitBar.
33758  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
33759  * document.body, make sure you assign an id to the body element.
33760  */
33761 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
33762     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33763     this.container = Roo.get(container);
33764 };
33765
33766 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
33767     init : function(s){
33768         this.basic.init(s);
33769     },
33770     
33771     getElementSize : function(s){
33772         return this.basic.getElementSize(s);
33773     },
33774     
33775     setElementSize : function(s, newSize, onComplete){
33776         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
33777     },
33778     
33779     moveSplitter : function(s){
33780         var yes = Roo.bootstrap.SplitBar;
33781         switch(s.placement){
33782             case yes.LEFT:
33783                 s.el.setX(s.resizingEl.getRight());
33784                 break;
33785             case yes.RIGHT:
33786                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
33787                 break;
33788             case yes.TOP:
33789                 s.el.setY(s.resizingEl.getBottom());
33790                 break;
33791             case yes.BOTTOM:
33792                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
33793                 break;
33794         }
33795     }
33796 };
33797
33798 /**
33799  * Orientation constant - Create a vertical SplitBar
33800  * @static
33801  * @type Number
33802  */
33803 Roo.bootstrap.SplitBar.VERTICAL = 1;
33804
33805 /**
33806  * Orientation constant - Create a horizontal SplitBar
33807  * @static
33808  * @type Number
33809  */
33810 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
33811
33812 /**
33813  * Placement constant - The resizing element is to the left of the splitter element
33814  * @static
33815  * @type Number
33816  */
33817 Roo.bootstrap.SplitBar.LEFT = 1;
33818
33819 /**
33820  * Placement constant - The resizing element is to the right of the splitter element
33821  * @static
33822  * @type Number
33823  */
33824 Roo.bootstrap.SplitBar.RIGHT = 2;
33825
33826 /**
33827  * Placement constant - The resizing element is positioned above the splitter element
33828  * @static
33829  * @type Number
33830  */
33831 Roo.bootstrap.SplitBar.TOP = 3;
33832
33833 /**
33834  * Placement constant - The resizing element is positioned under splitter element
33835  * @static
33836  * @type Number
33837  */
33838 Roo.bootstrap.SplitBar.BOTTOM = 4;
33839 Roo.namespace("Roo.bootstrap.layout");/*
33840  * Based on:
33841  * Ext JS Library 1.1.1
33842  * Copyright(c) 2006-2007, Ext JS, LLC.
33843  *
33844  * Originally Released Under LGPL - original licence link has changed is not relivant.
33845  *
33846  * Fork - LGPL
33847  * <script type="text/javascript">
33848  */
33849
33850 /**
33851  * @class Roo.bootstrap.layout.Manager
33852  * @extends Roo.bootstrap.Component
33853  * Base class for layout managers.
33854  */
33855 Roo.bootstrap.layout.Manager = function(config)
33856 {
33857     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
33858
33859
33860
33861
33862
33863     /** false to disable window resize monitoring @type Boolean */
33864     this.monitorWindowResize = true;
33865     this.regions = {};
33866     this.addEvents({
33867         /**
33868          * @event layout
33869          * Fires when a layout is performed.
33870          * @param {Roo.LayoutManager} this
33871          */
33872         "layout" : true,
33873         /**
33874          * @event regionresized
33875          * Fires when the user resizes a region.
33876          * @param {Roo.LayoutRegion} region The resized region
33877          * @param {Number} newSize The new size (width for east/west, height for north/south)
33878          */
33879         "regionresized" : true,
33880         /**
33881          * @event regioncollapsed
33882          * Fires when a region is collapsed.
33883          * @param {Roo.LayoutRegion} region The collapsed region
33884          */
33885         "regioncollapsed" : true,
33886         /**
33887          * @event regionexpanded
33888          * Fires when a region is expanded.
33889          * @param {Roo.LayoutRegion} region The expanded region
33890          */
33891         "regionexpanded" : true
33892     });
33893     this.updating = false;
33894
33895     if (config.el) {
33896         this.el = Roo.get(config.el);
33897         this.initEvents();
33898     }
33899
33900 };
33901
33902 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
33903
33904
33905     regions : null,
33906
33907     monitorWindowResize : true,
33908
33909
33910     updating : false,
33911
33912
33913     onRender : function(ct, position)
33914     {
33915         if(!this.el){
33916             this.el = Roo.get(ct);
33917             this.initEvents();
33918         }
33919         //this.fireEvent('render',this);
33920     },
33921
33922
33923     initEvents: function()
33924     {
33925
33926
33927         // ie scrollbar fix
33928         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
33929             document.body.scroll = "no";
33930         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
33931             this.el.position('relative');
33932         }
33933         this.id = this.el.id;
33934         this.el.addClass("roo-layout-container");
33935         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
33936         if(this.el.dom != document.body ) {
33937             this.el.on('resize', this.layout,this);
33938             this.el.on('show', this.layout,this);
33939         }
33940
33941     },
33942
33943     /**
33944      * Returns true if this layout is currently being updated
33945      * @return {Boolean}
33946      */
33947     isUpdating : function(){
33948         return this.updating;
33949     },
33950
33951     /**
33952      * Suspend the LayoutManager from doing auto-layouts while
33953      * making multiple add or remove calls
33954      */
33955     beginUpdate : function(){
33956         this.updating = true;
33957     },
33958
33959     /**
33960      * Restore auto-layouts and optionally disable the manager from performing a layout
33961      * @param {Boolean} noLayout true to disable a layout update
33962      */
33963     endUpdate : function(noLayout){
33964         this.updating = false;
33965         if(!noLayout){
33966             this.layout();
33967         }
33968     },
33969
33970     layout: function(){
33971         // abstract...
33972     },
33973
33974     onRegionResized : function(region, newSize){
33975         this.fireEvent("regionresized", region, newSize);
33976         this.layout();
33977     },
33978
33979     onRegionCollapsed : function(region){
33980         this.fireEvent("regioncollapsed", region);
33981     },
33982
33983     onRegionExpanded : function(region){
33984         this.fireEvent("regionexpanded", region);
33985     },
33986
33987     /**
33988      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
33989      * performs box-model adjustments.
33990      * @return {Object} The size as an object {width: (the width), height: (the height)}
33991      */
33992     getViewSize : function()
33993     {
33994         var size;
33995         if(this.el.dom != document.body){
33996             size = this.el.getSize();
33997         }else{
33998             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
33999         }
34000         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34001         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34002         return size;
34003     },
34004
34005     /**
34006      * Returns the Element this layout is bound to.
34007      * @return {Roo.Element}
34008      */
34009     getEl : function(){
34010         return this.el;
34011     },
34012
34013     /**
34014      * Returns the specified region.
34015      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34016      * @return {Roo.LayoutRegion}
34017      */
34018     getRegion : function(target){
34019         return this.regions[target.toLowerCase()];
34020     },
34021
34022     onWindowResize : function(){
34023         if(this.monitorWindowResize){
34024             this.layout();
34025         }
34026     }
34027 });
34028 /*
34029  * Based on:
34030  * Ext JS Library 1.1.1
34031  * Copyright(c) 2006-2007, Ext JS, LLC.
34032  *
34033  * Originally Released Under LGPL - original licence link has changed is not relivant.
34034  *
34035  * Fork - LGPL
34036  * <script type="text/javascript">
34037  */
34038 /**
34039  * @class Roo.bootstrap.layout.Border
34040  * @extends Roo.bootstrap.layout.Manager
34041  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34042  * please see: examples/bootstrap/nested.html<br><br>
34043  
34044 <b>The container the layout is rendered into can be either the body element or any other element.
34045 If it is not the body element, the container needs to either be an absolute positioned element,
34046 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34047 the container size if it is not the body element.</b>
34048
34049 * @constructor
34050 * Create a new Border
34051 * @param {Object} config Configuration options
34052  */
34053 Roo.bootstrap.layout.Border = function(config){
34054     config = config || {};
34055     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34056     
34057     
34058     
34059     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34060         if(config[region]){
34061             config[region].region = region;
34062             this.addRegion(config[region]);
34063         }
34064     },this);
34065     
34066 };
34067
34068 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34069
34070 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34071     /**
34072      * Creates and adds a new region if it doesn't already exist.
34073      * @param {String} target The target region key (north, south, east, west or center).
34074      * @param {Object} config The regions config object
34075      * @return {BorderLayoutRegion} The new region
34076      */
34077     addRegion : function(config)
34078     {
34079         if(!this.regions[config.region]){
34080             var r = this.factory(config);
34081             this.bindRegion(r);
34082         }
34083         return this.regions[config.region];
34084     },
34085
34086     // private (kinda)
34087     bindRegion : function(r){
34088         this.regions[r.config.region] = r;
34089         
34090         r.on("visibilitychange",    this.layout, this);
34091         r.on("paneladded",          this.layout, this);
34092         r.on("panelremoved",        this.layout, this);
34093         r.on("invalidated",         this.layout, this);
34094         r.on("resized",             this.onRegionResized, this);
34095         r.on("collapsed",           this.onRegionCollapsed, this);
34096         r.on("expanded",            this.onRegionExpanded, this);
34097     },
34098
34099     /**
34100      * Performs a layout update.
34101      */
34102     layout : function()
34103     {
34104         if(this.updating) {
34105             return;
34106         }
34107         
34108         // render all the rebions if they have not been done alreayd?
34109         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34110             if(this.regions[region] && !this.regions[region].bodyEl){
34111                 this.regions[region].onRender(this.el)
34112             }
34113         },this);
34114         
34115         var size = this.getViewSize();
34116         var w = size.width;
34117         var h = size.height;
34118         var centerW = w;
34119         var centerH = h;
34120         var centerY = 0;
34121         var centerX = 0;
34122         //var x = 0, y = 0;
34123
34124         var rs = this.regions;
34125         var north = rs["north"];
34126         var south = rs["south"]; 
34127         var west = rs["west"];
34128         var east = rs["east"];
34129         var center = rs["center"];
34130         //if(this.hideOnLayout){ // not supported anymore
34131             //c.el.setStyle("display", "none");
34132         //}
34133         if(north && north.isVisible()){
34134             var b = north.getBox();
34135             var m = north.getMargins();
34136             b.width = w - (m.left+m.right);
34137             b.x = m.left;
34138             b.y = m.top;
34139             centerY = b.height + b.y + m.bottom;
34140             centerH -= centerY;
34141             north.updateBox(this.safeBox(b));
34142         }
34143         if(south && south.isVisible()){
34144             var b = south.getBox();
34145             var m = south.getMargins();
34146             b.width = w - (m.left+m.right);
34147             b.x = m.left;
34148             var totalHeight = (b.height + m.top + m.bottom);
34149             b.y = h - totalHeight + m.top;
34150             centerH -= totalHeight;
34151             south.updateBox(this.safeBox(b));
34152         }
34153         if(west && west.isVisible()){
34154             var b = west.getBox();
34155             var m = west.getMargins();
34156             b.height = centerH - (m.top+m.bottom);
34157             b.x = m.left;
34158             b.y = centerY + m.top;
34159             var totalWidth = (b.width + m.left + m.right);
34160             centerX += totalWidth;
34161             centerW -= totalWidth;
34162             west.updateBox(this.safeBox(b));
34163         }
34164         if(east && east.isVisible()){
34165             var b = east.getBox();
34166             var m = east.getMargins();
34167             b.height = centerH - (m.top+m.bottom);
34168             var totalWidth = (b.width + m.left + m.right);
34169             b.x = w - totalWidth + m.left;
34170             b.y = centerY + m.top;
34171             centerW -= totalWidth;
34172             east.updateBox(this.safeBox(b));
34173         }
34174         if(center){
34175             var m = center.getMargins();
34176             var centerBox = {
34177                 x: centerX + m.left,
34178                 y: centerY + m.top,
34179                 width: centerW - (m.left+m.right),
34180                 height: centerH - (m.top+m.bottom)
34181             };
34182             //if(this.hideOnLayout){
34183                 //center.el.setStyle("display", "block");
34184             //}
34185             center.updateBox(this.safeBox(centerBox));
34186         }
34187         this.el.repaint();
34188         this.fireEvent("layout", this);
34189     },
34190
34191     // private
34192     safeBox : function(box){
34193         box.width = Math.max(0, box.width);
34194         box.height = Math.max(0, box.height);
34195         return box;
34196     },
34197
34198     /**
34199      * Adds a ContentPanel (or subclass) to this layout.
34200      * @param {String} target The target region key (north, south, east, west or center).
34201      * @param {Roo.ContentPanel} panel The panel to add
34202      * @return {Roo.ContentPanel} The added panel
34203      */
34204     add : function(target, panel){
34205          
34206         target = target.toLowerCase();
34207         return this.regions[target].add(panel);
34208     },
34209
34210     /**
34211      * Remove a ContentPanel (or subclass) to this layout.
34212      * @param {String} target The target region key (north, south, east, west or center).
34213      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34214      * @return {Roo.ContentPanel} The removed panel
34215      */
34216     remove : function(target, panel){
34217         target = target.toLowerCase();
34218         return this.regions[target].remove(panel);
34219     },
34220
34221     /**
34222      * Searches all regions for a panel with the specified id
34223      * @param {String} panelId
34224      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34225      */
34226     findPanel : function(panelId){
34227         var rs = this.regions;
34228         for(var target in rs){
34229             if(typeof rs[target] != "function"){
34230                 var p = rs[target].getPanel(panelId);
34231                 if(p){
34232                     return p;
34233                 }
34234             }
34235         }
34236         return null;
34237     },
34238
34239     /**
34240      * Searches all regions for a panel with the specified id and activates (shows) it.
34241      * @param {String/ContentPanel} panelId The panels id or the panel itself
34242      * @return {Roo.ContentPanel} The shown panel or null
34243      */
34244     showPanel : function(panelId) {
34245       var rs = this.regions;
34246       for(var target in rs){
34247          var r = rs[target];
34248          if(typeof r != "function"){
34249             if(r.hasPanel(panelId)){
34250                return r.showPanel(panelId);
34251             }
34252          }
34253       }
34254       return null;
34255    },
34256
34257    /**
34258      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34259      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34260      */
34261    /*
34262     restoreState : function(provider){
34263         if(!provider){
34264             provider = Roo.state.Manager;
34265         }
34266         var sm = new Roo.LayoutStateManager();
34267         sm.init(this, provider);
34268     },
34269 */
34270  
34271  
34272     /**
34273      * Adds a xtype elements to the layout.
34274      * <pre><code>
34275
34276 layout.addxtype({
34277        xtype : 'ContentPanel',
34278        region: 'west',
34279        items: [ .... ]
34280    }
34281 );
34282
34283 layout.addxtype({
34284         xtype : 'NestedLayoutPanel',
34285         region: 'west',
34286         layout: {
34287            center: { },
34288            west: { }   
34289         },
34290         items : [ ... list of content panels or nested layout panels.. ]
34291    }
34292 );
34293 </code></pre>
34294      * @param {Object} cfg Xtype definition of item to add.
34295      */
34296     addxtype : function(cfg)
34297     {
34298         // basically accepts a pannel...
34299         // can accept a layout region..!?!?
34300         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34301         
34302         
34303         // theory?  children can only be panels??
34304         
34305         //if (!cfg.xtype.match(/Panel$/)) {
34306         //    return false;
34307         //}
34308         var ret = false;
34309         
34310         if (typeof(cfg.region) == 'undefined') {
34311             Roo.log("Failed to add Panel, region was not set");
34312             Roo.log(cfg);
34313             return false;
34314         }
34315         var region = cfg.region;
34316         delete cfg.region;
34317         
34318           
34319         var xitems = [];
34320         if (cfg.items) {
34321             xitems = cfg.items;
34322             delete cfg.items;
34323         }
34324         var nb = false;
34325         
34326         switch(cfg.xtype) 
34327         {
34328             case 'Content':  // ContentPanel (el, cfg)
34329             case 'Scroll':  // ContentPanel (el, cfg)
34330             case 'View': 
34331                 cfg.autoCreate = true;
34332                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34333                 //} else {
34334                 //    var el = this.el.createChild();
34335                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34336                 //}
34337                 
34338                 this.add(region, ret);
34339                 break;
34340             
34341             /*
34342             case 'TreePanel': // our new panel!
34343                 cfg.el = this.el.createChild();
34344                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34345                 this.add(region, ret);
34346                 break;
34347             */
34348             
34349             case 'Nest': 
34350                 // create a new Layout (which is  a Border Layout...
34351                 
34352                 var clayout = cfg.layout;
34353                 clayout.el  = this.el.createChild();
34354                 clayout.items   = clayout.items  || [];
34355                 
34356                 delete cfg.layout;
34357                 
34358                 // replace this exitems with the clayout ones..
34359                 xitems = clayout.items;
34360                  
34361                 // force background off if it's in center...
34362                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34363                     cfg.background = false;
34364                 }
34365                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
34366                 
34367                 
34368                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34369                 //console.log('adding nested layout panel '  + cfg.toSource());
34370                 this.add(region, ret);
34371                 nb = {}; /// find first...
34372                 break;
34373             
34374             case 'Grid':
34375                 
34376                 // needs grid and region
34377                 
34378                 //var el = this.getRegion(region).el.createChild();
34379                 /*
34380                  *var el = this.el.createChild();
34381                 // create the grid first...
34382                 cfg.grid.container = el;
34383                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34384                 */
34385                 
34386                 if (region == 'center' && this.active ) {
34387                     cfg.background = false;
34388                 }
34389                 
34390                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34391                 
34392                 this.add(region, ret);
34393                 /*
34394                 if (cfg.background) {
34395                     // render grid on panel activation (if panel background)
34396                     ret.on('activate', function(gp) {
34397                         if (!gp.grid.rendered) {
34398                     //        gp.grid.render(el);
34399                         }
34400                     });
34401                 } else {
34402                   //  cfg.grid.render(el);
34403                 }
34404                 */
34405                 break;
34406            
34407            
34408             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34409                 // it was the old xcomponent building that caused this before.
34410                 // espeically if border is the top element in the tree.
34411                 ret = this;
34412                 break; 
34413                 
34414                     
34415                 
34416                 
34417                 
34418             default:
34419                 /*
34420                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34421                     
34422                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34423                     this.add(region, ret);
34424                 } else {
34425                 */
34426                     Roo.log(cfg);
34427                     throw "Can not add '" + cfg.xtype + "' to Border";
34428                     return null;
34429              
34430                                 
34431              
34432         }
34433         this.beginUpdate();
34434         // add children..
34435         var region = '';
34436         var abn = {};
34437         Roo.each(xitems, function(i)  {
34438             region = nb && i.region ? i.region : false;
34439             
34440             var add = ret.addxtype(i);
34441            
34442             if (region) {
34443                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
34444                 if (!i.background) {
34445                     abn[region] = nb[region] ;
34446                 }
34447             }
34448             
34449         });
34450         this.endUpdate();
34451
34452         // make the last non-background panel active..
34453         //if (nb) { Roo.log(abn); }
34454         if (nb) {
34455             
34456             for(var r in abn) {
34457                 region = this.getRegion(r);
34458                 if (region) {
34459                     // tried using nb[r], but it does not work..
34460                      
34461                     region.showPanel(abn[r]);
34462                    
34463                 }
34464             }
34465         }
34466         return ret;
34467         
34468     },
34469     
34470     
34471 // private
34472     factory : function(cfg)
34473     {
34474         
34475         var validRegions = Roo.bootstrap.layout.Border.regions;
34476
34477         var target = cfg.region;
34478         cfg.mgr = this;
34479         
34480         var r = Roo.bootstrap.layout;
34481         Roo.log(target);
34482         switch(target){
34483             case "north":
34484                 return new r.North(cfg);
34485             case "south":
34486                 return new r.South(cfg);
34487             case "east":
34488                 return new r.East(cfg);
34489             case "west":
34490                 return new r.West(cfg);
34491             case "center":
34492                 return new r.Center(cfg);
34493         }
34494         throw 'Layout region "'+target+'" not supported.';
34495     }
34496     
34497     
34498 });
34499  /*
34500  * Based on:
34501  * Ext JS Library 1.1.1
34502  * Copyright(c) 2006-2007, Ext JS, LLC.
34503  *
34504  * Originally Released Under LGPL - original licence link has changed is not relivant.
34505  *
34506  * Fork - LGPL
34507  * <script type="text/javascript">
34508  */
34509  
34510 /**
34511  * @class Roo.bootstrap.layout.Basic
34512  * @extends Roo.util.Observable
34513  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
34514  * and does not have a titlebar, tabs or any other features. All it does is size and position 
34515  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
34516  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34517  * @cfg {string}   region  the region that it inhabits..
34518  * @cfg {bool}   skipConfig skip config?
34519  * 
34520
34521  */
34522 Roo.bootstrap.layout.Basic = function(config){
34523     
34524     this.mgr = config.mgr;
34525     
34526     this.position = config.region;
34527     
34528     var skipConfig = config.skipConfig;
34529     
34530     this.events = {
34531         /**
34532          * @scope Roo.BasicLayoutRegion
34533          */
34534         
34535         /**
34536          * @event beforeremove
34537          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
34538          * @param {Roo.LayoutRegion} this
34539          * @param {Roo.ContentPanel} panel The panel
34540          * @param {Object} e The cancel event object
34541          */
34542         "beforeremove" : true,
34543         /**
34544          * @event invalidated
34545          * Fires when the layout for this region is changed.
34546          * @param {Roo.LayoutRegion} this
34547          */
34548         "invalidated" : true,
34549         /**
34550          * @event visibilitychange
34551          * Fires when this region is shown or hidden 
34552          * @param {Roo.LayoutRegion} this
34553          * @param {Boolean} visibility true or false
34554          */
34555         "visibilitychange" : true,
34556         /**
34557          * @event paneladded
34558          * Fires when a panel is added. 
34559          * @param {Roo.LayoutRegion} this
34560          * @param {Roo.ContentPanel} panel The panel
34561          */
34562         "paneladded" : true,
34563         /**
34564          * @event panelremoved
34565          * Fires when a panel is removed. 
34566          * @param {Roo.LayoutRegion} this
34567          * @param {Roo.ContentPanel} panel The panel
34568          */
34569         "panelremoved" : true,
34570         /**
34571          * @event beforecollapse
34572          * Fires when this region before collapse.
34573          * @param {Roo.LayoutRegion} this
34574          */
34575         "beforecollapse" : true,
34576         /**
34577          * @event collapsed
34578          * Fires when this region is collapsed.
34579          * @param {Roo.LayoutRegion} this
34580          */
34581         "collapsed" : true,
34582         /**
34583          * @event expanded
34584          * Fires when this region is expanded.
34585          * @param {Roo.LayoutRegion} this
34586          */
34587         "expanded" : true,
34588         /**
34589          * @event slideshow
34590          * Fires when this region is slid into view.
34591          * @param {Roo.LayoutRegion} this
34592          */
34593         "slideshow" : true,
34594         /**
34595          * @event slidehide
34596          * Fires when this region slides out of view. 
34597          * @param {Roo.LayoutRegion} this
34598          */
34599         "slidehide" : true,
34600         /**
34601          * @event panelactivated
34602          * Fires when a panel is activated. 
34603          * @param {Roo.LayoutRegion} this
34604          * @param {Roo.ContentPanel} panel The activated panel
34605          */
34606         "panelactivated" : true,
34607         /**
34608          * @event resized
34609          * Fires when the user resizes this region. 
34610          * @param {Roo.LayoutRegion} this
34611          * @param {Number} newSize The new size (width for east/west, height for north/south)
34612          */
34613         "resized" : true
34614     };
34615     /** A collection of panels in this region. @type Roo.util.MixedCollection */
34616     this.panels = new Roo.util.MixedCollection();
34617     this.panels.getKey = this.getPanelId.createDelegate(this);
34618     this.box = null;
34619     this.activePanel = null;
34620     // ensure listeners are added...
34621     
34622     if (config.listeners || config.events) {
34623         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
34624             listeners : config.listeners || {},
34625             events : config.events || {}
34626         });
34627     }
34628     
34629     if(skipConfig !== true){
34630         this.applyConfig(config);
34631     }
34632 };
34633
34634 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
34635 {
34636     getPanelId : function(p){
34637         return p.getId();
34638     },
34639     
34640     applyConfig : function(config){
34641         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34642         this.config = config;
34643         
34644     },
34645     
34646     /**
34647      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
34648      * the width, for horizontal (north, south) the height.
34649      * @param {Number} newSize The new width or height
34650      */
34651     resizeTo : function(newSize){
34652         var el = this.el ? this.el :
34653                  (this.activePanel ? this.activePanel.getEl() : null);
34654         if(el){
34655             switch(this.position){
34656                 case "east":
34657                 case "west":
34658                     el.setWidth(newSize);
34659                     this.fireEvent("resized", this, newSize);
34660                 break;
34661                 case "north":
34662                 case "south":
34663                     el.setHeight(newSize);
34664                     this.fireEvent("resized", this, newSize);
34665                 break;                
34666             }
34667         }
34668     },
34669     
34670     getBox : function(){
34671         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
34672     },
34673     
34674     getMargins : function(){
34675         return this.margins;
34676     },
34677     
34678     updateBox : function(box){
34679         this.box = box;
34680         var el = this.activePanel.getEl();
34681         el.dom.style.left = box.x + "px";
34682         el.dom.style.top = box.y + "px";
34683         this.activePanel.setSize(box.width, box.height);
34684     },
34685     
34686     /**
34687      * Returns the container element for this region.
34688      * @return {Roo.Element}
34689      */
34690     getEl : function(){
34691         return this.activePanel;
34692     },
34693     
34694     /**
34695      * Returns true if this region is currently visible.
34696      * @return {Boolean}
34697      */
34698     isVisible : function(){
34699         return this.activePanel ? true : false;
34700     },
34701     
34702     setActivePanel : function(panel){
34703         panel = this.getPanel(panel);
34704         if(this.activePanel && this.activePanel != panel){
34705             this.activePanel.setActiveState(false);
34706             this.activePanel.getEl().setLeftTop(-10000,-10000);
34707         }
34708         this.activePanel = panel;
34709         panel.setActiveState(true);
34710         if(this.box){
34711             panel.setSize(this.box.width, this.box.height);
34712         }
34713         this.fireEvent("panelactivated", this, panel);
34714         this.fireEvent("invalidated");
34715     },
34716     
34717     /**
34718      * Show the specified panel.
34719      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
34720      * @return {Roo.ContentPanel} The shown panel or null
34721      */
34722     showPanel : function(panel){
34723         panel = this.getPanel(panel);
34724         if(panel){
34725             this.setActivePanel(panel);
34726         }
34727         return panel;
34728     },
34729     
34730     /**
34731      * Get the active panel for this region.
34732      * @return {Roo.ContentPanel} The active panel or null
34733      */
34734     getActivePanel : function(){
34735         return this.activePanel;
34736     },
34737     
34738     /**
34739      * Add the passed ContentPanel(s)
34740      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34741      * @return {Roo.ContentPanel} The panel added (if only one was added)
34742      */
34743     add : function(panel){
34744         if(arguments.length > 1){
34745             for(var i = 0, len = arguments.length; i < len; i++) {
34746                 this.add(arguments[i]);
34747             }
34748             return null;
34749         }
34750         if(this.hasPanel(panel)){
34751             this.showPanel(panel);
34752             return panel;
34753         }
34754         var el = panel.getEl();
34755         if(el.dom.parentNode != this.mgr.el.dom){
34756             this.mgr.el.dom.appendChild(el.dom);
34757         }
34758         if(panel.setRegion){
34759             panel.setRegion(this);
34760         }
34761         this.panels.add(panel);
34762         el.setStyle("position", "absolute");
34763         if(!panel.background){
34764             this.setActivePanel(panel);
34765             if(this.config.initialSize && this.panels.getCount()==1){
34766                 this.resizeTo(this.config.initialSize);
34767             }
34768         }
34769         this.fireEvent("paneladded", this, panel);
34770         return panel;
34771     },
34772     
34773     /**
34774      * Returns true if the panel is in this region.
34775      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34776      * @return {Boolean}
34777      */
34778     hasPanel : function(panel){
34779         if(typeof panel == "object"){ // must be panel obj
34780             panel = panel.getId();
34781         }
34782         return this.getPanel(panel) ? true : false;
34783     },
34784     
34785     /**
34786      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34787      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34788      * @param {Boolean} preservePanel Overrides the config preservePanel option
34789      * @return {Roo.ContentPanel} The panel that was removed
34790      */
34791     remove : function(panel, preservePanel){
34792         panel = this.getPanel(panel);
34793         if(!panel){
34794             return null;
34795         }
34796         var e = {};
34797         this.fireEvent("beforeremove", this, panel, e);
34798         if(e.cancel === true){
34799             return null;
34800         }
34801         var panelId = panel.getId();
34802         this.panels.removeKey(panelId);
34803         return panel;
34804     },
34805     
34806     /**
34807      * Returns the panel specified or null if it's not in this region.
34808      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34809      * @return {Roo.ContentPanel}
34810      */
34811     getPanel : function(id){
34812         if(typeof id == "object"){ // must be panel obj
34813             return id;
34814         }
34815         return this.panels.get(id);
34816     },
34817     
34818     /**
34819      * Returns this regions position (north/south/east/west/center).
34820      * @return {String} 
34821      */
34822     getPosition: function(){
34823         return this.position;    
34824     }
34825 });/*
34826  * Based on:
34827  * Ext JS Library 1.1.1
34828  * Copyright(c) 2006-2007, Ext JS, LLC.
34829  *
34830  * Originally Released Under LGPL - original licence link has changed is not relivant.
34831  *
34832  * Fork - LGPL
34833  * <script type="text/javascript">
34834  */
34835  
34836 /**
34837  * @class Roo.bootstrap.layout.Region
34838  * @extends Roo.bootstrap.layout.Basic
34839  * This class represents a region in a layout manager.
34840  
34841  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
34842  * @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})
34843  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
34844  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
34845  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
34846  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
34847  * @cfg {String}    title           The title for the region (overrides panel titles)
34848  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
34849  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
34850  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
34851  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
34852  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
34853  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
34854  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
34855  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
34856  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
34857  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
34858
34859  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
34860  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
34861  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
34862  * @cfg {Number}    width           For East/West panels
34863  * @cfg {Number}    height          For North/South panels
34864  * @cfg {Boolean}   split           To show the splitter
34865  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
34866  * 
34867  * @cfg {string}   cls             Extra CSS classes to add to region
34868  * 
34869  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34870  * @cfg {string}   region  the region that it inhabits..
34871  *
34872
34873  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
34874  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
34875
34876  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
34877  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
34878  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
34879  */
34880 Roo.bootstrap.layout.Region = function(config)
34881 {
34882     this.applyConfig(config);
34883
34884     var mgr = config.mgr;
34885     var pos = config.region;
34886     config.skipConfig = true;
34887     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
34888     
34889     if (mgr.el) {
34890         this.onRender(mgr.el);   
34891     }
34892      
34893     this.visible = true;
34894     this.collapsed = false;
34895     this.unrendered_panels = [];
34896 };
34897
34898 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
34899
34900     position: '', // set by wrapper (eg. north/south etc..)
34901     unrendered_panels : null,  // unrendered panels.
34902     createBody : function(){
34903         /** This region's body element 
34904         * @type Roo.Element */
34905         this.bodyEl = this.el.createChild({
34906                 tag: "div",
34907                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
34908         });
34909     },
34910
34911     onRender: function(ctr, pos)
34912     {
34913         var dh = Roo.DomHelper;
34914         /** This region's container element 
34915         * @type Roo.Element */
34916         this.el = dh.append(ctr.dom, {
34917                 tag: "div",
34918                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
34919             }, true);
34920         /** This region's title element 
34921         * @type Roo.Element */
34922     
34923         this.titleEl = dh.append(this.el.dom,
34924             {
34925                     tag: "div",
34926                     unselectable: "on",
34927                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
34928                     children:[
34929                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
34930                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
34931                     ]}, true);
34932         
34933         this.titleEl.enableDisplayMode();
34934         /** This region's title text element 
34935         * @type HTMLElement */
34936         this.titleTextEl = this.titleEl.dom.firstChild;
34937         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
34938         /*
34939         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
34940         this.closeBtn.enableDisplayMode();
34941         this.closeBtn.on("click", this.closeClicked, this);
34942         this.closeBtn.hide();
34943     */
34944         this.createBody(this.config);
34945         if(this.config.hideWhenEmpty){
34946             this.hide();
34947             this.on("paneladded", this.validateVisibility, this);
34948             this.on("panelremoved", this.validateVisibility, this);
34949         }
34950         if(this.autoScroll){
34951             this.bodyEl.setStyle("overflow", "auto");
34952         }else{
34953             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
34954         }
34955         //if(c.titlebar !== false){
34956             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
34957                 this.titleEl.hide();
34958             }else{
34959                 this.titleEl.show();
34960                 if(this.config.title){
34961                     this.titleTextEl.innerHTML = this.config.title;
34962                 }
34963             }
34964         //}
34965         if(this.config.collapsed){
34966             this.collapse(true);
34967         }
34968         if(this.config.hidden){
34969             this.hide();
34970         }
34971         
34972         if (this.unrendered_panels && this.unrendered_panels.length) {
34973             for (var i =0;i< this.unrendered_panels.length; i++) {
34974                 this.add(this.unrendered_panels[i]);
34975             }
34976             this.unrendered_panels = null;
34977             
34978         }
34979         
34980     },
34981     
34982     applyConfig : function(c)
34983     {
34984         /*
34985          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
34986             var dh = Roo.DomHelper;
34987             if(c.titlebar !== false){
34988                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
34989                 this.collapseBtn.on("click", this.collapse, this);
34990                 this.collapseBtn.enableDisplayMode();
34991                 /*
34992                 if(c.showPin === true || this.showPin){
34993                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
34994                     this.stickBtn.enableDisplayMode();
34995                     this.stickBtn.on("click", this.expand, this);
34996                     this.stickBtn.hide();
34997                 }
34998                 
34999             }
35000             */
35001             /** This region's collapsed element
35002             * @type Roo.Element */
35003             /*
35004              *
35005             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35006                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35007             ]}, true);
35008             
35009             if(c.floatable !== false){
35010                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35011                this.collapsedEl.on("click", this.collapseClick, this);
35012             }
35013
35014             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35015                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35016                    id: "message", unselectable: "on", style:{"float":"left"}});
35017                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35018              }
35019             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35020             this.expandBtn.on("click", this.expand, this);
35021             
35022         }
35023         
35024         if(this.collapseBtn){
35025             this.collapseBtn.setVisible(c.collapsible == true);
35026         }
35027         
35028         this.cmargins = c.cmargins || this.cmargins ||
35029                          (this.position == "west" || this.position == "east" ?
35030                              {top: 0, left: 2, right:2, bottom: 0} :
35031                              {top: 2, left: 0, right:0, bottom: 2});
35032         */
35033         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35034         
35035         
35036         this.bottomTabs = c.tabPosition != "top";
35037         
35038         this.autoScroll = c.autoScroll || false;
35039         
35040         
35041        
35042         
35043         this.duration = c.duration || .30;
35044         this.slideDuration = c.slideDuration || .45;
35045         this.config = c;
35046        
35047     },
35048     /**
35049      * Returns true if this region is currently visible.
35050      * @return {Boolean}
35051      */
35052     isVisible : function(){
35053         return this.visible;
35054     },
35055
35056     /**
35057      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35058      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35059      */
35060     //setCollapsedTitle : function(title){
35061     //    title = title || "&#160;";
35062      //   if(this.collapsedTitleTextEl){
35063       //      this.collapsedTitleTextEl.innerHTML = title;
35064        // }
35065     //},
35066
35067     getBox : function(){
35068         var b;
35069       //  if(!this.collapsed){
35070             b = this.el.getBox(false, true);
35071        // }else{
35072           //  b = this.collapsedEl.getBox(false, true);
35073         //}
35074         return b;
35075     },
35076
35077     getMargins : function(){
35078         return this.margins;
35079         //return this.collapsed ? this.cmargins : this.margins;
35080     },
35081 /*
35082     highlight : function(){
35083         this.el.addClass("x-layout-panel-dragover");
35084     },
35085
35086     unhighlight : function(){
35087         this.el.removeClass("x-layout-panel-dragover");
35088     },
35089 */
35090     updateBox : function(box)
35091     {
35092         if (!this.bodyEl) {
35093             return; // not rendered yet..
35094         }
35095         
35096         this.box = box;
35097         if(!this.collapsed){
35098             this.el.dom.style.left = box.x + "px";
35099             this.el.dom.style.top = box.y + "px";
35100             this.updateBody(box.width, box.height);
35101         }else{
35102             this.collapsedEl.dom.style.left = box.x + "px";
35103             this.collapsedEl.dom.style.top = box.y + "px";
35104             this.collapsedEl.setSize(box.width, box.height);
35105         }
35106         if(this.tabs){
35107             this.tabs.autoSizeTabs();
35108         }
35109     },
35110
35111     updateBody : function(w, h)
35112     {
35113         if(w !== null){
35114             this.el.setWidth(w);
35115             w -= this.el.getBorderWidth("rl");
35116             if(this.config.adjustments){
35117                 w += this.config.adjustments[0];
35118             }
35119         }
35120         if(h !== null && h > 0){
35121             this.el.setHeight(h);
35122             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35123             h -= this.el.getBorderWidth("tb");
35124             if(this.config.adjustments){
35125                 h += this.config.adjustments[1];
35126             }
35127             this.bodyEl.setHeight(h);
35128             if(this.tabs){
35129                 h = this.tabs.syncHeight(h);
35130             }
35131         }
35132         if(this.panelSize){
35133             w = w !== null ? w : this.panelSize.width;
35134             h = h !== null ? h : this.panelSize.height;
35135         }
35136         if(this.activePanel){
35137             var el = this.activePanel.getEl();
35138             w = w !== null ? w : el.getWidth();
35139             h = h !== null ? h : el.getHeight();
35140             this.panelSize = {width: w, height: h};
35141             this.activePanel.setSize(w, h);
35142         }
35143         if(Roo.isIE && this.tabs){
35144             this.tabs.el.repaint();
35145         }
35146     },
35147
35148     /**
35149      * Returns the container element for this region.
35150      * @return {Roo.Element}
35151      */
35152     getEl : function(){
35153         return this.el;
35154     },
35155
35156     /**
35157      * Hides this region.
35158      */
35159     hide : function(){
35160         //if(!this.collapsed){
35161             this.el.dom.style.left = "-2000px";
35162             this.el.hide();
35163         //}else{
35164          //   this.collapsedEl.dom.style.left = "-2000px";
35165          //   this.collapsedEl.hide();
35166        // }
35167         this.visible = false;
35168         this.fireEvent("visibilitychange", this, false);
35169     },
35170
35171     /**
35172      * Shows this region if it was previously hidden.
35173      */
35174     show : function(){
35175         //if(!this.collapsed){
35176             this.el.show();
35177         //}else{
35178         //    this.collapsedEl.show();
35179        // }
35180         this.visible = true;
35181         this.fireEvent("visibilitychange", this, true);
35182     },
35183 /*
35184     closeClicked : function(){
35185         if(this.activePanel){
35186             this.remove(this.activePanel);
35187         }
35188     },
35189
35190     collapseClick : function(e){
35191         if(this.isSlid){
35192            e.stopPropagation();
35193            this.slideIn();
35194         }else{
35195            e.stopPropagation();
35196            this.slideOut();
35197         }
35198     },
35199 */
35200     /**
35201      * Collapses this region.
35202      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35203      */
35204     /*
35205     collapse : function(skipAnim, skipCheck = false){
35206         if(this.collapsed) {
35207             return;
35208         }
35209         
35210         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35211             
35212             this.collapsed = true;
35213             if(this.split){
35214                 this.split.el.hide();
35215             }
35216             if(this.config.animate && skipAnim !== true){
35217                 this.fireEvent("invalidated", this);
35218                 this.animateCollapse();
35219             }else{
35220                 this.el.setLocation(-20000,-20000);
35221                 this.el.hide();
35222                 this.collapsedEl.show();
35223                 this.fireEvent("collapsed", this);
35224                 this.fireEvent("invalidated", this);
35225             }
35226         }
35227         
35228     },
35229 */
35230     animateCollapse : function(){
35231         // overridden
35232     },
35233
35234     /**
35235      * Expands this region if it was previously collapsed.
35236      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35237      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35238      */
35239     /*
35240     expand : function(e, skipAnim){
35241         if(e) {
35242             e.stopPropagation();
35243         }
35244         if(!this.collapsed || this.el.hasActiveFx()) {
35245             return;
35246         }
35247         if(this.isSlid){
35248             this.afterSlideIn();
35249             skipAnim = true;
35250         }
35251         this.collapsed = false;
35252         if(this.config.animate && skipAnim !== true){
35253             this.animateExpand();
35254         }else{
35255             this.el.show();
35256             if(this.split){
35257                 this.split.el.show();
35258             }
35259             this.collapsedEl.setLocation(-2000,-2000);
35260             this.collapsedEl.hide();
35261             this.fireEvent("invalidated", this);
35262             this.fireEvent("expanded", this);
35263         }
35264     },
35265 */
35266     animateExpand : function(){
35267         // overridden
35268     },
35269
35270     initTabs : function()
35271     {
35272         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35273         
35274         var ts = new Roo.bootstrap.panel.Tabs({
35275                 el: this.bodyEl.dom,
35276                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35277                 disableTooltips: this.config.disableTabTips,
35278                 toolbar : this.config.toolbar
35279             });
35280         
35281         if(this.config.hideTabs){
35282             ts.stripWrap.setDisplayed(false);
35283         }
35284         this.tabs = ts;
35285         ts.resizeTabs = this.config.resizeTabs === true;
35286         ts.minTabWidth = this.config.minTabWidth || 40;
35287         ts.maxTabWidth = this.config.maxTabWidth || 250;
35288         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35289         ts.monitorResize = false;
35290         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35291         ts.bodyEl.addClass('roo-layout-tabs-body');
35292         this.panels.each(this.initPanelAsTab, this);
35293     },
35294
35295     initPanelAsTab : function(panel){
35296         var ti = this.tabs.addTab(
35297             panel.getEl().id,
35298             panel.getTitle(),
35299             null,
35300             this.config.closeOnTab && panel.isClosable(),
35301             panel.tpl
35302         );
35303         if(panel.tabTip !== undefined){
35304             ti.setTooltip(panel.tabTip);
35305         }
35306         ti.on("activate", function(){
35307               this.setActivePanel(panel);
35308         }, this);
35309         
35310         if(this.config.closeOnTab){
35311             ti.on("beforeclose", function(t, e){
35312                 e.cancel = true;
35313                 this.remove(panel);
35314             }, this);
35315         }
35316         
35317         panel.tabItem = ti;
35318         
35319         return ti;
35320     },
35321
35322     updatePanelTitle : function(panel, title)
35323     {
35324         if(this.activePanel == panel){
35325             this.updateTitle(title);
35326         }
35327         if(this.tabs){
35328             var ti = this.tabs.getTab(panel.getEl().id);
35329             ti.setText(title);
35330             if(panel.tabTip !== undefined){
35331                 ti.setTooltip(panel.tabTip);
35332             }
35333         }
35334     },
35335
35336     updateTitle : function(title){
35337         if(this.titleTextEl && !this.config.title){
35338             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
35339         }
35340     },
35341
35342     setActivePanel : function(panel)
35343     {
35344         panel = this.getPanel(panel);
35345         if(this.activePanel && this.activePanel != panel){
35346             this.activePanel.setActiveState(false);
35347         }
35348         this.activePanel = panel;
35349         panel.setActiveState(true);
35350         if(this.panelSize){
35351             panel.setSize(this.panelSize.width, this.panelSize.height);
35352         }
35353         if(this.closeBtn){
35354             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35355         }
35356         this.updateTitle(panel.getTitle());
35357         if(this.tabs){
35358             this.fireEvent("invalidated", this);
35359         }
35360         this.fireEvent("panelactivated", this, panel);
35361     },
35362
35363     /**
35364      * Shows the specified panel.
35365      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35366      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35367      */
35368     showPanel : function(panel)
35369     {
35370         panel = this.getPanel(panel);
35371         if(panel){
35372             if(this.tabs){
35373                 var tab = this.tabs.getTab(panel.getEl().id);
35374                 if(tab.isHidden()){
35375                     this.tabs.unhideTab(tab.id);
35376                 }
35377                 tab.activate();
35378             }else{
35379                 this.setActivePanel(panel);
35380             }
35381         }
35382         return panel;
35383     },
35384
35385     /**
35386      * Get the active panel for this region.
35387      * @return {Roo.ContentPanel} The active panel or null
35388      */
35389     getActivePanel : function(){
35390         return this.activePanel;
35391     },
35392
35393     validateVisibility : function(){
35394         if(this.panels.getCount() < 1){
35395             this.updateTitle("&#160;");
35396             this.closeBtn.hide();
35397             this.hide();
35398         }else{
35399             if(!this.isVisible()){
35400                 this.show();
35401             }
35402         }
35403     },
35404
35405     /**
35406      * Adds the passed ContentPanel(s) to this region.
35407      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35408      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35409      */
35410     add : function(panel)
35411     {
35412         if(arguments.length > 1){
35413             for(var i = 0, len = arguments.length; i < len; i++) {
35414                 this.add(arguments[i]);
35415             }
35416             return null;
35417         }
35418         
35419         // if we have not been rendered yet, then we can not really do much of this..
35420         if (!this.bodyEl) {
35421             this.unrendered_panels.push(panel);
35422             return panel;
35423         }
35424         
35425         
35426         
35427         
35428         if(this.hasPanel(panel)){
35429             this.showPanel(panel);
35430             return panel;
35431         }
35432         panel.setRegion(this);
35433         this.panels.add(panel);
35434        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
35435             // sinle panel - no tab...?? would it not be better to render it with the tabs,
35436             // and hide them... ???
35437             this.bodyEl.dom.appendChild(panel.getEl().dom);
35438             if(panel.background !== true){
35439                 this.setActivePanel(panel);
35440             }
35441             this.fireEvent("paneladded", this, panel);
35442             return panel;
35443         }
35444         */
35445         if(!this.tabs){
35446             this.initTabs();
35447         }else{
35448             this.initPanelAsTab(panel);
35449         }
35450         
35451         
35452         if(panel.background !== true){
35453             this.tabs.activate(panel.getEl().id);
35454         }
35455         this.fireEvent("paneladded", this, panel);
35456         return panel;
35457     },
35458
35459     /**
35460      * Hides the tab for the specified panel.
35461      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35462      */
35463     hidePanel : function(panel){
35464         if(this.tabs && (panel = this.getPanel(panel))){
35465             this.tabs.hideTab(panel.getEl().id);
35466         }
35467     },
35468
35469     /**
35470      * Unhides the tab for a previously hidden panel.
35471      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35472      */
35473     unhidePanel : function(panel){
35474         if(this.tabs && (panel = this.getPanel(panel))){
35475             this.tabs.unhideTab(panel.getEl().id);
35476         }
35477     },
35478
35479     clearPanels : function(){
35480         while(this.panels.getCount() > 0){
35481              this.remove(this.panels.first());
35482         }
35483     },
35484
35485     /**
35486      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35487      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35488      * @param {Boolean} preservePanel Overrides the config preservePanel option
35489      * @return {Roo.ContentPanel} The panel that was removed
35490      */
35491     remove : function(panel, preservePanel)
35492     {
35493         panel = this.getPanel(panel);
35494         if(!panel){
35495             return null;
35496         }
35497         var e = {};
35498         this.fireEvent("beforeremove", this, panel, e);
35499         if(e.cancel === true){
35500             return null;
35501         }
35502         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
35503         var panelId = panel.getId();
35504         this.panels.removeKey(panelId);
35505         if(preservePanel){
35506             document.body.appendChild(panel.getEl().dom);
35507         }
35508         if(this.tabs){
35509             this.tabs.removeTab(panel.getEl().id);
35510         }else if (!preservePanel){
35511             this.bodyEl.dom.removeChild(panel.getEl().dom);
35512         }
35513         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
35514             var p = this.panels.first();
35515             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
35516             tempEl.appendChild(p.getEl().dom);
35517             this.bodyEl.update("");
35518             this.bodyEl.dom.appendChild(p.getEl().dom);
35519             tempEl = null;
35520             this.updateTitle(p.getTitle());
35521             this.tabs = null;
35522             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
35523             this.setActivePanel(p);
35524         }
35525         panel.setRegion(null);
35526         if(this.activePanel == panel){
35527             this.activePanel = null;
35528         }
35529         if(this.config.autoDestroy !== false && preservePanel !== true){
35530             try{panel.destroy();}catch(e){}
35531         }
35532         this.fireEvent("panelremoved", this, panel);
35533         return panel;
35534     },
35535
35536     /**
35537      * Returns the TabPanel component used by this region
35538      * @return {Roo.TabPanel}
35539      */
35540     getTabs : function(){
35541         return this.tabs;
35542     },
35543
35544     createTool : function(parentEl, className){
35545         var btn = Roo.DomHelper.append(parentEl, {
35546             tag: "div",
35547             cls: "x-layout-tools-button",
35548             children: [ {
35549                 tag: "div",
35550                 cls: "roo-layout-tools-button-inner " + className,
35551                 html: "&#160;"
35552             }]
35553         }, true);
35554         btn.addClassOnOver("roo-layout-tools-button-over");
35555         return btn;
35556     }
35557 });/*
35558  * Based on:
35559  * Ext JS Library 1.1.1
35560  * Copyright(c) 2006-2007, Ext JS, LLC.
35561  *
35562  * Originally Released Under LGPL - original licence link has changed is not relivant.
35563  *
35564  * Fork - LGPL
35565  * <script type="text/javascript">
35566  */
35567  
35568
35569
35570 /**
35571  * @class Roo.SplitLayoutRegion
35572  * @extends Roo.LayoutRegion
35573  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
35574  */
35575 Roo.bootstrap.layout.Split = function(config){
35576     this.cursor = config.cursor;
35577     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
35578 };
35579
35580 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
35581 {
35582     splitTip : "Drag to resize.",
35583     collapsibleSplitTip : "Drag to resize. Double click to hide.",
35584     useSplitTips : false,
35585
35586     applyConfig : function(config){
35587         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
35588     },
35589     
35590     onRender : function(ctr,pos) {
35591         
35592         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
35593         if(!this.config.split){
35594             return;
35595         }
35596         if(!this.split){
35597             
35598             var splitEl = Roo.DomHelper.append(ctr.dom,  {
35599                             tag: "div",
35600                             id: this.el.id + "-split",
35601                             cls: "roo-layout-split roo-layout-split-"+this.position,
35602                             html: "&#160;"
35603             });
35604             /** The SplitBar for this region 
35605             * @type Roo.SplitBar */
35606             // does not exist yet...
35607             Roo.log([this.position, this.orientation]);
35608             
35609             this.split = new Roo.bootstrap.SplitBar({
35610                 dragElement : splitEl,
35611                 resizingElement: this.el,
35612                 orientation : this.orientation
35613             });
35614             
35615             this.split.on("moved", this.onSplitMove, this);
35616             this.split.useShim = this.config.useShim === true;
35617             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
35618             if(this.useSplitTips){
35619                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
35620             }
35621             //if(config.collapsible){
35622             //    this.split.el.on("dblclick", this.collapse,  this);
35623             //}
35624         }
35625         if(typeof this.config.minSize != "undefined"){
35626             this.split.minSize = this.config.minSize;
35627         }
35628         if(typeof this.config.maxSize != "undefined"){
35629             this.split.maxSize = this.config.maxSize;
35630         }
35631         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
35632             this.hideSplitter();
35633         }
35634         
35635     },
35636
35637     getHMaxSize : function(){
35638          var cmax = this.config.maxSize || 10000;
35639          var center = this.mgr.getRegion("center");
35640          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
35641     },
35642
35643     getVMaxSize : function(){
35644          var cmax = this.config.maxSize || 10000;
35645          var center = this.mgr.getRegion("center");
35646          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
35647     },
35648
35649     onSplitMove : function(split, newSize){
35650         this.fireEvent("resized", this, newSize);
35651     },
35652     
35653     /** 
35654      * Returns the {@link Roo.SplitBar} for this region.
35655      * @return {Roo.SplitBar}
35656      */
35657     getSplitBar : function(){
35658         return this.split;
35659     },
35660     
35661     hide : function(){
35662         this.hideSplitter();
35663         Roo.bootstrap.layout.Split.superclass.hide.call(this);
35664     },
35665
35666     hideSplitter : function(){
35667         if(this.split){
35668             this.split.el.setLocation(-2000,-2000);
35669             this.split.el.hide();
35670         }
35671     },
35672
35673     show : function(){
35674         if(this.split){
35675             this.split.el.show();
35676         }
35677         Roo.bootstrap.layout.Split.superclass.show.call(this);
35678     },
35679     
35680     beforeSlide: function(){
35681         if(Roo.isGecko){// firefox overflow auto bug workaround
35682             this.bodyEl.clip();
35683             if(this.tabs) {
35684                 this.tabs.bodyEl.clip();
35685             }
35686             if(this.activePanel){
35687                 this.activePanel.getEl().clip();
35688                 
35689                 if(this.activePanel.beforeSlide){
35690                     this.activePanel.beforeSlide();
35691                 }
35692             }
35693         }
35694     },
35695     
35696     afterSlide : function(){
35697         if(Roo.isGecko){// firefox overflow auto bug workaround
35698             this.bodyEl.unclip();
35699             if(this.tabs) {
35700                 this.tabs.bodyEl.unclip();
35701             }
35702             if(this.activePanel){
35703                 this.activePanel.getEl().unclip();
35704                 if(this.activePanel.afterSlide){
35705                     this.activePanel.afterSlide();
35706                 }
35707             }
35708         }
35709     },
35710
35711     initAutoHide : function(){
35712         if(this.autoHide !== false){
35713             if(!this.autoHideHd){
35714                 var st = new Roo.util.DelayedTask(this.slideIn, this);
35715                 this.autoHideHd = {
35716                     "mouseout": function(e){
35717                         if(!e.within(this.el, true)){
35718                             st.delay(500);
35719                         }
35720                     },
35721                     "mouseover" : function(e){
35722                         st.cancel();
35723                     },
35724                     scope : this
35725                 };
35726             }
35727             this.el.on(this.autoHideHd);
35728         }
35729     },
35730
35731     clearAutoHide : function(){
35732         if(this.autoHide !== false){
35733             this.el.un("mouseout", this.autoHideHd.mouseout);
35734             this.el.un("mouseover", this.autoHideHd.mouseover);
35735         }
35736     },
35737
35738     clearMonitor : function(){
35739         Roo.get(document).un("click", this.slideInIf, this);
35740     },
35741
35742     // these names are backwards but not changed for compat
35743     slideOut : function(){
35744         if(this.isSlid || this.el.hasActiveFx()){
35745             return;
35746         }
35747         this.isSlid = true;
35748         if(this.collapseBtn){
35749             this.collapseBtn.hide();
35750         }
35751         this.closeBtnState = this.closeBtn.getStyle('display');
35752         this.closeBtn.hide();
35753         if(this.stickBtn){
35754             this.stickBtn.show();
35755         }
35756         this.el.show();
35757         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
35758         this.beforeSlide();
35759         this.el.setStyle("z-index", 10001);
35760         this.el.slideIn(this.getSlideAnchor(), {
35761             callback: function(){
35762                 this.afterSlide();
35763                 this.initAutoHide();
35764                 Roo.get(document).on("click", this.slideInIf, this);
35765                 this.fireEvent("slideshow", this);
35766             },
35767             scope: this,
35768             block: true
35769         });
35770     },
35771
35772     afterSlideIn : function(){
35773         this.clearAutoHide();
35774         this.isSlid = false;
35775         this.clearMonitor();
35776         this.el.setStyle("z-index", "");
35777         if(this.collapseBtn){
35778             this.collapseBtn.show();
35779         }
35780         this.closeBtn.setStyle('display', this.closeBtnState);
35781         if(this.stickBtn){
35782             this.stickBtn.hide();
35783         }
35784         this.fireEvent("slidehide", this);
35785     },
35786
35787     slideIn : function(cb){
35788         if(!this.isSlid || this.el.hasActiveFx()){
35789             Roo.callback(cb);
35790             return;
35791         }
35792         this.isSlid = false;
35793         this.beforeSlide();
35794         this.el.slideOut(this.getSlideAnchor(), {
35795             callback: function(){
35796                 this.el.setLeftTop(-10000, -10000);
35797                 this.afterSlide();
35798                 this.afterSlideIn();
35799                 Roo.callback(cb);
35800             },
35801             scope: this,
35802             block: true
35803         });
35804     },
35805     
35806     slideInIf : function(e){
35807         if(!e.within(this.el)){
35808             this.slideIn();
35809         }
35810     },
35811
35812     animateCollapse : function(){
35813         this.beforeSlide();
35814         this.el.setStyle("z-index", 20000);
35815         var anchor = this.getSlideAnchor();
35816         this.el.slideOut(anchor, {
35817             callback : function(){
35818                 this.el.setStyle("z-index", "");
35819                 this.collapsedEl.slideIn(anchor, {duration:.3});
35820                 this.afterSlide();
35821                 this.el.setLocation(-10000,-10000);
35822                 this.el.hide();
35823                 this.fireEvent("collapsed", this);
35824             },
35825             scope: this,
35826             block: true
35827         });
35828     },
35829
35830     animateExpand : function(){
35831         this.beforeSlide();
35832         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
35833         this.el.setStyle("z-index", 20000);
35834         this.collapsedEl.hide({
35835             duration:.1
35836         });
35837         this.el.slideIn(this.getSlideAnchor(), {
35838             callback : function(){
35839                 this.el.setStyle("z-index", "");
35840                 this.afterSlide();
35841                 if(this.split){
35842                     this.split.el.show();
35843                 }
35844                 this.fireEvent("invalidated", this);
35845                 this.fireEvent("expanded", this);
35846             },
35847             scope: this,
35848             block: true
35849         });
35850     },
35851
35852     anchors : {
35853         "west" : "left",
35854         "east" : "right",
35855         "north" : "top",
35856         "south" : "bottom"
35857     },
35858
35859     sanchors : {
35860         "west" : "l",
35861         "east" : "r",
35862         "north" : "t",
35863         "south" : "b"
35864     },
35865
35866     canchors : {
35867         "west" : "tl-tr",
35868         "east" : "tr-tl",
35869         "north" : "tl-bl",
35870         "south" : "bl-tl"
35871     },
35872
35873     getAnchor : function(){
35874         return this.anchors[this.position];
35875     },
35876
35877     getCollapseAnchor : function(){
35878         return this.canchors[this.position];
35879     },
35880
35881     getSlideAnchor : function(){
35882         return this.sanchors[this.position];
35883     },
35884
35885     getAlignAdj : function(){
35886         var cm = this.cmargins;
35887         switch(this.position){
35888             case "west":
35889                 return [0, 0];
35890             break;
35891             case "east":
35892                 return [0, 0];
35893             break;
35894             case "north":
35895                 return [0, 0];
35896             break;
35897             case "south":
35898                 return [0, 0];
35899             break;
35900         }
35901     },
35902
35903     getExpandAdj : function(){
35904         var c = this.collapsedEl, cm = this.cmargins;
35905         switch(this.position){
35906             case "west":
35907                 return [-(cm.right+c.getWidth()+cm.left), 0];
35908             break;
35909             case "east":
35910                 return [cm.right+c.getWidth()+cm.left, 0];
35911             break;
35912             case "north":
35913                 return [0, -(cm.top+cm.bottom+c.getHeight())];
35914             break;
35915             case "south":
35916                 return [0, cm.top+cm.bottom+c.getHeight()];
35917             break;
35918         }
35919     }
35920 });/*
35921  * Based on:
35922  * Ext JS Library 1.1.1
35923  * Copyright(c) 2006-2007, Ext JS, LLC.
35924  *
35925  * Originally Released Under LGPL - original licence link has changed is not relivant.
35926  *
35927  * Fork - LGPL
35928  * <script type="text/javascript">
35929  */
35930 /*
35931  * These classes are private internal classes
35932  */
35933 Roo.bootstrap.layout.Center = function(config){
35934     config.region = "center";
35935     Roo.bootstrap.layout.Region.call(this, config);
35936     this.visible = true;
35937     this.minWidth = config.minWidth || 20;
35938     this.minHeight = config.minHeight || 20;
35939 };
35940
35941 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
35942     hide : function(){
35943         // center panel can't be hidden
35944     },
35945     
35946     show : function(){
35947         // center panel can't be hidden
35948     },
35949     
35950     getMinWidth: function(){
35951         return this.minWidth;
35952     },
35953     
35954     getMinHeight: function(){
35955         return this.minHeight;
35956     }
35957 });
35958
35959
35960
35961
35962  
35963
35964
35965
35966
35967
35968 Roo.bootstrap.layout.North = function(config)
35969 {
35970     config.region = 'north';
35971     config.cursor = 'n-resize';
35972     
35973     Roo.bootstrap.layout.Split.call(this, config);
35974     
35975     
35976     if(this.split){
35977         this.split.placement = Roo.bootstrap.SplitBar.TOP;
35978         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
35979         this.split.el.addClass("roo-layout-split-v");
35980     }
35981     var size = config.initialSize || config.height;
35982     if(typeof size != "undefined"){
35983         this.el.setHeight(size);
35984     }
35985 };
35986 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
35987 {
35988     orientation: Roo.bootstrap.SplitBar.VERTICAL,
35989     
35990     
35991     
35992     getBox : function(){
35993         if(this.collapsed){
35994             return this.collapsedEl.getBox();
35995         }
35996         var box = this.el.getBox();
35997         if(this.split){
35998             box.height += this.split.el.getHeight();
35999         }
36000         return box;
36001     },
36002     
36003     updateBox : function(box){
36004         if(this.split && !this.collapsed){
36005             box.height -= this.split.el.getHeight();
36006             this.split.el.setLeft(box.x);
36007             this.split.el.setTop(box.y+box.height);
36008             this.split.el.setWidth(box.width);
36009         }
36010         if(this.collapsed){
36011             this.updateBody(box.width, null);
36012         }
36013         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36014     }
36015 });
36016
36017
36018
36019
36020
36021 Roo.bootstrap.layout.South = function(config){
36022     config.region = 'south';
36023     config.cursor = 's-resize';
36024     Roo.bootstrap.layout.Split.call(this, config);
36025     if(this.split){
36026         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36027         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36028         this.split.el.addClass("roo-layout-split-v");
36029     }
36030     var size = config.initialSize || config.height;
36031     if(typeof size != "undefined"){
36032         this.el.setHeight(size);
36033     }
36034 };
36035
36036 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36037     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36038     getBox : function(){
36039         if(this.collapsed){
36040             return this.collapsedEl.getBox();
36041         }
36042         var box = this.el.getBox();
36043         if(this.split){
36044             var sh = this.split.el.getHeight();
36045             box.height += sh;
36046             box.y -= sh;
36047         }
36048         return box;
36049     },
36050     
36051     updateBox : function(box){
36052         if(this.split && !this.collapsed){
36053             var sh = this.split.el.getHeight();
36054             box.height -= sh;
36055             box.y += sh;
36056             this.split.el.setLeft(box.x);
36057             this.split.el.setTop(box.y-sh);
36058             this.split.el.setWidth(box.width);
36059         }
36060         if(this.collapsed){
36061             this.updateBody(box.width, null);
36062         }
36063         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36064     }
36065 });
36066
36067 Roo.bootstrap.layout.East = function(config){
36068     config.region = "east";
36069     config.cursor = "e-resize";
36070     Roo.bootstrap.layout.Split.call(this, config);
36071     if(this.split){
36072         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36073         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36074         this.split.el.addClass("roo-layout-split-h");
36075     }
36076     var size = config.initialSize || config.width;
36077     if(typeof size != "undefined"){
36078         this.el.setWidth(size);
36079     }
36080 };
36081 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36082     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36083     getBox : function(){
36084         if(this.collapsed){
36085             return this.collapsedEl.getBox();
36086         }
36087         var box = this.el.getBox();
36088         if(this.split){
36089             var sw = this.split.el.getWidth();
36090             box.width += sw;
36091             box.x -= sw;
36092         }
36093         return box;
36094     },
36095
36096     updateBox : function(box){
36097         if(this.split && !this.collapsed){
36098             var sw = this.split.el.getWidth();
36099             box.width -= sw;
36100             this.split.el.setLeft(box.x);
36101             this.split.el.setTop(box.y);
36102             this.split.el.setHeight(box.height);
36103             box.x += sw;
36104         }
36105         if(this.collapsed){
36106             this.updateBody(null, box.height);
36107         }
36108         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36109     }
36110 });
36111
36112 Roo.bootstrap.layout.West = function(config){
36113     config.region = "west";
36114     config.cursor = "w-resize";
36115     
36116     Roo.bootstrap.layout.Split.call(this, config);
36117     if(this.split){
36118         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36119         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36120         this.split.el.addClass("roo-layout-split-h");
36121     }
36122     
36123 };
36124 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36125     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36126     
36127     onRender: function(ctr, pos)
36128     {
36129         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36130         var size = this.config.initialSize || this.config.width;
36131         if(typeof size != "undefined"){
36132             this.el.setWidth(size);
36133         }
36134     },
36135     
36136     getBox : function(){
36137         if(this.collapsed){
36138             return this.collapsedEl.getBox();
36139         }
36140         var box = this.el.getBox();
36141         if(this.split){
36142             box.width += this.split.el.getWidth();
36143         }
36144         return box;
36145     },
36146     
36147     updateBox : function(box){
36148         if(this.split && !this.collapsed){
36149             var sw = this.split.el.getWidth();
36150             box.width -= sw;
36151             this.split.el.setLeft(box.x+box.width);
36152             this.split.el.setTop(box.y);
36153             this.split.el.setHeight(box.height);
36154         }
36155         if(this.collapsed){
36156             this.updateBody(null, box.height);
36157         }
36158         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36159     }
36160 });
36161 Roo.namespace("Roo.bootstrap.panel");/*
36162  * Based on:
36163  * Ext JS Library 1.1.1
36164  * Copyright(c) 2006-2007, Ext JS, LLC.
36165  *
36166  * Originally Released Under LGPL - original licence link has changed is not relivant.
36167  *
36168  * Fork - LGPL
36169  * <script type="text/javascript">
36170  */
36171 /**
36172  * @class Roo.ContentPanel
36173  * @extends Roo.util.Observable
36174  * A basic ContentPanel element.
36175  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36176  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36177  * @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
36178  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36179  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36180  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36181  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36182  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36183  * @cfg {String} title          The title for this panel
36184  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36185  * @cfg {String} url            Calls {@link #setUrl} with this value
36186  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36187  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36188  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36189  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36190  * @cfg {Boolean} badges render the badges
36191
36192  * @constructor
36193  * Create a new ContentPanel.
36194  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36195  * @param {String/Object} config A string to set only the title or a config object
36196  * @param {String} content (optional) Set the HTML content for this panel
36197  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36198  */
36199 Roo.bootstrap.panel.Content = function( config){
36200     
36201     this.tpl = config.tpl || false;
36202     
36203     var el = config.el;
36204     var content = config.content;
36205
36206     if(config.autoCreate){ // xtype is available if this is called from factory
36207         el = Roo.id();
36208     }
36209     this.el = Roo.get(el);
36210     if(!this.el && config && config.autoCreate){
36211         if(typeof config.autoCreate == "object"){
36212             if(!config.autoCreate.id){
36213                 config.autoCreate.id = config.id||el;
36214             }
36215             this.el = Roo.DomHelper.append(document.body,
36216                         config.autoCreate, true);
36217         }else{
36218             var elcfg =  {   tag: "div",
36219                             cls: "roo-layout-inactive-content",
36220                             id: config.id||el
36221                             };
36222             if (config.html) {
36223                 elcfg.html = config.html;
36224                 
36225             }
36226                         
36227             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36228         }
36229     } 
36230     this.closable = false;
36231     this.loaded = false;
36232     this.active = false;
36233    
36234       
36235     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36236         
36237         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36238         
36239         this.wrapEl = this.el; //this.el.wrap();
36240         var ti = [];
36241         if (config.toolbar.items) {
36242             ti = config.toolbar.items ;
36243             delete config.toolbar.items ;
36244         }
36245         
36246         var nitems = [];
36247         this.toolbar.render(this.wrapEl, 'before');
36248         for(var i =0;i < ti.length;i++) {
36249           //  Roo.log(['add child', items[i]]);
36250             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36251         }
36252         this.toolbar.items = nitems;
36253         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36254         delete config.toolbar;
36255         
36256     }
36257     /*
36258     // xtype created footer. - not sure if will work as we normally have to render first..
36259     if (this.footer && !this.footer.el && this.footer.xtype) {
36260         if (!this.wrapEl) {
36261             this.wrapEl = this.el.wrap();
36262         }
36263     
36264         this.footer.container = this.wrapEl.createChild();
36265          
36266         this.footer = Roo.factory(this.footer, Roo);
36267         
36268     }
36269     */
36270     
36271      if(typeof config == "string"){
36272         this.title = config;
36273     }else{
36274         Roo.apply(this, config);
36275     }
36276     
36277     if(this.resizeEl){
36278         this.resizeEl = Roo.get(this.resizeEl, true);
36279     }else{
36280         this.resizeEl = this.el;
36281     }
36282     // handle view.xtype
36283     
36284  
36285     
36286     
36287     this.addEvents({
36288         /**
36289          * @event activate
36290          * Fires when this panel is activated. 
36291          * @param {Roo.ContentPanel} this
36292          */
36293         "activate" : true,
36294         /**
36295          * @event deactivate
36296          * Fires when this panel is activated. 
36297          * @param {Roo.ContentPanel} this
36298          */
36299         "deactivate" : true,
36300
36301         /**
36302          * @event resize
36303          * Fires when this panel is resized if fitToFrame is true.
36304          * @param {Roo.ContentPanel} this
36305          * @param {Number} width The width after any component adjustments
36306          * @param {Number} height The height after any component adjustments
36307          */
36308         "resize" : true,
36309         
36310          /**
36311          * @event render
36312          * Fires when this tab is created
36313          * @param {Roo.ContentPanel} this
36314          */
36315         "render" : true
36316         
36317         
36318         
36319     });
36320     
36321
36322     
36323     
36324     if(this.autoScroll){
36325         this.resizeEl.setStyle("overflow", "auto");
36326     } else {
36327         // fix randome scrolling
36328         //this.el.on('scroll', function() {
36329         //    Roo.log('fix random scolling');
36330         //    this.scrollTo('top',0); 
36331         //});
36332     }
36333     content = content || this.content;
36334     if(content){
36335         this.setContent(content);
36336     }
36337     if(config && config.url){
36338         this.setUrl(this.url, this.params, this.loadOnce);
36339     }
36340     
36341     
36342     
36343     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36344     
36345     if (this.view && typeof(this.view.xtype) != 'undefined') {
36346         this.view.el = this.el.appendChild(document.createElement("div"));
36347         this.view = Roo.factory(this.view); 
36348         this.view.render  &&  this.view.render(false, '');  
36349     }
36350     
36351     
36352     this.fireEvent('render', this);
36353 };
36354
36355 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36356     
36357     tabTip : '',
36358     
36359     setRegion : function(region){
36360         this.region = region;
36361         this.setActiveClass(region && !this.background);
36362     },
36363     
36364     
36365     setActiveClass: function(state)
36366     {
36367         if(state){
36368            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36369            this.el.setStyle('position','relative');
36370         }else{
36371            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36372            this.el.setStyle('position', 'absolute');
36373         } 
36374     },
36375     
36376     /**
36377      * Returns the toolbar for this Panel if one was configured. 
36378      * @return {Roo.Toolbar} 
36379      */
36380     getToolbar : function(){
36381         return this.toolbar;
36382     },
36383     
36384     setActiveState : function(active)
36385     {
36386         this.active = active;
36387         this.setActiveClass(active);
36388         if(!active){
36389             this.fireEvent("deactivate", this);
36390         }else{
36391             this.fireEvent("activate", this);
36392         }
36393     },
36394     /**
36395      * Updates this panel's element
36396      * @param {String} content The new content
36397      * @param {Boolean} loadScripts (optional) true to look for and process scripts
36398     */
36399     setContent : function(content, loadScripts){
36400         this.el.update(content, loadScripts);
36401     },
36402
36403     ignoreResize : function(w, h){
36404         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36405             return true;
36406         }else{
36407             this.lastSize = {width: w, height: h};
36408             return false;
36409         }
36410     },
36411     /**
36412      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36413      * @return {Roo.UpdateManager} The UpdateManager
36414      */
36415     getUpdateManager : function(){
36416         return this.el.getUpdateManager();
36417     },
36418      /**
36419      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36420      * @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:
36421 <pre><code>
36422 panel.load({
36423     url: "your-url.php",
36424     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36425     callback: yourFunction,
36426     scope: yourObject, //(optional scope)
36427     discardUrl: false,
36428     nocache: false,
36429     text: "Loading...",
36430     timeout: 30,
36431     scripts: false
36432 });
36433 </code></pre>
36434      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
36435      * 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.
36436      * @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}
36437      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
36438      * @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.
36439      * @return {Roo.ContentPanel} this
36440      */
36441     load : function(){
36442         var um = this.el.getUpdateManager();
36443         um.update.apply(um, arguments);
36444         return this;
36445     },
36446
36447
36448     /**
36449      * 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.
36450      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
36451      * @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)
36452      * @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)
36453      * @return {Roo.UpdateManager} The UpdateManager
36454      */
36455     setUrl : function(url, params, loadOnce){
36456         if(this.refreshDelegate){
36457             this.removeListener("activate", this.refreshDelegate);
36458         }
36459         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36460         this.on("activate", this.refreshDelegate);
36461         return this.el.getUpdateManager();
36462     },
36463     
36464     _handleRefresh : function(url, params, loadOnce){
36465         if(!loadOnce || !this.loaded){
36466             var updater = this.el.getUpdateManager();
36467             updater.update(url, params, this._setLoaded.createDelegate(this));
36468         }
36469     },
36470     
36471     _setLoaded : function(){
36472         this.loaded = true;
36473     }, 
36474     
36475     /**
36476      * Returns this panel's id
36477      * @return {String} 
36478      */
36479     getId : function(){
36480         return this.el.id;
36481     },
36482     
36483     /** 
36484      * Returns this panel's element - used by regiosn to add.
36485      * @return {Roo.Element} 
36486      */
36487     getEl : function(){
36488         return this.wrapEl || this.el;
36489     },
36490     
36491    
36492     
36493     adjustForComponents : function(width, height)
36494     {
36495         //Roo.log('adjustForComponents ');
36496         if(this.resizeEl != this.el){
36497             width -= this.el.getFrameWidth('lr');
36498             height -= this.el.getFrameWidth('tb');
36499         }
36500         if(this.toolbar){
36501             var te = this.toolbar.getEl();
36502             te.setWidth(width);
36503             height -= te.getHeight();
36504         }
36505         if(this.footer){
36506             var te = this.footer.getEl();
36507             te.setWidth(width);
36508             height -= te.getHeight();
36509         }
36510         
36511         
36512         if(this.adjustments){
36513             width += this.adjustments[0];
36514             height += this.adjustments[1];
36515         }
36516         return {"width": width, "height": height};
36517     },
36518     
36519     setSize : function(width, height){
36520         if(this.fitToFrame && !this.ignoreResize(width, height)){
36521             if(this.fitContainer && this.resizeEl != this.el){
36522                 this.el.setSize(width, height);
36523             }
36524             var size = this.adjustForComponents(width, height);
36525             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
36526             this.fireEvent('resize', this, size.width, size.height);
36527         }
36528     },
36529     
36530     /**
36531      * Returns this panel's title
36532      * @return {String} 
36533      */
36534     getTitle : function(){
36535         
36536         if (typeof(this.title) != 'object') {
36537             return this.title;
36538         }
36539         
36540         var t = '';
36541         for (var k in this.title) {
36542             if (!this.title.hasOwnProperty(k)) {
36543                 continue;
36544             }
36545             
36546             if (k.indexOf('-') >= 0) {
36547                 var s = k.split('-');
36548                 for (var i = 0; i<s.length; i++) {
36549                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
36550                 }
36551             } else {
36552                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
36553             }
36554         }
36555         return t;
36556     },
36557     
36558     /**
36559      * Set this panel's title
36560      * @param {String} title
36561      */
36562     setTitle : function(title){
36563         this.title = title;
36564         if(this.region){
36565             this.region.updatePanelTitle(this, title);
36566         }
36567     },
36568     
36569     /**
36570      * Returns true is this panel was configured to be closable
36571      * @return {Boolean} 
36572      */
36573     isClosable : function(){
36574         return this.closable;
36575     },
36576     
36577     beforeSlide : function(){
36578         this.el.clip();
36579         this.resizeEl.clip();
36580     },
36581     
36582     afterSlide : function(){
36583         this.el.unclip();
36584         this.resizeEl.unclip();
36585     },
36586     
36587     /**
36588      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
36589      *   Will fail silently if the {@link #setUrl} method has not been called.
36590      *   This does not activate the panel, just updates its content.
36591      */
36592     refresh : function(){
36593         if(this.refreshDelegate){
36594            this.loaded = false;
36595            this.refreshDelegate();
36596         }
36597     },
36598     
36599     /**
36600      * Destroys this panel
36601      */
36602     destroy : function(){
36603         this.el.removeAllListeners();
36604         var tempEl = document.createElement("span");
36605         tempEl.appendChild(this.el.dom);
36606         tempEl.innerHTML = "";
36607         this.el.remove();
36608         this.el = null;
36609     },
36610     
36611     /**
36612      * form - if the content panel contains a form - this is a reference to it.
36613      * @type {Roo.form.Form}
36614      */
36615     form : false,
36616     /**
36617      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
36618      *    This contains a reference to it.
36619      * @type {Roo.View}
36620      */
36621     view : false,
36622     
36623       /**
36624      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
36625      * <pre><code>
36626
36627 layout.addxtype({
36628        xtype : 'Form',
36629        items: [ .... ]
36630    }
36631 );
36632
36633 </code></pre>
36634      * @param {Object} cfg Xtype definition of item to add.
36635      */
36636     
36637     
36638     getChildContainer: function () {
36639         return this.getEl();
36640     }
36641     
36642     
36643     /*
36644         var  ret = new Roo.factory(cfg);
36645         return ret;
36646         
36647         
36648         // add form..
36649         if (cfg.xtype.match(/^Form$/)) {
36650             
36651             var el;
36652             //if (this.footer) {
36653             //    el = this.footer.container.insertSibling(false, 'before');
36654             //} else {
36655                 el = this.el.createChild();
36656             //}
36657
36658             this.form = new  Roo.form.Form(cfg);
36659             
36660             
36661             if ( this.form.allItems.length) {
36662                 this.form.render(el.dom);
36663             }
36664             return this.form;
36665         }
36666         // should only have one of theses..
36667         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
36668             // views.. should not be just added - used named prop 'view''
36669             
36670             cfg.el = this.el.appendChild(document.createElement("div"));
36671             // factory?
36672             
36673             var ret = new Roo.factory(cfg);
36674              
36675              ret.render && ret.render(false, ''); // render blank..
36676             this.view = ret;
36677             return ret;
36678         }
36679         return false;
36680     }
36681     \*/
36682 });
36683  
36684 /**
36685  * @class Roo.bootstrap.panel.Grid
36686  * @extends Roo.bootstrap.panel.Content
36687  * @constructor
36688  * Create a new GridPanel.
36689  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
36690  * @param {Object} config A the config object
36691   
36692  */
36693
36694
36695
36696 Roo.bootstrap.panel.Grid = function(config)
36697 {
36698     
36699       
36700     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
36701         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
36702
36703     config.el = this.wrapper;
36704     //this.el = this.wrapper;
36705     
36706       if (config.container) {
36707         // ctor'ed from a Border/panel.grid
36708         
36709         
36710         this.wrapper.setStyle("overflow", "hidden");
36711         this.wrapper.addClass('roo-grid-container');
36712
36713     }
36714     
36715     
36716     if(config.toolbar){
36717         var tool_el = this.wrapper.createChild();    
36718         this.toolbar = Roo.factory(config.toolbar);
36719         var ti = [];
36720         if (config.toolbar.items) {
36721             ti = config.toolbar.items ;
36722             delete config.toolbar.items ;
36723         }
36724         
36725         var nitems = [];
36726         this.toolbar.render(tool_el);
36727         for(var i =0;i < ti.length;i++) {
36728           //  Roo.log(['add child', items[i]]);
36729             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36730         }
36731         this.toolbar.items = nitems;
36732         
36733         delete config.toolbar;
36734     }
36735     
36736     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
36737     config.grid.scrollBody = true;;
36738     config.grid.monitorWindowResize = false; // turn off autosizing
36739     config.grid.autoHeight = false;
36740     config.grid.autoWidth = false;
36741     
36742     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
36743     
36744     if (config.background) {
36745         // render grid on panel activation (if panel background)
36746         this.on('activate', function(gp) {
36747             if (!gp.grid.rendered) {
36748                 gp.grid.render(this.wrapper);
36749                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
36750             }
36751         });
36752             
36753     } else {
36754         this.grid.render(this.wrapper);
36755         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
36756
36757     }
36758     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
36759     // ??? needed ??? config.el = this.wrapper;
36760     
36761     
36762     
36763   
36764     // xtype created footer. - not sure if will work as we normally have to render first..
36765     if (this.footer && !this.footer.el && this.footer.xtype) {
36766         
36767         var ctr = this.grid.getView().getFooterPanel(true);
36768         this.footer.dataSource = this.grid.dataSource;
36769         this.footer = Roo.factory(this.footer, Roo);
36770         this.footer.render(ctr);
36771         
36772     }
36773     
36774     
36775     
36776     
36777      
36778 };
36779
36780 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
36781     getId : function(){
36782         return this.grid.id;
36783     },
36784     
36785     /**
36786      * Returns the grid for this panel
36787      * @return {Roo.bootstrap.Table} 
36788      */
36789     getGrid : function(){
36790         return this.grid;    
36791     },
36792     
36793     setSize : function(width, height){
36794         if(!this.ignoreResize(width, height)){
36795             var grid = this.grid;
36796             var size = this.adjustForComponents(width, height);
36797             var gridel = grid.getGridEl();
36798             gridel.setSize(size.width, size.height);
36799             /*
36800             var thd = grid.getGridEl().select('thead',true).first();
36801             var tbd = grid.getGridEl().select('tbody', true).first();
36802             if (tbd) {
36803                 tbd.setSize(width, height - thd.getHeight());
36804             }
36805             */
36806             grid.autoSize();
36807         }
36808     },
36809      
36810     
36811     
36812     beforeSlide : function(){
36813         this.grid.getView().scroller.clip();
36814     },
36815     
36816     afterSlide : function(){
36817         this.grid.getView().scroller.unclip();
36818     },
36819     
36820     destroy : function(){
36821         this.grid.destroy();
36822         delete this.grid;
36823         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
36824     }
36825 });
36826
36827 /**
36828  * @class Roo.bootstrap.panel.Nest
36829  * @extends Roo.bootstrap.panel.Content
36830  * @constructor
36831  * Create a new Panel, that can contain a layout.Border.
36832  * 
36833  * 
36834  * @param {Roo.BorderLayout} layout The layout for this panel
36835  * @param {String/Object} config A string to set only the title or a config object
36836  */
36837 Roo.bootstrap.panel.Nest = function(config)
36838 {
36839     // construct with only one argument..
36840     /* FIXME - implement nicer consturctors
36841     if (layout.layout) {
36842         config = layout;
36843         layout = config.layout;
36844         delete config.layout;
36845     }
36846     if (layout.xtype && !layout.getEl) {
36847         // then layout needs constructing..
36848         layout = Roo.factory(layout, Roo);
36849     }
36850     */
36851     
36852     config.el =  config.layout.getEl();
36853     
36854     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
36855     
36856     config.layout.monitorWindowResize = false; // turn off autosizing
36857     this.layout = config.layout;
36858     this.layout.getEl().addClass("roo-layout-nested-layout");
36859     
36860     
36861     
36862     
36863 };
36864
36865 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
36866
36867     setSize : function(width, height){
36868         if(!this.ignoreResize(width, height)){
36869             var size = this.adjustForComponents(width, height);
36870             var el = this.layout.getEl();
36871             if (size.height < 1) {
36872                 el.setWidth(size.width);   
36873             } else {
36874                 el.setSize(size.width, size.height);
36875             }
36876             var touch = el.dom.offsetWidth;
36877             this.layout.layout();
36878             // ie requires a double layout on the first pass
36879             if(Roo.isIE && !this.initialized){
36880                 this.initialized = true;
36881                 this.layout.layout();
36882             }
36883         }
36884     },
36885     
36886     // activate all subpanels if not currently active..
36887     
36888     setActiveState : function(active){
36889         this.active = active;
36890         this.setActiveClass(active);
36891         
36892         if(!active){
36893             this.fireEvent("deactivate", this);
36894             return;
36895         }
36896         
36897         this.fireEvent("activate", this);
36898         // not sure if this should happen before or after..
36899         if (!this.layout) {
36900             return; // should not happen..
36901         }
36902         var reg = false;
36903         for (var r in this.layout.regions) {
36904             reg = this.layout.getRegion(r);
36905             if (reg.getActivePanel()) {
36906                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
36907                 reg.setActivePanel(reg.getActivePanel());
36908                 continue;
36909             }
36910             if (!reg.panels.length) {
36911                 continue;
36912             }
36913             reg.showPanel(reg.getPanel(0));
36914         }
36915         
36916         
36917         
36918         
36919     },
36920     
36921     /**
36922      * Returns the nested BorderLayout for this panel
36923      * @return {Roo.BorderLayout} 
36924      */
36925     getLayout : function(){
36926         return this.layout;
36927     },
36928     
36929      /**
36930      * Adds a xtype elements to the layout of the nested panel
36931      * <pre><code>
36932
36933 panel.addxtype({
36934        xtype : 'ContentPanel',
36935        region: 'west',
36936        items: [ .... ]
36937    }
36938 );
36939
36940 panel.addxtype({
36941         xtype : 'NestedLayoutPanel',
36942         region: 'west',
36943         layout: {
36944            center: { },
36945            west: { }   
36946         },
36947         items : [ ... list of content panels or nested layout panels.. ]
36948    }
36949 );
36950 </code></pre>
36951      * @param {Object} cfg Xtype definition of item to add.
36952      */
36953     addxtype : function(cfg) {
36954         return this.layout.addxtype(cfg);
36955     
36956     }
36957 });        /*
36958  * Based on:
36959  * Ext JS Library 1.1.1
36960  * Copyright(c) 2006-2007, Ext JS, LLC.
36961  *
36962  * Originally Released Under LGPL - original licence link has changed is not relivant.
36963  *
36964  * Fork - LGPL
36965  * <script type="text/javascript">
36966  */
36967 /**
36968  * @class Roo.TabPanel
36969  * @extends Roo.util.Observable
36970  * A lightweight tab container.
36971  * <br><br>
36972  * Usage:
36973  * <pre><code>
36974 // basic tabs 1, built from existing content
36975 var tabs = new Roo.TabPanel("tabs1");
36976 tabs.addTab("script", "View Script");
36977 tabs.addTab("markup", "View Markup");
36978 tabs.activate("script");
36979
36980 // more advanced tabs, built from javascript
36981 var jtabs = new Roo.TabPanel("jtabs");
36982 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
36983
36984 // set up the UpdateManager
36985 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
36986 var updater = tab2.getUpdateManager();
36987 updater.setDefaultUrl("ajax1.htm");
36988 tab2.on('activate', updater.refresh, updater, true);
36989
36990 // Use setUrl for Ajax loading
36991 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
36992 tab3.setUrl("ajax2.htm", null, true);
36993
36994 // Disabled tab
36995 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
36996 tab4.disable();
36997
36998 jtabs.activate("jtabs-1");
36999  * </code></pre>
37000  * @constructor
37001  * Create a new TabPanel.
37002  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37003  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37004  */
37005 Roo.bootstrap.panel.Tabs = function(config){
37006     /**
37007     * The container element for this TabPanel.
37008     * @type Roo.Element
37009     */
37010     this.el = Roo.get(config.el);
37011     delete config.el;
37012     if(config){
37013         if(typeof config == "boolean"){
37014             this.tabPosition = config ? "bottom" : "top";
37015         }else{
37016             Roo.apply(this, config);
37017         }
37018     }
37019     
37020     if(this.tabPosition == "bottom"){
37021         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37022         this.el.addClass("roo-tabs-bottom");
37023     }
37024     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37025     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37026     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37027     if(Roo.isIE){
37028         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37029     }
37030     if(this.tabPosition != "bottom"){
37031         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37032          * @type Roo.Element
37033          */
37034         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37035         this.el.addClass("roo-tabs-top");
37036     }
37037     this.items = [];
37038
37039     this.bodyEl.setStyle("position", "relative");
37040
37041     this.active = null;
37042     this.activateDelegate = this.activate.createDelegate(this);
37043
37044     this.addEvents({
37045         /**
37046          * @event tabchange
37047          * Fires when the active tab changes
37048          * @param {Roo.TabPanel} this
37049          * @param {Roo.TabPanelItem} activePanel The new active tab
37050          */
37051         "tabchange": true,
37052         /**
37053          * @event beforetabchange
37054          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37055          * @param {Roo.TabPanel} this
37056          * @param {Object} e Set cancel to true on this object to cancel the tab change
37057          * @param {Roo.TabPanelItem} tab The tab being changed to
37058          */
37059         "beforetabchange" : true
37060     });
37061
37062     Roo.EventManager.onWindowResize(this.onResize, this);
37063     this.cpad = this.el.getPadding("lr");
37064     this.hiddenCount = 0;
37065
37066
37067     // toolbar on the tabbar support...
37068     if (this.toolbar) {
37069         alert("no toolbar support yet");
37070         this.toolbar  = false;
37071         /*
37072         var tcfg = this.toolbar;
37073         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37074         this.toolbar = new Roo.Toolbar(tcfg);
37075         if (Roo.isSafari) {
37076             var tbl = tcfg.container.child('table', true);
37077             tbl.setAttribute('width', '100%');
37078         }
37079         */
37080         
37081     }
37082    
37083
37084
37085     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37086 };
37087
37088 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37089     /*
37090      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37091      */
37092     tabPosition : "top",
37093     /*
37094      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37095      */
37096     currentTabWidth : 0,
37097     /*
37098      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37099      */
37100     minTabWidth : 40,
37101     /*
37102      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37103      */
37104     maxTabWidth : 250,
37105     /*
37106      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37107      */
37108     preferredTabWidth : 175,
37109     /*
37110      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37111      */
37112     resizeTabs : false,
37113     /*
37114      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37115      */
37116     monitorResize : true,
37117     /*
37118      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37119      */
37120     toolbar : false,
37121
37122     /**
37123      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37124      * @param {String} id The id of the div to use <b>or create</b>
37125      * @param {String} text The text for the tab
37126      * @param {String} content (optional) Content to put in the TabPanelItem body
37127      * @param {Boolean} closable (optional) True to create a close icon on the tab
37128      * @return {Roo.TabPanelItem} The created TabPanelItem
37129      */
37130     addTab : function(id, text, content, closable, tpl)
37131     {
37132         var item = new Roo.bootstrap.panel.TabItem({
37133             panel: this,
37134             id : id,
37135             text : text,
37136             closable : closable,
37137             tpl : tpl
37138         });
37139         this.addTabItem(item);
37140         if(content){
37141             item.setContent(content);
37142         }
37143         return item;
37144     },
37145
37146     /**
37147      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37148      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37149      * @return {Roo.TabPanelItem}
37150      */
37151     getTab : function(id){
37152         return this.items[id];
37153     },
37154
37155     /**
37156      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37157      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37158      */
37159     hideTab : function(id){
37160         var t = this.items[id];
37161         if(!t.isHidden()){
37162            t.setHidden(true);
37163            this.hiddenCount++;
37164            this.autoSizeTabs();
37165         }
37166     },
37167
37168     /**
37169      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37170      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37171      */
37172     unhideTab : function(id){
37173         var t = this.items[id];
37174         if(t.isHidden()){
37175            t.setHidden(false);
37176            this.hiddenCount--;
37177            this.autoSizeTabs();
37178         }
37179     },
37180
37181     /**
37182      * Adds an existing {@link Roo.TabPanelItem}.
37183      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37184      */
37185     addTabItem : function(item){
37186         this.items[item.id] = item;
37187         this.items.push(item);
37188       //  if(this.resizeTabs){
37189     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37190   //         this.autoSizeTabs();
37191 //        }else{
37192 //            item.autoSize();
37193        // }
37194     },
37195
37196     /**
37197      * Removes a {@link Roo.TabPanelItem}.
37198      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37199      */
37200     removeTab : function(id){
37201         var items = this.items;
37202         var tab = items[id];
37203         if(!tab) { return; }
37204         var index = items.indexOf(tab);
37205         if(this.active == tab && items.length > 1){
37206             var newTab = this.getNextAvailable(index);
37207             if(newTab) {
37208                 newTab.activate();
37209             }
37210         }
37211         this.stripEl.dom.removeChild(tab.pnode.dom);
37212         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37213             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37214         }
37215         items.splice(index, 1);
37216         delete this.items[tab.id];
37217         tab.fireEvent("close", tab);
37218         tab.purgeListeners();
37219         this.autoSizeTabs();
37220     },
37221
37222     getNextAvailable : function(start){
37223         var items = this.items;
37224         var index = start;
37225         // look for a next tab that will slide over to
37226         // replace the one being removed
37227         while(index < items.length){
37228             var item = items[++index];
37229             if(item && !item.isHidden()){
37230                 return item;
37231             }
37232         }
37233         // if one isn't found select the previous tab (on the left)
37234         index = start;
37235         while(index >= 0){
37236             var item = items[--index];
37237             if(item && !item.isHidden()){
37238                 return item;
37239             }
37240         }
37241         return null;
37242     },
37243
37244     /**
37245      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37246      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37247      */
37248     disableTab : function(id){
37249         var tab = this.items[id];
37250         if(tab && this.active != tab){
37251             tab.disable();
37252         }
37253     },
37254
37255     /**
37256      * Enables a {@link Roo.TabPanelItem} that is disabled.
37257      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37258      */
37259     enableTab : function(id){
37260         var tab = this.items[id];
37261         tab.enable();
37262     },
37263
37264     /**
37265      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37266      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37267      * @return {Roo.TabPanelItem} The TabPanelItem.
37268      */
37269     activate : function(id){
37270         var tab = this.items[id];
37271         if(!tab){
37272             return null;
37273         }
37274         if(tab == this.active || tab.disabled){
37275             return tab;
37276         }
37277         var e = {};
37278         this.fireEvent("beforetabchange", this, e, tab);
37279         if(e.cancel !== true && !tab.disabled){
37280             if(this.active){
37281                 this.active.hide();
37282             }
37283             this.active = this.items[id];
37284             this.active.show();
37285             this.fireEvent("tabchange", this, this.active);
37286         }
37287         return tab;
37288     },
37289
37290     /**
37291      * Gets the active {@link Roo.TabPanelItem}.
37292      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37293      */
37294     getActiveTab : function(){
37295         return this.active;
37296     },
37297
37298     /**
37299      * Updates the tab body element to fit the height of the container element
37300      * for overflow scrolling
37301      * @param {Number} targetHeight (optional) Override the starting height from the elements height
37302      */
37303     syncHeight : function(targetHeight){
37304         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37305         var bm = this.bodyEl.getMargins();
37306         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37307         this.bodyEl.setHeight(newHeight);
37308         return newHeight;
37309     },
37310
37311     onResize : function(){
37312         if(this.monitorResize){
37313             this.autoSizeTabs();
37314         }
37315     },
37316
37317     /**
37318      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37319      */
37320     beginUpdate : function(){
37321         this.updating = true;
37322     },
37323
37324     /**
37325      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37326      */
37327     endUpdate : function(){
37328         this.updating = false;
37329         this.autoSizeTabs();
37330     },
37331
37332     /**
37333      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37334      */
37335     autoSizeTabs : function(){
37336         var count = this.items.length;
37337         var vcount = count - this.hiddenCount;
37338         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37339             return;
37340         }
37341         var w = Math.max(this.el.getWidth() - this.cpad, 10);
37342         var availWidth = Math.floor(w / vcount);
37343         var b = this.stripBody;
37344         if(b.getWidth() > w){
37345             var tabs = this.items;
37346             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37347             if(availWidth < this.minTabWidth){
37348                 /*if(!this.sleft){    // incomplete scrolling code
37349                     this.createScrollButtons();
37350                 }
37351                 this.showScroll();
37352                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37353             }
37354         }else{
37355             if(this.currentTabWidth < this.preferredTabWidth){
37356                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37357             }
37358         }
37359     },
37360
37361     /**
37362      * Returns the number of tabs in this TabPanel.
37363      * @return {Number}
37364      */
37365      getCount : function(){
37366          return this.items.length;
37367      },
37368
37369     /**
37370      * Resizes all the tabs to the passed width
37371      * @param {Number} The new width
37372      */
37373     setTabWidth : function(width){
37374         this.currentTabWidth = width;
37375         for(var i = 0, len = this.items.length; i < len; i++) {
37376                 if(!this.items[i].isHidden()) {
37377                 this.items[i].setWidth(width);
37378             }
37379         }
37380     },
37381
37382     /**
37383      * Destroys this TabPanel
37384      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37385      */
37386     destroy : function(removeEl){
37387         Roo.EventManager.removeResizeListener(this.onResize, this);
37388         for(var i = 0, len = this.items.length; i < len; i++){
37389             this.items[i].purgeListeners();
37390         }
37391         if(removeEl === true){
37392             this.el.update("");
37393             this.el.remove();
37394         }
37395     },
37396     
37397     createStrip : function(container)
37398     {
37399         var strip = document.createElement("nav");
37400         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37401         container.appendChild(strip);
37402         return strip;
37403     },
37404     
37405     createStripList : function(strip)
37406     {
37407         // div wrapper for retard IE
37408         // returns the "tr" element.
37409         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
37410         //'<div class="x-tabs-strip-wrap">'+
37411           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
37412           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
37413         return strip.firstChild; //.firstChild.firstChild.firstChild;
37414     },
37415     createBody : function(container)
37416     {
37417         var body = document.createElement("div");
37418         Roo.id(body, "tab-body");
37419         //Roo.fly(body).addClass("x-tabs-body");
37420         Roo.fly(body).addClass("tab-content");
37421         container.appendChild(body);
37422         return body;
37423     },
37424     createItemBody :function(bodyEl, id){
37425         var body = Roo.getDom(id);
37426         if(!body){
37427             body = document.createElement("div");
37428             body.id = id;
37429         }
37430         //Roo.fly(body).addClass("x-tabs-item-body");
37431         Roo.fly(body).addClass("tab-pane");
37432          bodyEl.insertBefore(body, bodyEl.firstChild);
37433         return body;
37434     },
37435     /** @private */
37436     createStripElements :  function(stripEl, text, closable, tpl)
37437     {
37438         var td = document.createElement("li"); // was td..
37439         
37440         
37441         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
37442         
37443         
37444         stripEl.appendChild(td);
37445         /*if(closable){
37446             td.className = "x-tabs-closable";
37447             if(!this.closeTpl){
37448                 this.closeTpl = new Roo.Template(
37449                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37450                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
37451                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
37452                 );
37453             }
37454             var el = this.closeTpl.overwrite(td, {"text": text});
37455             var close = el.getElementsByTagName("div")[0];
37456             var inner = el.getElementsByTagName("em")[0];
37457             return {"el": el, "close": close, "inner": inner};
37458         } else {
37459         */
37460         // not sure what this is..
37461 //            if(!this.tabTpl){
37462                 //this.tabTpl = new Roo.Template(
37463                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37464                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
37465                 //);
37466 //                this.tabTpl = new Roo.Template(
37467 //                   '<a href="#">' +
37468 //                   '<span unselectable="on"' +
37469 //                            (this.disableTooltips ? '' : ' title="{text}"') +
37470 //                            ' >{text}</span></a>'
37471 //                );
37472 //                
37473 //            }
37474
37475
37476             var template = tpl || this.tabTpl || false;
37477             
37478             if(!template){
37479                 
37480                 template = new Roo.Template(
37481                    '<a href="#">' +
37482                    '<span unselectable="on"' +
37483                             (this.disableTooltips ? '' : ' title="{text}"') +
37484                             ' >{text}</span></a>'
37485                 );
37486             }
37487             
37488             switch (typeof(template)) {
37489                 case 'object' :
37490                     break;
37491                 case 'string' :
37492                     template = new Roo.Template(template);
37493                     break;
37494                 default :
37495                     break;
37496             }
37497             
37498             var el = template.overwrite(td, {"text": text});
37499             
37500             var inner = el.getElementsByTagName("span")[0];
37501             
37502             return {"el": el, "inner": inner};
37503             
37504     }
37505         
37506     
37507 });
37508
37509 /**
37510  * @class Roo.TabPanelItem
37511  * @extends Roo.util.Observable
37512  * Represents an individual item (tab plus body) in a TabPanel.
37513  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
37514  * @param {String} id The id of this TabPanelItem
37515  * @param {String} text The text for the tab of this TabPanelItem
37516  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
37517  */
37518 Roo.bootstrap.panel.TabItem = function(config){
37519     /**
37520      * The {@link Roo.TabPanel} this TabPanelItem belongs to
37521      * @type Roo.TabPanel
37522      */
37523     this.tabPanel = config.panel;
37524     /**
37525      * The id for this TabPanelItem
37526      * @type String
37527      */
37528     this.id = config.id;
37529     /** @private */
37530     this.disabled = false;
37531     /** @private */
37532     this.text = config.text;
37533     /** @private */
37534     this.loaded = false;
37535     this.closable = config.closable;
37536
37537     /**
37538      * The body element for this TabPanelItem.
37539      * @type Roo.Element
37540      */
37541     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
37542     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
37543     this.bodyEl.setStyle("display", "block");
37544     this.bodyEl.setStyle("zoom", "1");
37545     //this.hideAction();
37546
37547     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
37548     /** @private */
37549     this.el = Roo.get(els.el);
37550     this.inner = Roo.get(els.inner, true);
37551     this.textEl = Roo.get(this.el.dom.firstChild, true);
37552     this.pnode = Roo.get(els.el.parentNode, true);
37553     this.el.on("mousedown", this.onTabMouseDown, this);
37554     this.el.on("click", this.onTabClick, this);
37555     /** @private */
37556     if(config.closable){
37557         var c = Roo.get(els.close, true);
37558         c.dom.title = this.closeText;
37559         c.addClassOnOver("close-over");
37560         c.on("click", this.closeClick, this);
37561      }
37562
37563     this.addEvents({
37564          /**
37565          * @event activate
37566          * Fires when this tab becomes the active tab.
37567          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37568          * @param {Roo.TabPanelItem} this
37569          */
37570         "activate": true,
37571         /**
37572          * @event beforeclose
37573          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
37574          * @param {Roo.TabPanelItem} this
37575          * @param {Object} e Set cancel to true on this object to cancel the close.
37576          */
37577         "beforeclose": true,
37578         /**
37579          * @event close
37580          * Fires when this tab is closed.
37581          * @param {Roo.TabPanelItem} this
37582          */
37583          "close": true,
37584         /**
37585          * @event deactivate
37586          * Fires when this tab is no longer the active tab.
37587          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37588          * @param {Roo.TabPanelItem} this
37589          */
37590          "deactivate" : true
37591     });
37592     this.hidden = false;
37593
37594     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
37595 };
37596
37597 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
37598            {
37599     purgeListeners : function(){
37600        Roo.util.Observable.prototype.purgeListeners.call(this);
37601        this.el.removeAllListeners();
37602     },
37603     /**
37604      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
37605      */
37606     show : function(){
37607         this.pnode.addClass("active");
37608         this.showAction();
37609         if(Roo.isOpera){
37610             this.tabPanel.stripWrap.repaint();
37611         }
37612         this.fireEvent("activate", this.tabPanel, this);
37613     },
37614
37615     /**
37616      * Returns true if this tab is the active tab.
37617      * @return {Boolean}
37618      */
37619     isActive : function(){
37620         return this.tabPanel.getActiveTab() == this;
37621     },
37622
37623     /**
37624      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
37625      */
37626     hide : function(){
37627         this.pnode.removeClass("active");
37628         this.hideAction();
37629         this.fireEvent("deactivate", this.tabPanel, this);
37630     },
37631
37632     hideAction : function(){
37633         this.bodyEl.hide();
37634         this.bodyEl.setStyle("position", "absolute");
37635         this.bodyEl.setLeft("-20000px");
37636         this.bodyEl.setTop("-20000px");
37637     },
37638
37639     showAction : function(){
37640         this.bodyEl.setStyle("position", "relative");
37641         this.bodyEl.setTop("");
37642         this.bodyEl.setLeft("");
37643         this.bodyEl.show();
37644     },
37645
37646     /**
37647      * Set the tooltip for the tab.
37648      * @param {String} tooltip The tab's tooltip
37649      */
37650     setTooltip : function(text){
37651         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
37652             this.textEl.dom.qtip = text;
37653             this.textEl.dom.removeAttribute('title');
37654         }else{
37655             this.textEl.dom.title = text;
37656         }
37657     },
37658
37659     onTabClick : function(e){
37660         e.preventDefault();
37661         this.tabPanel.activate(this.id);
37662     },
37663
37664     onTabMouseDown : function(e){
37665         e.preventDefault();
37666         this.tabPanel.activate(this.id);
37667     },
37668 /*
37669     getWidth : function(){
37670         return this.inner.getWidth();
37671     },
37672
37673     setWidth : function(width){
37674         var iwidth = width - this.pnode.getPadding("lr");
37675         this.inner.setWidth(iwidth);
37676         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
37677         this.pnode.setWidth(width);
37678     },
37679 */
37680     /**
37681      * Show or hide the tab
37682      * @param {Boolean} hidden True to hide or false to show.
37683      */
37684     setHidden : function(hidden){
37685         this.hidden = hidden;
37686         this.pnode.setStyle("display", hidden ? "none" : "");
37687     },
37688
37689     /**
37690      * Returns true if this tab is "hidden"
37691      * @return {Boolean}
37692      */
37693     isHidden : function(){
37694         return this.hidden;
37695     },
37696
37697     /**
37698      * Returns the text for this tab
37699      * @return {String}
37700      */
37701     getText : function(){
37702         return this.text;
37703     },
37704     /*
37705     autoSize : function(){
37706         //this.el.beginMeasure();
37707         this.textEl.setWidth(1);
37708         /*
37709          *  #2804 [new] Tabs in Roojs
37710          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
37711          */
37712         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
37713         //this.el.endMeasure();
37714     //},
37715
37716     /**
37717      * Sets the text for the tab (Note: this also sets the tooltip text)
37718      * @param {String} text The tab's text and tooltip
37719      */
37720     setText : function(text){
37721         this.text = text;
37722         this.textEl.update(text);
37723         this.setTooltip(text);
37724         //if(!this.tabPanel.resizeTabs){
37725         //    this.autoSize();
37726         //}
37727     },
37728     /**
37729      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
37730      */
37731     activate : function(){
37732         this.tabPanel.activate(this.id);
37733     },
37734
37735     /**
37736      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
37737      */
37738     disable : function(){
37739         if(this.tabPanel.active != this){
37740             this.disabled = true;
37741             this.pnode.addClass("disabled");
37742         }
37743     },
37744
37745     /**
37746      * Enables this TabPanelItem if it was previously disabled.
37747      */
37748     enable : function(){
37749         this.disabled = false;
37750         this.pnode.removeClass("disabled");
37751     },
37752
37753     /**
37754      * Sets the content for this TabPanelItem.
37755      * @param {String} content The content
37756      * @param {Boolean} loadScripts true to look for and load scripts
37757      */
37758     setContent : function(content, loadScripts){
37759         this.bodyEl.update(content, loadScripts);
37760     },
37761
37762     /**
37763      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
37764      * @return {Roo.UpdateManager} The UpdateManager
37765      */
37766     getUpdateManager : function(){
37767         return this.bodyEl.getUpdateManager();
37768     },
37769
37770     /**
37771      * Set a URL to be used to load the content for this TabPanelItem.
37772      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
37773      * @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)
37774      * @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)
37775      * @return {Roo.UpdateManager} The UpdateManager
37776      */
37777     setUrl : function(url, params, loadOnce){
37778         if(this.refreshDelegate){
37779             this.un('activate', this.refreshDelegate);
37780         }
37781         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37782         this.on("activate", this.refreshDelegate);
37783         return this.bodyEl.getUpdateManager();
37784     },
37785
37786     /** @private */
37787     _handleRefresh : function(url, params, loadOnce){
37788         if(!loadOnce || !this.loaded){
37789             var updater = this.bodyEl.getUpdateManager();
37790             updater.update(url, params, this._setLoaded.createDelegate(this));
37791         }
37792     },
37793
37794     /**
37795      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
37796      *   Will fail silently if the setUrl method has not been called.
37797      *   This does not activate the panel, just updates its content.
37798      */
37799     refresh : function(){
37800         if(this.refreshDelegate){
37801            this.loaded = false;
37802            this.refreshDelegate();
37803         }
37804     },
37805
37806     /** @private */
37807     _setLoaded : function(){
37808         this.loaded = true;
37809     },
37810
37811     /** @private */
37812     closeClick : function(e){
37813         var o = {};
37814         e.stopEvent();
37815         this.fireEvent("beforeclose", this, o);
37816         if(o.cancel !== true){
37817             this.tabPanel.removeTab(this.id);
37818         }
37819     },
37820     /**
37821      * The text displayed in the tooltip for the close icon.
37822      * @type String
37823      */
37824     closeText : "Close this tab"
37825 });
37826 /*
37827  * - LGPL
37828  *
37829  * element
37830  * 
37831  */
37832
37833 /**
37834  * @class Roo.bootstrap.PhoneInput
37835  * @extends Roo.bootstrap.TriggerField
37836  * Bootstrap PhoneInput class
37837  * 
37838  * @constructor
37839  * Create a new PhoneInput
37840  * @param {Object} config The config object
37841 */
37842
37843 Roo.bootstrap.PhoneInput = function(config){
37844     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
37845     this.addEvents({
37846         
37847     });
37848     
37849     //setting global...
37850     this.item = [];
37851     this.tickItems = [];
37852     
37853     this.selectedIndex = -1;
37854     if(this.mode == 'local'){
37855         if(config.queryDelay === undefined){
37856             this.queryDelay = 10;
37857         }
37858         if(config.minChars === undefined){
37859             this.minChars = 0;
37860         }
37861     }
37862 };
37863
37864 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
37865      
37866      //setting properties..
37867     
37868     getAutoCreate : function()
37869     {   
37870         var cfg = false;
37871         //render
37872         /*
37873          * Render classic select for iso
37874          */
37875         
37876         if(Roo.isIOS && this.useNativeIOS){
37877             cfg = this.getAutoCreateNativeIOS();
37878             return cfg;
37879         }
37880         
37881         /*
37882          * Touch Devices
37883          */
37884         
37885         if(Roo.isTouch && this.mobileTouchView){
37886             cfg = this.getAutoCreateTouchView();
37887             return cfg;;
37888         }
37889         
37890         /*
37891          *  Normal PhoneInput
37892          */
37893         if(!this.tickable){
37894             cfg = Roo.bootstrap.PhoneInput.superclass.getAutoCreate.call(this);
37895             if(this.name == 'info_year_invest_id_display_name'){
37896                 Roo.log('cfg.................................................');
37897                 Roo.log(cfg);
37898             }
37899             return cfg;
37900         }
37901         
37902         /*
37903          *  PhoneInput with tickable selections
37904          */
37905              
37906         var align = this.labelAlign || this.parentLabelAlign();
37907         
37908         cfg = {
37909             cls : 'form-group roo-PhoneInput-tickable' //input-group
37910         };
37911         
37912         var btn_text_select = '';
37913         var btn_text_done = '';
37914         var btn_text_cancel = '';
37915         
37916         if (this.btn_text_show) {
37917             btn_text_select = 'Select';
37918             btn_text_done = 'Done';
37919             btn_text_cancel = 'Cancel'; 
37920         }
37921         
37922         var buttons = {
37923             tag : 'div',
37924             cls : 'tickable-buttons',
37925             cn : [
37926                 {
37927                     tag : 'button',
37928                     type : 'button',
37929                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
37930                     //html : this.triggerText
37931                     html: btn_text_select
37932                 },
37933                 {
37934                     tag : 'button',
37935                     type : 'button',
37936                     name : 'ok',
37937                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
37938                     //html : 'Done'
37939                     html: btn_text_done
37940                 },
37941                 {
37942                     tag : 'button',
37943                     type : 'button',
37944                     name : 'cancel',
37945                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
37946                     //html : 'Cancel'
37947                     html: btn_text_cancel
37948                 }
37949             ]
37950         };
37951         
37952         if(this.editable){
37953             buttons.cn.unshift({
37954                 tag: 'input',
37955                 cls: 'roo-select2-search-field-input'
37956             });
37957         }
37958         
37959         var _this = this;
37960         
37961         Roo.each(buttons.cn, function(c){
37962             if (_this.size) {
37963                 c.cls += ' btn-' + _this.size;
37964             }
37965
37966             if (_this.disabled) {
37967                 c.disabled = true;
37968             }
37969         });
37970         
37971         var box = {
37972             tag: 'div',
37973             cn: [
37974                 {
37975                     tag: 'input',
37976                     type : 'hidden',
37977                     cls: 'form-hidden-field'
37978                 },
37979                 {
37980                     tag: 'ul',
37981                     cls: 'roo-select2-choices',
37982                     cn:[
37983                         {
37984                             tag: 'li',
37985                             cls: 'roo-select2-search-field',
37986                             cn: [
37987                                 buttons
37988                             ]
37989                         }
37990                     ]
37991                 }
37992             ]
37993         };
37994         
37995         var PhoneInput = {
37996             cls: 'roo-select2-container input-group roo-select2-container-multi',
37997             cn: [
37998                 box
37999 //                {
38000 //                    tag: 'ul',
38001 //                    cls: 'typeahead typeahead-long dropdown-menu',
38002 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
38003 //                }
38004             ]
38005         };
38006         
38007         if(this.hasFeedback && !this.allowBlank){
38008             
38009             var feedback = {
38010                 tag: 'span',
38011                 cls: 'glyphicon form-control-feedback'
38012             };
38013
38014             PhoneInput.cn.push(feedback);
38015         }
38016         
38017         
38018         if (align ==='left' && this.fieldLabel.length) {
38019             
38020             cfg.cls += ' roo-form-group-label-left';
38021             
38022             cfg.cn = [
38023                 {
38024                     tag : 'i',
38025                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
38026                     tooltip : 'This field is required'
38027                 },
38028                 {
38029                     tag: 'label',
38030                     'for' :  id,
38031                     cls : 'control-label',
38032                     html : this.fieldLabel
38033
38034                 },
38035                 {
38036                     cls : "", 
38037                     cn: [
38038                         PhoneInput
38039                     ]
38040                 }
38041
38042             ];
38043             
38044             var labelCfg = cfg.cn[1];
38045             var contentCfg = cfg.cn[2];
38046             
38047
38048             if(this.indicatorpos == 'right'){
38049                 
38050                 cfg.cn = [
38051                     {
38052                         tag: 'label',
38053                         'for' :  id,
38054                         cls : 'control-label',
38055                         cn : [
38056                             {
38057                                 tag : 'span',
38058                                 html : this.fieldLabel
38059                             },
38060                             {
38061                                 tag : 'i',
38062                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
38063                                 tooltip : 'This field is required'
38064                             }
38065                         ]
38066                     },
38067                     {
38068                         cls : "",
38069                         cn: [
38070                             PhoneInput
38071                         ]
38072                     }
38073
38074                 ];
38075                 
38076                 
38077                 
38078                 labelCfg = cfg.cn[0];
38079                 contentCfg = cfg.cn[1];
38080             
38081             }
38082             
38083             if(this.labelWidth > 12){
38084                 labelCfg.style = "width: " + this.labelWidth + 'px';
38085             }
38086             
38087             if(this.labelWidth < 13 && this.labelmd == 0){
38088                 this.labelmd = this.labelWidth;
38089             }
38090             
38091             if(this.labellg > 0){
38092                 labelCfg.cls += ' col-lg-' + this.labellg;
38093                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
38094             }
38095             
38096             if(this.labelmd > 0){
38097                 labelCfg.cls += ' col-md-' + this.labelmd;
38098                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
38099             }
38100             
38101             if(this.labelsm > 0){
38102                 labelCfg.cls += ' col-sm-' + this.labelsm;
38103                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
38104             }
38105             
38106             if(this.labelxs > 0){
38107                 labelCfg.cls += ' col-xs-' + this.labelxs;
38108                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
38109             }
38110                 
38111                 
38112         } else if ( this.fieldLabel.length) {
38113 //                Roo.log(" label");
38114                  cfg.cn = [
38115                     {
38116                         tag : 'i',
38117                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
38118                         tooltip : 'This field is required'
38119                     },
38120                     {
38121                         tag: 'label',
38122                         //cls : 'input-group-addon',
38123                         html : this.fieldLabel
38124                     },
38125                     PhoneInput
38126                 ];
38127                 
38128                 if(this.indicatorpos == 'right'){
38129                     Roo.log('hidden name:'+this.hiddenName);
38130                     cfg.cn = [
38131                         {
38132                             tag: 'label',
38133                             //cls : 'input-group-addon',
38134                             html : this.fieldLabel
38135                         },
38136                         {
38137                             tag : 'i',
38138                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
38139                             tooltip : 'This field is required'
38140                         },
38141                         PhoneInput
38142                     ];
38143                     
38144                 }
38145
38146         } else {
38147             
38148 //                Roo.log(" no label && no align");
38149                 cfg = PhoneInput
38150                      
38151                 
38152         }
38153          
38154         var settings=this;
38155         ['xs','sm','md','lg'].map(function(size){
38156             if (settings[size]) {
38157                 cfg.cls += ' col-' + size + '-' + settings[size];
38158             }
38159         });
38160         
38161         return cfg;
38162         
38163     },
38164     
38165     _initEventsCalled : false,
38166     
38167     // private
38168     initEvents: function()
38169     {   
38170         if (this._initEventsCalled) { // as we call render... prevent looping...
38171             return;
38172         }
38173         this._initEventsCalled = true;
38174         
38175         if (!this.store) {
38176             throw "can not find store for combo";
38177         }
38178         
38179         this.store = Roo.factory(this.store, Roo.data);
38180         this.store.parent = this;
38181         
38182         // if we are building from html. then this element is so complex, that we can not really
38183         // use the rendered HTML.
38184         // so we have to trash and replace the previous code.
38185         if (Roo.XComponent.build_from_html) {
38186             
38187             // remove this element....
38188             var e = this.el.dom, k=0;
38189             while (e ) { e = e.previousSibling;  ++k;}
38190
38191             this.el.remove();
38192             
38193             this.el=false;
38194             this.rendered = false;
38195             
38196             this.render(this.parent().getChildContainer(true), k);
38197             
38198             
38199             
38200         }
38201         
38202         if(Roo.isIOS && this.useNativeIOS){
38203             this.initIOSView();
38204             return;
38205         }
38206         
38207         /*
38208          * Touch Devices
38209          */
38210         
38211         if(Roo.isTouch && this.mobileTouchView){
38212             this.initTouchView();
38213             return;
38214         }
38215         
38216         if(this.tickable){
38217             this.initTickableEvents();
38218             return;
38219         }
38220         
38221         Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
38222         
38223         if(this.hiddenName){
38224             
38225             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
38226             
38227             this.hiddenField.dom.value =
38228                 this.hiddenValue !== undefined ? this.hiddenValue :
38229                 this.value !== undefined ? this.value : '';
38230
38231             // prevent input submission
38232             this.el.dom.removeAttribute('name');
38233             this.hiddenField.dom.setAttribute('name', this.hiddenName);
38234              
38235              
38236         }
38237         //if(Roo.isGecko){
38238         //    this.el.dom.setAttribute('autocomplete', 'off');
38239         //}
38240         
38241         var cls = 'x-combo-list';
38242         
38243         //this.list = new Roo.Layer({
38244         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
38245         //});
38246         
38247         var _this = this;
38248         
38249         (function(){
38250             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
38251             _this.list.setWidth(lw);
38252         }).defer(100);
38253         
38254         this.list.on('mouseover', this.onViewOver, this);
38255         this.list.on('mousemove', this.onViewMove, this);
38256         
38257         this.list.on('scroll', this.onViewScroll, this);
38258         
38259         /*
38260         this.list.swallowEvent('mousewheel');
38261         this.assetHeight = 0;
38262
38263         if(this.title){
38264             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
38265             this.assetHeight += this.header.getHeight();
38266         }
38267
38268         this.innerList = this.list.createChild({cls:cls+'-inner'});
38269         this.innerList.on('mouseover', this.onViewOver, this);
38270         this.innerList.on('mousemove', this.onViewMove, this);
38271         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
38272         
38273         if(this.allowBlank && !this.pageSize && !this.disableClear){
38274             this.footer = this.list.createChild({cls:cls+'-ft'});
38275             this.pageTb = new Roo.Toolbar(this.footer);
38276            
38277         }
38278         if(this.pageSize){
38279             this.footer = this.list.createChild({cls:cls+'-ft'});
38280             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
38281                     {pageSize: this.pageSize});
38282             
38283         }
38284         
38285         if (this.pageTb && this.allowBlank && !this.disableClear) {
38286             var _this = this;
38287             this.pageTb.add(new Roo.Toolbar.Fill(), {
38288                 cls: 'x-btn-icon x-btn-clear',
38289                 text: '&#160;',
38290                 handler: function()
38291                 {
38292                     _this.collapse();
38293                     _this.clearValue();
38294                     _this.onSelect(false, -1);
38295                 }
38296             });
38297         }
38298         if (this.footer) {
38299             this.assetHeight += this.footer.getHeight();
38300         }
38301         */
38302             
38303         if(!this.tpl){
38304             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
38305         }
38306
38307         this.view = new Roo.View(this.list, this.tpl, {
38308             singleSelect:true, store: this.store, selectedClass: this.selectedClass
38309         });
38310         //this.view.wrapEl.setDisplayed(false);
38311         this.view.on('click', this.onViewClick, this);
38312         
38313         
38314         this.store.on('beforeload', this.onBeforeLoad, this);
38315         this.store.on('load', this.onLoad, this);
38316         this.store.on('loadexception', this.onLoadException, this);
38317         /*
38318         if(this.resizable){
38319             this.resizer = new Roo.Resizable(this.list,  {
38320                pinned:true, handles:'se'
38321             });
38322             this.resizer.on('resize', function(r, w, h){
38323                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
38324                 this.listWidth = w;
38325                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
38326                 this.restrictHeight();
38327             }, this);
38328             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
38329         }
38330         */
38331         if(!this.editable){
38332             this.editable = true;
38333             this.setEditable(false);
38334         }
38335         
38336         /*
38337         
38338         if (typeof(this.events.add.listeners) != 'undefined') {
38339             
38340             this.addicon = this.wrap.createChild(
38341                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
38342        
38343             this.addicon.on('click', function(e) {
38344                 this.fireEvent('add', this);
38345             }, this);
38346         }
38347         if (typeof(this.events.edit.listeners) != 'undefined') {
38348             
38349             this.editicon = this.wrap.createChild(
38350                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
38351             if (this.addicon) {
38352                 this.editicon.setStyle('margin-left', '40px');
38353             }
38354             this.editicon.on('click', function(e) {
38355                 
38356                 // we fire even  if inothing is selected..
38357                 this.fireEvent('edit', this, this.lastData );
38358                 
38359             }, this);
38360         }
38361         */
38362         
38363         this.keyNav = new Roo.KeyNav(this.inputEl(), {
38364             "up" : function(e){
38365                 this.inKeyMode = true;
38366                 this.selectPrev();
38367             },
38368
38369             "down" : function(e){
38370                 if(!this.isExpanded()){
38371                     this.onTriggerClick();
38372                 }else{
38373                     this.inKeyMode = true;
38374                     this.selectNext();
38375                 }
38376             },
38377
38378             "enter" : function(e){
38379 //                this.onViewClick();
38380                 //return true;
38381                 this.collapse();
38382                 
38383                 if(this.fireEvent("specialkey", this, e)){
38384                     this.onViewClick(false);
38385                 }
38386                 
38387                 return true;
38388             },
38389
38390             "esc" : function(e){
38391                 this.collapse();
38392             },
38393
38394             "tab" : function(e){
38395                 this.collapse();
38396                 
38397                 if(this.fireEvent("specialkey", this, e)){
38398                     this.onViewClick(false);
38399                 }
38400                 
38401                 return true;
38402             },
38403
38404             scope : this,
38405
38406             doRelay : function(foo, bar, hname){
38407                 if(hname == 'down' || this.scope.isExpanded()){
38408                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
38409                 }
38410                 return true;
38411             },
38412
38413             forceKeyDown: true
38414         });
38415         
38416         
38417         this.queryDelay = Math.max(this.queryDelay || 10,
38418                 this.mode == 'local' ? 10 : 250);
38419         
38420         
38421         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
38422         
38423         if(this.typeAhead){
38424             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
38425         }
38426         if(this.editable !== false){
38427             this.inputEl().on("keyup", this.onKeyUp, this);
38428         }
38429         if(this.forceSelection){
38430             this.inputEl().on('blur', this.doForce, this);
38431         }
38432         
38433         if(this.multiple){
38434             this.choices = this.el.select('ul.roo-select2-choices', true).first();
38435             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
38436         }
38437     },
38438     
38439     initTickableEvents: function()
38440     {   
38441         this.createList();
38442         
38443         if(this.hiddenName){
38444             
38445             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
38446             
38447             this.hiddenField.dom.value =
38448                 this.hiddenValue !== undefined ? this.hiddenValue :
38449                 this.value !== undefined ? this.value : '';
38450
38451             // prevent input submission
38452             this.el.dom.removeAttribute('name');
38453             this.hiddenField.dom.setAttribute('name', this.hiddenName);
38454              
38455              
38456         }
38457         
38458 //        this.list = this.el.select('ul.dropdown-menu',true).first();
38459         
38460         this.choices = this.el.select('ul.roo-select2-choices', true).first();
38461         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
38462         if(this.triggerList){
38463             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
38464         }
38465          
38466         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
38467         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
38468         
38469         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
38470         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
38471         
38472         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
38473         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
38474         
38475         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
38476         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
38477         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
38478         
38479         this.okBtn.hide();
38480         this.cancelBtn.hide();
38481         
38482         var _this = this;
38483         
38484         (function(){
38485             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
38486             _this.list.setWidth(lw);
38487         }).defer(100);
38488         
38489         this.list.on('mouseover', this.onViewOver, this);
38490         this.list.on('mousemove', this.onViewMove, this);
38491         
38492         this.list.on('scroll', this.onViewScroll, this);
38493         
38494         if(!this.tpl){
38495             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>';
38496         }
38497
38498         this.view = new Roo.View(this.list, this.tpl, {
38499             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
38500         });
38501         
38502         //this.view.wrapEl.setDisplayed(false);
38503         this.view.on('click', this.onViewClick, this);
38504         
38505         
38506         
38507         this.store.on('beforeload', this.onBeforeLoad, this);
38508         this.store.on('load', this.onLoad, this);
38509         this.store.on('loadexception', this.onLoadException, this);
38510         
38511         if(this.editable){
38512             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
38513                 "up" : function(e){
38514                     this.inKeyMode = true;
38515                     this.selectPrev();
38516                 },
38517
38518                 "down" : function(e){
38519                     this.inKeyMode = true;
38520                     this.selectNext();
38521                 },
38522
38523                 "enter" : function(e){
38524                     if(this.fireEvent("specialkey", this, e)){
38525                         this.onViewClick(false);
38526                     }
38527                     
38528                     return true;
38529                 },
38530
38531                 "esc" : function(e){
38532                     this.onTickableFooterButtonClick(e, false, false);
38533                 },
38534
38535                 "tab" : function(e){
38536                     this.fireEvent("specialkey", this, e);
38537                     
38538                     this.onTickableFooterButtonClick(e, false, false);
38539                     
38540                     return true;
38541                 },
38542
38543                 scope : this,
38544
38545                 doRelay : function(e, fn, key){
38546                     if(this.scope.isExpanded()){
38547                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
38548                     }
38549                     return true;
38550                 },
38551
38552                 forceKeyDown: true
38553             });
38554         }
38555         
38556         this.queryDelay = Math.max(this.queryDelay || 10,
38557                 this.mode == 'local' ? 10 : 250);
38558         
38559         
38560         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
38561         
38562         if(this.typeAhead){
38563             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
38564         }
38565         
38566         if(this.editable !== false){
38567             this.tickableInputEl().on("keyup", this.onKeyUp, this);
38568         }
38569         
38570         this.indicator = this.indicatorEl();
38571         
38572         if(this.indicator){
38573             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
38574             this.indicator.hide();
38575         }
38576         
38577     },
38578
38579     onDestroy : function(){
38580         if(this.view){
38581             this.view.setStore(null);
38582             this.view.el.removeAllListeners();
38583             this.view.el.remove();
38584             this.view.purgeListeners();
38585         }
38586         if(this.list){
38587             this.list.dom.innerHTML  = '';
38588         }
38589         
38590         if(this.store){
38591             this.store.un('beforeload', this.onBeforeLoad, this);
38592             this.store.un('load', this.onLoad, this);
38593             this.store.un('loadexception', this.onLoadException, this);
38594         }
38595         Roo.bootstrap.PhoneInput.superclass.onDestroy.call(this);
38596     },
38597
38598     // private
38599     fireKey : function(e){
38600         if(e.isNavKeyPress() && !this.list.isVisible()){
38601             this.fireEvent("specialkey", this, e);
38602         }
38603     },
38604
38605     // private
38606     onResize: function(w, h){
38607 //        Roo.bootstrap.PhoneInput.superclass.onResize.apply(this, arguments);
38608 //        
38609 //        if(typeof w != 'number'){
38610 //            // we do not handle it!?!?
38611 //            return;
38612 //        }
38613 //        var tw = this.trigger.getWidth();
38614 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
38615 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
38616 //        var x = w - tw;
38617 //        this.inputEl().setWidth( this.adjustWidth('input', x));
38618 //            
38619 //        //this.trigger.setStyle('left', x+'px');
38620 //        
38621 //        if(this.list && this.listWidth === undefined){
38622 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
38623 //            this.list.setWidth(lw);
38624 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
38625 //        }
38626         
38627     
38628         
38629     },
38630
38631     /**
38632      * Allow or prevent the user from directly editing the field text.  If false is passed,
38633      * the user will only be able to select from the items defined in the dropdown list.  This method
38634      * is the runtime equivalent of setting the 'editable' config option at config time.
38635      * @param {Boolean} value True to allow the user to directly edit the field text
38636      */
38637     setEditable : function(value){
38638         if(value == this.editable){
38639             return;
38640         }
38641         this.editable = value;
38642         if(!value){
38643             this.inputEl().dom.setAttribute('readOnly', true);
38644             this.inputEl().on('mousedown', this.onTriggerClick,  this);
38645             this.inputEl().addClass('x-combo-noedit');
38646         }else{
38647             this.inputEl().dom.setAttribute('readOnly', false);
38648             this.inputEl().un('mousedown', this.onTriggerClick,  this);
38649             this.inputEl().removeClass('x-combo-noedit');
38650         }
38651     },
38652
38653     // private
38654     
38655     onBeforeLoad : function(combo,opts){
38656         if(!this.hasFocus){
38657             return;
38658         }
38659          if (!opts.add) {
38660             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
38661          }
38662         this.restrictHeight();
38663         this.selectedIndex = -1;
38664     },
38665
38666     // private
38667     onLoad : function(){
38668         
38669         this.hasQuery = false;
38670         
38671         if(!this.hasFocus){
38672             return;
38673         }
38674         
38675         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
38676             this.loading.hide();
38677         }
38678         
38679         if(this.store.getCount() > 0){
38680             
38681             this.expand();
38682             this.restrictHeight();
38683             if(this.lastQuery == this.allQuery){
38684                 if(this.editable && !this.tickable){
38685                     this.inputEl().dom.select();
38686                 }
38687                 
38688                 if(
38689                     !this.selectByValue(this.value, true) &&
38690                     this.autoFocus && 
38691                     (
38692                         !this.store.lastOptions ||
38693                         typeof(this.store.lastOptions.add) == 'undefined' || 
38694                         this.store.lastOptions.add != true
38695                     )
38696                 ){
38697                     this.select(0, true);
38698                 }
38699             }else{
38700                 if(this.autoFocus){
38701                     this.selectNext();
38702                 }
38703                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
38704                     this.taTask.delay(this.typeAheadDelay);
38705                 }
38706             }
38707         }else{
38708             this.onEmptyResults();
38709         }
38710         
38711         //this.el.focus();
38712     },
38713     // private
38714     onLoadException : function()
38715     {
38716         this.hasQuery = false;
38717         
38718         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
38719             this.loading.hide();
38720         }
38721         
38722         if(this.tickable && this.editable){
38723             return;
38724         }
38725         
38726         this.collapse();
38727         // only causes errors at present
38728         //Roo.log(this.store.reader.jsonData);
38729         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
38730             // fixme
38731             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
38732         //}
38733         
38734         
38735     },
38736     // private
38737     onTypeAhead : function(){
38738         if(this.store.getCount() > 0){
38739             var r = this.store.getAt(0);
38740             var newValue = r.data[this.displayField];
38741             var len = newValue.length;
38742             var selStart = this.getRawValue().length;
38743             
38744             if(selStart != len){
38745                 this.setRawValue(newValue);
38746                 this.selectText(selStart, newValue.length);
38747             }
38748         }
38749     },
38750
38751     // private
38752     onSelect : function(record, index){
38753         
38754         if(this.fireEvent('beforeselect', this, record, index) !== false){
38755         
38756             this.setFromData(index > -1 ? record.data : false);
38757             
38758             this.collapse();
38759             this.fireEvent('select', this, record, index);
38760         }
38761     },
38762
38763     /**
38764      * Returns the currently selected field value or empty string if no value is set.
38765      * @return {String} value The selected value
38766      */
38767     getValue : function()
38768     {
38769         if(Roo.isIOS && this.useNativeIOS){
38770             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
38771         }
38772         
38773         if(this.multiple){
38774             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
38775         }
38776         
38777         if(this.valueField){
38778             return typeof this.value != 'undefined' ? this.value : '';
38779         }else{
38780             return Roo.bootstrap.PhoneInput.superclass.getValue.call(this);
38781         }
38782     },
38783     
38784     getRawValue : function()
38785     {
38786         if(Roo.isIOS && this.useNativeIOS){
38787             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
38788         }
38789         
38790         var v = this.inputEl().getValue();
38791         
38792         return v;
38793     },
38794
38795     /**
38796      * Clears any text/value currently set in the field
38797      */
38798     clearValue : function(){
38799         
38800         if(this.hiddenField){
38801             this.hiddenField.dom.value = '';
38802         }
38803         this.value = '';
38804         this.setRawValue('');
38805         this.lastSelectionText = '';
38806         this.lastData = false;
38807         
38808         var close = this.closeTriggerEl();
38809         
38810         if(close){
38811             close.hide();
38812         }
38813         
38814         this.validate();
38815         
38816     },
38817
38818     /**
38819      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
38820      * will be displayed in the field.  If the value does not match the data value of an existing item,
38821      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
38822      * Otherwise the field will be blank (although the value will still be set).
38823      * @param {String} value The value to match
38824      */
38825     setValue : function(v)
38826     {
38827         if(Roo.isIOS && this.useNativeIOS){
38828             this.setIOSValue(v);
38829             return;
38830         }
38831         
38832         if(this.multiple){
38833             this.syncValue();
38834             return;
38835         }
38836         
38837         var text = v;
38838         if(this.valueField){
38839             var r = this.findRecord(this.valueField, v);
38840             if(r){
38841                 text = r.data[this.displayField];
38842             }else if(this.valueNotFoundText !== undefined){
38843                 text = this.valueNotFoundText;
38844             }
38845         }
38846         this.lastSelectionText = text;
38847         if(this.hiddenField){
38848             this.hiddenField.dom.value = v;
38849         }
38850         Roo.bootstrap.PhoneInput.superclass.setValue.call(this, text);
38851         this.value = v;
38852         
38853         var close = this.closeTriggerEl();
38854         
38855         if(close){
38856             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
38857         }
38858         
38859         this.validate();
38860     },
38861     /**
38862      * @property {Object} the last set data for the element
38863      */
38864     
38865     lastData : false,
38866     /**
38867      * Sets the value of the field based on a object which is related to the record format for the store.
38868      * @param {Object} value the value to set as. or false on reset?
38869      */
38870     setFromData : function(o){
38871         
38872         if(this.multiple){
38873             this.addItem(o);
38874             return;
38875         }
38876             
38877         var dv = ''; // display value
38878         var vv = ''; // value value..
38879         this.lastData = o;
38880         if (this.displayField) {
38881             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
38882         } else {
38883             // this is an error condition!!!
38884             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
38885         }
38886         
38887         if(this.valueField){
38888             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
38889         }
38890         
38891         var close = this.closeTriggerEl();
38892         
38893         if(close){
38894             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
38895         }
38896         
38897         if(this.hiddenField){
38898             this.hiddenField.dom.value = vv;
38899             
38900             this.lastSelectionText = dv;
38901             Roo.bootstrap.PhoneInput.superclass.setValue.call(this, dv);
38902             this.value = vv;
38903             return;
38904         }
38905         // no hidden field.. - we store the value in 'value', but still display
38906         // display field!!!!
38907         this.lastSelectionText = dv;
38908         Roo.bootstrap.PhoneInput.superclass.setValue.call(this, dv);
38909         this.value = vv;
38910         
38911         
38912         
38913     },
38914     // private
38915     reset : function(){
38916         // overridden so that last data is reset..
38917         
38918         if(this.multiple){
38919             this.clearItem();
38920             return;
38921         }
38922         
38923         this.setValue(this.originalValue);
38924         //this.clearInvalid();
38925         this.lastData = false;
38926         if (this.view) {
38927             this.view.clearSelections();
38928         }
38929         
38930         this.validate();
38931     },
38932     // private
38933     findRecord : function(prop, value){
38934         var record;
38935         if(this.store.getCount() > 0){
38936             this.store.each(function(r){
38937                 if(r.data[prop] == value){
38938                     record = r;
38939                     return false;
38940                 }
38941                 return true;
38942             });
38943         }
38944         return record;
38945     },
38946     
38947     getName: function()
38948     {
38949         // returns hidden if it's set..
38950         if (!this.rendered) {return ''};
38951         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
38952         
38953     },
38954     // private
38955     onViewMove : function(e, t){
38956         this.inKeyMode = false;
38957     },
38958
38959     // private
38960     onViewOver : function(e, t){
38961         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
38962             return;
38963         }
38964         var item = this.view.findItemFromChild(t);
38965         
38966         if(item){
38967             var index = this.view.indexOf(item);
38968             this.select(index, false);
38969         }
38970     },
38971
38972     // private
38973     onViewClick : function(view, doFocus, el, e)
38974     {
38975         var index = this.view.getSelectedIndexes()[0];
38976         
38977         var r = this.store.getAt(index);
38978         
38979         if(this.tickable){
38980             
38981             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
38982                 return;
38983             }
38984             
38985             var rm = false;
38986             var _this = this;
38987             
38988             Roo.each(this.tickItems, function(v,k){
38989                 
38990                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
38991                     Roo.log(v);
38992                     _this.tickItems.splice(k, 1);
38993                     
38994                     if(typeof(e) == 'undefined' && view == false){
38995                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
38996                     }
38997                     
38998                     rm = true;
38999                     return;
39000                 }
39001             });
39002             
39003             if(rm){
39004                 return;
39005             }
39006             
39007             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
39008                 this.tickItems.push(r.data);
39009             }
39010             
39011             if(typeof(e) == 'undefined' && view == false){
39012                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
39013             }
39014                     
39015             return;
39016         }
39017         
39018         if(r){
39019             this.onSelect(r, index);
39020         }
39021         if(doFocus !== false && !this.blockFocus){
39022             this.inputEl().focus();
39023         }
39024     },
39025
39026     // private
39027     restrictHeight : function(){
39028         //this.innerList.dom.style.height = '';
39029         //var inner = this.innerList.dom;
39030         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
39031         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
39032         //this.list.beginUpdate();
39033         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
39034         this.list.alignTo(this.inputEl(), this.listAlign);
39035         this.list.alignTo(this.inputEl(), this.listAlign);
39036         //this.list.endUpdate();
39037     },
39038
39039     // private
39040     onEmptyResults : function(){
39041         
39042         if(this.tickable && this.editable){
39043             this.restrictHeight();
39044             return;
39045         }
39046         
39047         this.collapse();
39048     },
39049
39050     /**
39051      * Returns true if the dropdown list is expanded, else false.
39052      */
39053     isExpanded : function(){
39054         return this.list.isVisible();
39055     },
39056
39057     /**
39058      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
39059      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
39060      * @param {String} value The data value of the item to select
39061      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
39062      * selected item if it is not currently in view (defaults to true)
39063      * @return {Boolean} True if the value matched an item in the list, else false
39064      */
39065     selectByValue : function(v, scrollIntoView){
39066         if(v !== undefined && v !== null){
39067             var r = this.findRecord(this.valueField || this.displayField, v);
39068             if(r){
39069                 this.select(this.store.indexOf(r), scrollIntoView);
39070                 return true;
39071             }
39072         }
39073         return false;
39074     },
39075
39076     /**
39077      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
39078      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
39079      * @param {Number} index The zero-based index of the list item to select
39080      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
39081      * selected item if it is not currently in view (defaults to true)
39082      */
39083     select : function(index, scrollIntoView){
39084         this.selectedIndex = index;
39085         this.view.select(index);
39086         if(scrollIntoView !== false){
39087             var el = this.view.getNode(index);
39088             /*
39089              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
39090              */
39091             if(el){
39092                 this.list.scrollChildIntoView(el, false);
39093             }
39094         }
39095     },
39096
39097     // private
39098     selectNext : function(){
39099         var ct = this.store.getCount();
39100         if(ct > 0){
39101             if(this.selectedIndex == -1){
39102                 this.select(0);
39103             }else if(this.selectedIndex < ct-1){
39104                 this.select(this.selectedIndex+1);
39105             }
39106         }
39107     },
39108
39109     // private
39110     selectPrev : function(){
39111         var ct = this.store.getCount();
39112         if(ct > 0){
39113             if(this.selectedIndex == -1){
39114                 this.select(0);
39115             }else if(this.selectedIndex != 0){
39116                 this.select(this.selectedIndex-1);
39117             }
39118         }
39119     },
39120
39121     // private
39122     onKeyUp : function(e){
39123         if(this.editable !== false && !e.isSpecialKey()){
39124             this.lastKey = e.getKey();
39125             this.dqTask.delay(this.queryDelay);
39126         }
39127     },
39128
39129     // private
39130     validateBlur : function(){
39131         return !this.list || !this.list.isVisible();   
39132     },
39133
39134     // private
39135     initQuery : function(){
39136         
39137         var v = this.getRawValue();
39138         
39139         if(this.tickable && this.editable){
39140             v = this.tickableInputEl().getValue();
39141         }
39142         
39143         this.doQuery(v);
39144     },
39145
39146     // private
39147     doForce : function(){
39148         if(this.inputEl().dom.value.length > 0){
39149             this.inputEl().dom.value =
39150                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
39151              
39152         }
39153     },
39154
39155     /**
39156      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
39157      * query allowing the query action to be canceled if needed.
39158      * @param {String} query The SQL query to execute
39159      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
39160      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
39161      * saved in the current store (defaults to false)
39162      */
39163     doQuery : function(q, forceAll){
39164         
39165         if(q === undefined || q === null){
39166             q = '';
39167         }
39168         var qe = {
39169             query: q,
39170             forceAll: forceAll,
39171             combo: this,
39172             cancel:false
39173         };
39174         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
39175             return false;
39176         }
39177         q = qe.query;
39178         
39179         forceAll = qe.forceAll;
39180         if(forceAll === true || (q.length >= this.minChars)){
39181             
39182             this.hasQuery = true;
39183             
39184             if(this.lastQuery != q || this.alwaysQuery){
39185                 this.lastQuery = q;
39186                 if(this.mode == 'local'){
39187                     this.selectedIndex = -1;
39188                     if(forceAll){
39189                         this.store.clearFilter();
39190                     }else{
39191                         
39192                         if(this.specialFilter){
39193                             this.fireEvent('specialfilter', this);
39194                             this.onLoad();
39195                             return;
39196                         }
39197                         
39198                         this.store.filter(this.displayField, q);
39199                     }
39200                     
39201                     this.store.fireEvent("datachanged", this.store);
39202                     
39203                     this.onLoad();
39204                     
39205                     
39206                 }else{
39207                     
39208                     this.store.baseParams[this.queryParam] = q;
39209                     
39210                     var options = {params : this.getParams(q)};
39211                     
39212                     if(this.loadNext){
39213                         options.add = true;
39214                         options.params.start = this.page * this.pageSize;
39215                     }
39216                     
39217                     this.store.load(options);
39218                     
39219                     /*
39220                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
39221                      *  we should expand the list on onLoad
39222                      *  so command out it
39223                      */
39224 //                    this.expand();
39225                 }
39226             }else{
39227                 this.selectedIndex = -1;
39228                 this.onLoad();   
39229             }
39230         }
39231         
39232         this.loadNext = false;
39233     },
39234     
39235     // private
39236     getParams : function(q){
39237         var p = {};
39238         //p[this.queryParam] = q;
39239         
39240         if(this.pageSize){
39241             p.start = 0;
39242             p.limit = this.pageSize;
39243         }
39244         return p;
39245     },
39246
39247     /**
39248      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
39249      */
39250     collapse : function(){
39251         if(!this.isExpanded()){
39252             return;
39253         }
39254         
39255         this.list.hide();
39256         
39257         this.hasFocus = false;
39258         
39259         if(this.tickable){
39260             this.okBtn.hide();
39261             this.cancelBtn.hide();
39262             this.trigger.show();
39263             
39264             if(this.editable){
39265                 this.tickableInputEl().dom.value = '';
39266                 this.tickableInputEl().blur();
39267             }
39268             
39269         }
39270         
39271         Roo.get(document).un('mousedown', this.collapseIf, this);
39272         Roo.get(document).un('mousewheel', this.collapseIf, this);
39273         if (!this.editable) {
39274             Roo.get(document).un('keydown', this.listKeyPress, this);
39275         }
39276         this.fireEvent('collapse', this);
39277         
39278         this.validate();
39279     },
39280
39281     // private
39282     collapseIf : function(e){
39283         var in_combo  = e.within(this.el);
39284         var in_list =  e.within(this.list);
39285         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
39286         
39287         if (in_combo || in_list || is_list) {
39288             //e.stopPropagation();
39289             return;
39290         }
39291         
39292         if(this.tickable){
39293             this.onTickableFooterButtonClick(e, false, false);
39294         }
39295
39296         this.collapse();
39297         
39298     },
39299
39300     /**
39301      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
39302      */
39303     expand : function(){
39304        
39305         if(this.isExpanded() || !this.hasFocus){
39306             return;
39307         }
39308         
39309         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
39310         this.list.setWidth(lw);
39311         
39312         Roo.log('expand');
39313         
39314         this.list.show();
39315         
39316         this.restrictHeight();
39317         
39318         if(this.tickable){
39319             
39320             this.tickItems = Roo.apply([], this.item);
39321             
39322             this.okBtn.show();
39323             this.cancelBtn.show();
39324             this.trigger.hide();
39325             
39326             if(this.editable){
39327                 this.tickableInputEl().focus();
39328             }
39329             
39330         }
39331         
39332         Roo.get(document).on('mousedown', this.collapseIf, this);
39333         Roo.get(document).on('mousewheel', this.collapseIf, this);
39334         if (!this.editable) {
39335             Roo.get(document).on('keydown', this.listKeyPress, this);
39336         }
39337         
39338         this.fireEvent('expand', this);
39339     },
39340
39341     // private
39342     // Implements the default empty TriggerField.onTriggerClick function
39343     onTriggerClick : function(e)
39344     {
39345         Roo.log('trigger click');
39346         
39347         if(this.disabled || !this.triggerList){
39348             return;
39349         }
39350         
39351         this.page = 0;
39352         this.loadNext = false;
39353         
39354         if(this.isExpanded()){
39355             this.collapse();
39356             if (!this.blockFocus) {
39357                 this.inputEl().focus();
39358             }
39359             
39360         }else {
39361             this.hasFocus = true;
39362             if(this.triggerAction == 'all') {
39363                 this.doQuery(this.allQuery, true);
39364             } else {
39365                 this.doQuery(this.getRawValue());
39366             }
39367             if (!this.blockFocus) {
39368                 this.inputEl().focus();
39369             }
39370         }
39371     },
39372     
39373     onTickableTriggerClick : function(e)
39374     {
39375         if(this.disabled){
39376             return;
39377         }
39378         
39379         this.page = 0;
39380         this.loadNext = false;
39381         this.hasFocus = true;
39382         
39383         if(this.triggerAction == 'all') {
39384             this.doQuery(this.allQuery, true);
39385         } else {
39386             this.doQuery(this.getRawValue());
39387         }
39388     },
39389     
39390     onSearchFieldClick : function(e)
39391     {
39392         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
39393             this.onTickableFooterButtonClick(e, false, false);
39394             return;
39395         }
39396         
39397         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
39398             return;
39399         }
39400         
39401         this.page = 0;
39402         this.loadNext = false;
39403         this.hasFocus = true;
39404         
39405         if(this.triggerAction == 'all') {
39406             this.doQuery(this.allQuery, true);
39407         } else {
39408             this.doQuery(this.getRawValue());
39409         }
39410     },
39411     
39412     listKeyPress : function(e)
39413     {
39414         //Roo.log('listkeypress');
39415         // scroll to first matching element based on key pres..
39416         if (e.isSpecialKey()) {
39417             return false;
39418         }
39419         var k = String.fromCharCode(e.getKey()).toUpperCase();
39420         //Roo.log(k);
39421         var match  = false;
39422         var csel = this.view.getSelectedNodes();
39423         var cselitem = false;
39424         if (csel.length) {
39425             var ix = this.view.indexOf(csel[0]);
39426             cselitem  = this.store.getAt(ix);
39427             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
39428                 cselitem = false;
39429             }
39430             
39431         }
39432         
39433         this.store.each(function(v) { 
39434             if (cselitem) {
39435                 // start at existing selection.
39436                 if (cselitem.id == v.id) {
39437                     cselitem = false;
39438                 }
39439                 return true;
39440             }
39441                 
39442             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
39443                 match = this.store.indexOf(v);
39444                 return false;
39445             }
39446             return true;
39447         }, this);
39448         
39449         if (match === false) {
39450             return true; // no more action?
39451         }
39452         // scroll to?
39453         this.view.select(match);
39454         var sn = Roo.get(this.view.getSelectedNodes()[0]);
39455         sn.scrollIntoView(sn.dom.parentNode, false);
39456     },
39457     
39458     onViewScroll : function(e, t){
39459         
39460         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){
39461             return;
39462         }
39463         
39464         this.hasQuery = true;
39465         
39466         this.loading = this.list.select('.loading', true).first();
39467         
39468         if(this.loading === null){
39469             this.list.createChild({
39470                 tag: 'div',
39471                 cls: 'loading roo-select2-more-results roo-select2-active',
39472                 html: 'Loading more results...'
39473             });
39474             
39475             this.loading = this.list.select('.loading', true).first();
39476             
39477             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
39478             
39479             this.loading.hide();
39480         }
39481         
39482         this.loading.show();
39483         
39484         var _combo = this;
39485         
39486         this.page++;
39487         this.loadNext = true;
39488         
39489         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
39490         
39491         return;
39492     },
39493     
39494     addItem : function(o)
39495     {   
39496         var dv = ''; // display value
39497         
39498         if (this.displayField) {
39499             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
39500         } else {
39501             // this is an error condition!!!
39502             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
39503         }
39504         
39505         if(!dv.length){
39506             return;
39507         }
39508         
39509         var choice = this.choices.createChild({
39510             tag: 'li',
39511             cls: 'roo-select2-search-choice',
39512             cn: [
39513                 {
39514                     tag: 'div',
39515                     html: dv
39516                 },
39517                 {
39518                     tag: 'a',
39519                     href: '#',
39520                     cls: 'roo-select2-search-choice-close fa fa-times',
39521                     tabindex: '-1'
39522                 }
39523             ]
39524             
39525         }, this.searchField);
39526         
39527         var close = choice.select('a.roo-select2-search-choice-close', true).first();
39528         
39529         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
39530         
39531         this.item.push(o);
39532         
39533         this.lastData = o;
39534         
39535         this.syncValue();
39536         
39537         this.inputEl().dom.value = '';
39538         
39539         this.validate();
39540     },
39541     
39542     onRemoveItem : function(e, _self, o)
39543     {
39544         e.preventDefault();
39545         
39546         this.lastItem = Roo.apply([], this.item);
39547         
39548         var index = this.item.indexOf(o.data) * 1;
39549         
39550         if( index < 0){
39551             Roo.log('not this item?!');
39552             return;
39553         }
39554         
39555         this.item.splice(index, 1);
39556         o.item.remove();
39557         
39558         this.syncValue();
39559         
39560         this.fireEvent('remove', this, e);
39561         
39562         this.validate();
39563         
39564     },
39565     
39566     syncValue : function()
39567     {
39568         if(!this.item.length){
39569             this.clearValue();
39570             return;
39571         }
39572             
39573         var value = [];
39574         var _this = this;
39575         Roo.each(this.item, function(i){
39576             if(_this.valueField){
39577                 value.push(i[_this.valueField]);
39578                 return;
39579             }
39580
39581             value.push(i);
39582         });
39583
39584         this.value = value.join(',');
39585
39586         if(this.hiddenField){
39587             this.hiddenField.dom.value = this.value;
39588         }
39589         
39590         this.store.fireEvent("datachanged", this.store);
39591         
39592         this.validate();
39593     },
39594     
39595     clearItem : function()
39596     {
39597         if(!this.multiple){
39598             return;
39599         }
39600         
39601         this.item = [];
39602         
39603         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
39604            c.remove();
39605         });
39606         
39607         this.syncValue();
39608         
39609         this.validate();
39610         
39611         if(this.tickable && !Roo.isTouch){
39612             this.view.refresh();
39613         }
39614     },
39615     
39616     inputEl: function ()
39617     {
39618         if(Roo.isIOS && this.useNativeIOS){
39619             return this.el.select('select.roo-ios-select', true).first();
39620         }
39621         
39622         if(Roo.isTouch && this.mobileTouchView){
39623             return this.el.select('input.form-control',true).first();
39624         }
39625         
39626         if(this.tickable){
39627             return this.searchField;
39628         }
39629         
39630         return this.el.select('input.form-control',true).first();
39631     },
39632     
39633     onTickableFooterButtonClick : function(e, btn, el)
39634     {
39635         e.preventDefault();
39636         
39637         this.lastItem = Roo.apply([], this.item);
39638         
39639         if(btn && btn.name == 'cancel'){
39640             this.tickItems = Roo.apply([], this.item);
39641             this.collapse();
39642             return;
39643         }
39644         
39645         this.clearItem();
39646         
39647         var _this = this;
39648         
39649         Roo.each(this.tickItems, function(o){
39650             _this.addItem(o);
39651         });
39652         
39653         this.collapse();
39654         
39655     },
39656     
39657     validate : function()
39658     {
39659         var v = this.getRawValue();
39660         
39661         if(this.multiple){
39662             v = this.getValue();
39663         }
39664         
39665         if(this.disabled || this.allowBlank || v.length){
39666             this.markValid();
39667             return true;
39668         }
39669         
39670         this.markInvalid();
39671         return false;
39672     },
39673     
39674     tickableInputEl : function()
39675     {
39676         if(!this.tickable || !this.editable){
39677             return this.inputEl();
39678         }
39679         
39680         return this.inputEl().select('.roo-select2-search-field-input', true).first();
39681     },
39682     
39683     
39684     getAutoCreateTouchView : function()
39685     {
39686         var id = Roo.id();
39687         
39688         var cfg = {
39689             cls: 'form-group' //input-group
39690         };
39691         
39692         var input =  {
39693             tag: 'input',
39694             id : id,
39695             type : this.inputType,
39696             cls : 'form-control x-combo-noedit',
39697             autocomplete: 'new-password',
39698             placeholder : this.placeholder || '',
39699             readonly : true
39700         };
39701         
39702         if (this.name) {
39703             input.name = this.name;
39704         }
39705         
39706         if (this.size) {
39707             input.cls += ' input-' + this.size;
39708         }
39709         
39710         if (this.disabled) {
39711             input.disabled = true;
39712         }
39713         
39714         var inputblock = {
39715             cls : '',
39716             cn : [
39717                 input
39718             ]
39719         };
39720         
39721         if(this.before){
39722             inputblock.cls += ' input-group';
39723             
39724             inputblock.cn.unshift({
39725                 tag :'span',
39726                 cls : 'input-group-addon',
39727                 html : this.before
39728             });
39729         }
39730         
39731         if(this.removable && !this.multiple){
39732             inputblock.cls += ' roo-removable';
39733             
39734             inputblock.cn.push({
39735                 tag: 'button',
39736                 html : 'x',
39737                 cls : 'roo-combo-removable-btn close'
39738             });
39739         }
39740
39741         if(this.hasFeedback && !this.allowBlank){
39742             
39743             inputblock.cls += ' has-feedback';
39744             
39745             inputblock.cn.push({
39746                 tag: 'span',
39747                 cls: 'glyphicon form-control-feedback'
39748             });
39749             
39750         }
39751         
39752         if (this.after) {
39753             
39754             inputblock.cls += (this.before) ? '' : ' input-group';
39755             
39756             inputblock.cn.push({
39757                 tag :'span',
39758                 cls : 'input-group-addon',
39759                 html : this.after
39760             });
39761         }
39762
39763         var box = {
39764             tag: 'div',
39765             cn: [
39766                 {
39767                     tag: 'input',
39768                     type : 'hidden',
39769                     cls: 'form-hidden-field'
39770                 },
39771                 inputblock
39772             ]
39773             
39774         };
39775         
39776         if(this.multiple){
39777             box = {
39778                 tag: 'div',
39779                 cn: [
39780                     {
39781                         tag: 'input',
39782                         type : 'hidden',
39783                         cls: 'form-hidden-field'
39784                     },
39785                     {
39786                         tag: 'ul',
39787                         cls: 'roo-select2-choices',
39788                         cn:[
39789                             {
39790                                 tag: 'li',
39791                                 cls: 'roo-select2-search-field',
39792                                 cn: [
39793
39794                                     inputblock
39795                                 ]
39796                             }
39797                         ]
39798                     }
39799                 ]
39800             }
39801         };
39802         
39803         var PhoneInput = {
39804             cls: 'roo-select2-container input-group roo-touchview-PhoneInput ',
39805             cn: [
39806                 box
39807             ]
39808         };
39809         
39810         if(!this.multiple && this.showToggleBtn){
39811             
39812             var caret = {
39813                         tag: 'span',
39814                         cls: 'caret'
39815             };
39816             
39817             if (this.caret != false) {
39818                 caret = {
39819                      tag: 'i',
39820                      cls: 'fa fa-' + this.caret
39821                 };
39822                 
39823             }
39824             
39825             PhoneInput.cn.push({
39826                 tag :'span',
39827                 cls : 'input-group-addon btn dropdown-toggle',
39828                 cn : [
39829                     caret,
39830                     {
39831                         tag: 'span',
39832                         cls: 'PhoneInput-clear',
39833                         cn  : [
39834                             {
39835                                 tag : 'i',
39836                                 cls: 'icon-remove'
39837                             }
39838                         ]
39839                     }
39840                 ]
39841
39842             })
39843         }
39844         
39845         if(this.multiple){
39846             PhoneInput.cls += ' roo-select2-container-multi';
39847         }
39848         
39849         var align = this.labelAlign || this.parentLabelAlign();
39850         
39851         if (align ==='left' && this.fieldLabel.length) {
39852
39853             cfg.cn = [
39854                 {
39855                    tag : 'i',
39856                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
39857                    tooltip : 'This field is required'
39858                 },
39859                 {
39860                     tag: 'label',
39861                     cls : 'control-label',
39862                     html : this.fieldLabel
39863
39864                 },
39865                 {
39866                     cls : '', 
39867                     cn: [
39868                         PhoneInput
39869                     ]
39870                 }
39871             ];
39872             
39873             var labelCfg = cfg.cn[1];
39874             var contentCfg = cfg.cn[2];
39875             
39876
39877             if(this.indicatorpos == 'right'){
39878                 cfg.cn = [
39879                     {
39880                         tag: 'label',
39881                         cls : 'control-label',
39882                         html : this.fieldLabel,
39883                         cn : [
39884                             {
39885                                tag : 'i',
39886                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
39887                                tooltip : 'This field is required'
39888                             }
39889                         ]
39890                     },
39891                     {
39892                         cls : '', 
39893                         cn: [
39894                             PhoneInput
39895                         ]
39896                     }
39897                 ];
39898             }
39899             
39900             labelCfg = cfg.cn[0];
39901             contentCfg = cfg.cn[2];
39902             
39903             if(this.labelWidth > 12){
39904                 labelCfg.style = "width: " + this.labelWidth + 'px';
39905             }
39906             
39907             if(this.labelWidth < 13 && this.labelmd == 0){
39908                 this.labelmd = this.labelWidth;
39909             }
39910             
39911             if(this.labellg > 0){
39912                 labelCfg.cls += ' col-lg-' + this.labellg;
39913                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
39914             }
39915             
39916             if(this.labelmd > 0){
39917                 labelCfg.cls += ' col-md-' + this.labelmd;
39918                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
39919             }
39920             
39921             if(this.labelsm > 0){
39922                 labelCfg.cls += ' col-sm-' + this.labelsm;
39923                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
39924             }
39925             
39926             if(this.labelxs > 0){
39927                 labelCfg.cls += ' col-xs-' + this.labelxs;
39928                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
39929             }
39930                 
39931                 
39932         } else if ( this.fieldLabel.length) {
39933             cfg.cn = [
39934                 {
39935                    tag : 'i',
39936                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
39937                    tooltip : 'This field is required'
39938                 },
39939                 {
39940                     tag: 'label',
39941                     cls : 'control-label',
39942                     html : this.fieldLabel
39943
39944                 },
39945                 {
39946                     cls : '', 
39947                     cn: [
39948                         PhoneInput
39949                     ]
39950                 }
39951             ];
39952             
39953             if(this.indicatorpos == 'right'){
39954                 cfg.cn = [
39955                     {
39956                         tag: 'label',
39957                         cls : 'control-label',
39958                         html : this.fieldLabel,
39959                         cn : [
39960                             {
39961                                tag : 'i',
39962                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
39963                                tooltip : 'This field is required'
39964                             }
39965                         ]
39966                     },
39967                     {
39968                         cls : '', 
39969                         cn: [
39970                             PhoneInput
39971                         ]
39972                     }
39973                 ];
39974             }
39975         } else {
39976             cfg.cn = PhoneInput;    
39977         }
39978         
39979         
39980         var settings = this;
39981         
39982         ['xs','sm','md','lg'].map(function(size){
39983             if (settings[size]) {
39984                 cfg.cls += ' col-' + size + '-' + settings[size];
39985             }
39986         });
39987         
39988         return cfg;
39989     },
39990     
39991     initTouchView : function()
39992     {
39993         this.renderTouchView();
39994         
39995         this.touchViewEl.on('scroll', function(){
39996             this.el.dom.scrollTop = 0;
39997         }, this);
39998         
39999         this.originalValue = this.getValue();
40000         
40001         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
40002         
40003         this.inputEl().on("click", this.showTouchView, this);
40004         if (this.triggerEl) {
40005             this.triggerEl.on("click", this.showTouchView, this);
40006         }
40007         
40008         
40009         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
40010         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
40011         
40012         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
40013         
40014         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
40015         this.store.on('load', this.onTouchViewLoad, this);
40016         this.store.on('loadexception', this.onTouchViewLoadException, this);
40017         
40018         if(this.hiddenName){
40019             
40020             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
40021             
40022             this.hiddenField.dom.value =
40023                 this.hiddenValue !== undefined ? this.hiddenValue :
40024                 this.value !== undefined ? this.value : '';
40025         
40026             this.el.dom.removeAttribute('name');
40027             this.hiddenField.dom.setAttribute('name', this.hiddenName);
40028         }
40029         
40030         if(this.multiple){
40031             this.choices = this.el.select('ul.roo-select2-choices', true).first();
40032             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
40033         }
40034         
40035         if(this.removable && !this.multiple){
40036             var close = this.closeTriggerEl();
40037             if(close){
40038                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
40039                 close.on('click', this.removeBtnClick, this, close);
40040             }
40041         }
40042         /*
40043          * fix the bug in Safari iOS8
40044          */
40045         this.inputEl().on("focus", function(e){
40046             document.activeElement.blur();
40047         }, this);
40048         
40049         return;
40050         
40051         
40052     },
40053     
40054     renderTouchView : function()
40055     {
40056         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.PhoneInput.touchViewTemplate);
40057         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40058         
40059         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
40060         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40061         
40062         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
40063         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40064         this.touchViewBodyEl.setStyle('overflow', 'auto');
40065         
40066         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
40067         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40068         
40069         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
40070         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
40071         
40072     },
40073     
40074     showTouchView : function()
40075     {
40076         if(this.disabled){
40077             return;
40078         }
40079         
40080         this.touchViewHeaderEl.hide();
40081
40082         if(this.modalTitle.length){
40083             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
40084             this.touchViewHeaderEl.show();
40085         }
40086
40087         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
40088         this.touchViewEl.show();
40089
40090         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
40091         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
40092                 Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
40093
40094         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
40095
40096         if(this.modalTitle.length){
40097             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
40098         }
40099         
40100         this.touchViewBodyEl.setHeight(bodyHeight);
40101
40102         if(this.animate){
40103             var _this = this;
40104             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
40105         }else{
40106             this.touchViewEl.addClass('in');
40107         }
40108
40109         this.doTouchViewQuery();
40110         
40111     },
40112     
40113     hideTouchView : function()
40114     {
40115         this.touchViewEl.removeClass('in');
40116
40117         if(this.animate){
40118             var _this = this;
40119             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
40120         }else{
40121             this.touchViewEl.setStyle('display', 'none');
40122         }
40123         
40124     },
40125     
40126     setTouchViewValue : function()
40127     {
40128         if(this.multiple){
40129             this.clearItem();
40130         
40131             var _this = this;
40132
40133             Roo.each(this.tickItems, function(o){
40134                 this.addItem(o);
40135             }, this);
40136         }
40137         
40138         this.hideTouchView();
40139     },
40140     
40141     doTouchViewQuery : function()
40142     {
40143         var qe = {
40144             query: '',
40145             forceAll: true,
40146             combo: this,
40147             cancel:false
40148         };
40149         
40150         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
40151             return false;
40152         }
40153         
40154         if(!this.alwaysQuery || this.mode == 'local'){
40155             this.onTouchViewLoad();
40156             return;
40157         }
40158         
40159         this.store.load();
40160     },
40161     
40162     onTouchViewBeforeLoad : function(combo,opts)
40163     {
40164         return;
40165     },
40166
40167     // private
40168     onTouchViewLoad : function()
40169     {
40170         if(this.store.getCount() < 1){
40171             this.onTouchViewEmptyResults();
40172             return;
40173         }
40174         
40175         this.clearTouchView();
40176         
40177         var rawValue = this.getRawValue();
40178         
40179         var template = (this.multiple) ? Roo.bootstrap.PhoneInput.listItemCheckbox : Roo.bootstrap.PhoneInput.listItemRadio;
40180         
40181         this.tickItems = [];
40182         
40183         this.store.data.each(function(d, rowIndex){
40184             var row = this.touchViewListGroup.createChild(template);
40185             
40186             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
40187                 row.addClass(d.data.cls);
40188             }
40189             
40190             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
40191                 var cfg = {
40192                     data : d.data,
40193                     html : d.data[this.displayField]
40194                 };
40195                 
40196                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
40197                     row.select('.roo-PhoneInput-list-group-item-value', true).first().dom.innerHTML = cfg.html;
40198                 }
40199             }
40200             row.removeClass('selected');
40201             if(!this.multiple && this.valueField &&
40202                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
40203             {
40204                 // radio buttons..
40205                 row.select('.roo-PhoneInput-list-group-item-box > input', true).first().attr('checked', true);
40206                 row.addClass('selected');
40207             }
40208             
40209             if(this.multiple && this.valueField &&
40210                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
40211             {
40212                 
40213                 // checkboxes...
40214                 row.select('.roo-PhoneInput-list-group-item-box > input', true).first().attr('checked', true);
40215                 this.tickItems.push(d.data);
40216             }
40217             
40218             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
40219             
40220         }, this);
40221         
40222         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-PhoneInput-list-group-item-box > input:checked', true).first();
40223         
40224         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
40225
40226         if(this.modalTitle.length){
40227             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
40228         }
40229
40230         var listHeight = this.touchViewListGroup.getHeight();
40231         
40232         var _this = this;
40233         
40234         if(firstChecked && listHeight > bodyHeight){
40235             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
40236         }
40237         
40238     },
40239     
40240     onTouchViewLoadException : function()
40241     {
40242         this.hideTouchView();
40243     },
40244     
40245     onTouchViewEmptyResults : function()
40246     {
40247         this.clearTouchView();
40248         
40249         this.touchViewListGroup.createChild(Roo.bootstrap.PhoneInput.emptyResult);
40250         
40251         this.touchViewListGroup.select('.roo-PhoneInput-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
40252         
40253     },
40254     
40255     clearTouchView : function()
40256     {
40257         this.touchViewListGroup.dom.innerHTML = '';
40258     },
40259     
40260     onTouchViewClick : function(e, el, o)
40261     {
40262         e.preventDefault();
40263         
40264         var row = o.row;
40265         var rowIndex = o.rowIndex;
40266         
40267         var r = this.store.getAt(rowIndex);
40268         
40269         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
40270             
40271             if(!this.multiple){
40272                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-PhoneInput-list-group-item-box > input:checked', true).elements, function(c){
40273                     c.dom.removeAttribute('checked');
40274                 }, this);
40275
40276                 row.select('.roo-PhoneInput-list-group-item-box > input', true).first().attr('checked', true);
40277
40278                 this.setFromData(r.data);
40279
40280                 var close = this.closeTriggerEl();
40281
40282                 if(close){
40283                     close.show();
40284                 }
40285
40286                 this.hideTouchView();
40287
40288                 this.fireEvent('select', this, r, rowIndex);
40289
40290                 return;
40291             }
40292
40293             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
40294                 row.select('.roo-PhoneInput-list-group-item-box > input', true).first().dom.removeAttribute('checked');
40295                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
40296                 return;
40297             }
40298
40299             row.select('.roo-PhoneInput-list-group-item-box > input', true).first().attr('checked', true);
40300             this.addItem(r.data);
40301             this.tickItems.push(r.data);
40302         }
40303     },
40304     
40305     getAutoCreateNativeIOS : function()
40306     {
40307         var cfg = {
40308             cls: 'form-group' //input-group,
40309         };
40310         
40311         var PhoneInput =  {
40312             tag: 'select',
40313             cls : 'roo-ios-select'
40314         };
40315         
40316         if (this.name) {
40317             PhoneInput.name = this.name;
40318         }
40319         
40320         if (this.disabled) {
40321             PhoneInput.disabled = true;
40322         }
40323         
40324         var settings = this;
40325         
40326         ['xs','sm','md','lg'].map(function(size){
40327             if (settings[size]) {
40328                 cfg.cls += ' col-' + size + '-' + settings[size];
40329             }
40330         });
40331         
40332         cfg.cn = PhoneInput;
40333         
40334         return cfg;
40335         
40336     },
40337     
40338     initIOSView : function()
40339     {
40340         this.store.on('load', this.onIOSViewLoad, this);
40341         
40342         return;
40343     },
40344     
40345     onIOSViewLoad : function()
40346     {
40347         if(this.store.getCount() < 1){
40348             return;
40349         }
40350         
40351         this.clearIOSView();
40352         
40353         if(this.allowBlank) {
40354             
40355             var default_text = '-- SELECT --';
40356             
40357             var opt = this.inputEl().createChild({
40358                 tag: 'option',
40359                 value : 0,
40360                 html : default_text
40361             });
40362             
40363             var o = {};
40364             o[this.valueField] = 0;
40365             o[this.displayField] = default_text;
40366             
40367             this.ios_options.push({
40368                 data : o,
40369                 el : opt
40370             });
40371             
40372         }
40373         
40374         this.store.data.each(function(d, rowIndex){
40375             
40376             var html = '';
40377             
40378             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
40379                 html = d.data[this.displayField];
40380             }
40381             
40382             var value = '';
40383             
40384             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
40385                 value = d.data[this.valueField];
40386             }
40387             
40388             var option = {
40389                 tag: 'option',
40390                 value : value,
40391                 html : html
40392             };
40393             
40394             if(this.value == d.data[this.valueField]){
40395                 option['selected'] = true;
40396             }
40397             
40398             var opt = this.inputEl().createChild(option);
40399             
40400             this.ios_options.push({
40401                 data : d.data,
40402                 el : opt
40403             });
40404             
40405         }, this);
40406         
40407         this.inputEl().on('change', function(){
40408            this.fireEvent('select', this);
40409         }, this);
40410         
40411     },
40412     
40413     clearIOSView: function()
40414     {
40415         this.inputEl().dom.innerHTML = '';
40416         
40417         this.ios_options = [];
40418     },
40419     
40420     setIOSValue: function(v)
40421     {
40422         this.value = v;
40423         
40424         if(!this.ios_options){
40425             return;
40426         }
40427         
40428         Roo.each(this.ios_options, function(opts){
40429            
40430            opts.el.dom.removeAttribute('selected');
40431            
40432            if(opts.data[this.valueField] != v){
40433                return;
40434            }
40435            
40436            opts.el.dom.setAttribute('selected', true);
40437            
40438         }, this);
40439     }
40440
40441     /** 
40442     * @cfg {Boolean} grow 
40443     * @hide 
40444     */
40445     /** 
40446     * @cfg {Number} growMin 
40447     * @hide 
40448     */
40449     /** 
40450     * @cfg {Number} growMax 
40451     * @hide 
40452     */
40453     /**
40454      * @hide
40455      * @method autoSize
40456      */
40457 });
40458
40459 Roo.apply(Roo.bootstrap.PhoneInput,  {
40460     
40461     header : {
40462         tag: 'div',
40463         cls: 'modal-header',
40464         cn: [
40465             {
40466                 tag: 'h4',
40467                 cls: 'modal-title'
40468             }
40469         ]
40470     },
40471     
40472     body : {
40473         tag: 'div',
40474         cls: 'modal-body',
40475         cn: [
40476             {
40477                 tag: 'ul',
40478                 cls: 'list-group'
40479             }
40480         ]
40481     },
40482     
40483     listItemRadio : {
40484         tag: 'li',
40485         cls: 'list-group-item',
40486         cn: [
40487             {
40488                 tag: 'span',
40489                 cls: 'roo-PhoneInput-list-group-item-value'
40490             },
40491             {
40492                 tag: 'div',
40493                 cls: 'roo-PhoneInput-list-group-item-box pull-xs-right radio-inline radio radio-info',
40494                 cn: [
40495                     {
40496                         tag: 'input',
40497                         type: 'radio'
40498                     },
40499                     {
40500                         tag: 'label'
40501                     }
40502                 ]
40503             }
40504         ]
40505     },
40506     
40507     listItemCheckbox : {
40508         tag: 'li',
40509         cls: 'list-group-item',
40510         cn: [
40511             {
40512                 tag: 'span',
40513                 cls: 'roo-PhoneInput-list-group-item-value'
40514             },
40515             {
40516                 tag: 'div',
40517                 cls: 'roo-PhoneInput-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
40518                 cn: [
40519                     {
40520                         tag: 'input',
40521                         type: 'checkbox'
40522                     },
40523                     {
40524                         tag: 'label'
40525                     }
40526                 ]
40527             }
40528         ]
40529     },
40530     
40531     emptyResult : {
40532         tag: 'div',
40533         cls: 'alert alert-danger roo-PhoneInput-touch-view-empty-result'
40534     },
40535     
40536     footer : {
40537         tag: 'div',
40538         cls: 'modal-footer',
40539         cn: [
40540             {
40541                 tag: 'div',
40542                 cls: 'row',
40543                 cn: [
40544                     {
40545                         tag: 'div',
40546                         cls: 'col-xs-6 text-left',
40547                         cn: {
40548                             tag: 'button',
40549                             cls: 'btn btn-danger roo-touch-view-cancel',
40550                             html: 'Cancel'
40551                         }
40552                     },
40553                     {
40554                         tag: 'div',
40555                         cls: 'col-xs-6 text-right',
40556                         cn: {
40557                             tag: 'button',
40558                             cls: 'btn btn-success roo-touch-view-ok',
40559                             html: 'OK'
40560                         }
40561                     }
40562                 ]
40563             }
40564         ]
40565         
40566     }
40567 });