roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31        
32     this.addEvents({
33         /**
34          * @event childrenrendered
35          * Fires when the children have been rendered..
36          * @param {Roo.bootstrap.Component} this
37          */
38         "childrenrendered" : true
39         
40         
41         
42     });
43     
44     
45 };
46
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
48     
49     
50     allowDomMove : false, // to stop relocations in parent onRender...
51     
52     cls : false,
53     
54     style : false,
55     
56     autoCreate : false,
57     
58     tooltip : null,
59     /**
60      * Initialize Events for the element
61      */
62     initEvents : function() { },
63     
64     xattr : false,
65     
66     parentId : false,
67     
68     can_build_overlaid : true,
69     
70     container_method : false,
71     
72     dataId : false,
73     
74     name : false,
75     
76     parent: function() {
77         // returns the parent component..
78         return Roo.ComponentMgr.get(this.parentId)
79         
80         
81     },
82     
83     // private
84     onRender : function(ct, position)
85     {
86        // Roo.log("Call onRender: " + this.xtype);
87         
88         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
89         
90         if(this.el){
91             if (this.el.attr('xtype')) {
92                 this.el.attr('xtypex', this.el.attr('xtype'));
93                 this.el.dom.removeAttribute('xtype');
94                 
95                 this.initEvents();
96             }
97             
98             return;
99         }
100         
101          
102         
103         var cfg = Roo.apply({},  this.getAutoCreate());
104         
105         cfg.id = this.id || Roo.id();
106         
107         // fill in the extra attributes 
108         if (this.xattr && typeof(this.xattr) =='object') {
109             for (var i in this.xattr) {
110                 cfg[i] = this.xattr[i];
111             }
112         }
113         
114         if(this.dataId){
115             cfg.dataId = this.dataId;
116         }
117         
118         if (this.cls) {
119             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
120         }
121         
122         if (this.style) { // fixme needs to support more complex style data.
123             cfg.style = this.style;
124         }
125         
126         if(this.name){
127             cfg.name = this.name;
128         }
129         
130         this.el = ct.createChild(cfg, position);
131         
132         if (this.tooltip) {
133             this.tooltipEl().attr('tooltip', this.tooltip);
134         }
135         
136         if(this.tabIndex !== undefined){
137             this.el.dom.setAttribute('tabIndex', this.tabIndex);
138         }
139         
140         this.initEvents();
141         
142     },
143     /**
144      * Fetch the element to add children to
145      * @return {Roo.Element} defaults to this.el
146      */
147     getChildContainer : function()
148     {
149         return this.el;
150     },
151     /**
152      * Fetch the element to display the tooltip on.
153      * @return {Roo.Element} defaults to this.el
154      */
155     tooltipEl : function()
156     {
157         return this.el;
158     },
159         
160     addxtype  : function(tree,cntr)
161     {
162         var cn = this;
163         
164         cn = Roo.factory(tree);
165         //Roo.log(['addxtype', cn]);
166            
167         cn.parentType = this.xtype; //??
168         cn.parentId = this.id;
169         
170         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
171         if (typeof(cn.container_method) == 'string') {
172             cntr = cn.container_method;
173         }
174         
175         
176         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
177         
178         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
179         
180         var build_from_html =  Roo.XComponent.build_from_html;
181           
182         var is_body  = (tree.xtype == 'Body') ;
183           
184         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
185           
186         var self_cntr_el = Roo.get(this[cntr](false));
187         
188         // do not try and build conditional elements 
189         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
190             return false;
191         }
192         
193         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
194             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
195                 return this.addxtypeChild(tree,cntr, is_body);
196             }
197             
198             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
199                 
200             if(echild){
201                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
202             }
203             
204             Roo.log('skipping render');
205             return cn;
206             
207         }
208         
209         var ret = false;
210         if (!build_from_html) {
211             return false;
212         }
213         
214         // this i think handles overlaying multiple children of the same type
215         // with the sam eelement.. - which might be buggy..
216         while (true) {
217             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
218             
219             if (!echild) {
220                 break;
221             }
222             
223             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
224                 break;
225             }
226             
227             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
228         }
229        
230         return ret;
231     },
232     
233     
234     addxtypeChild : function (tree, cntr, is_body)
235     {
236         Roo.debug && Roo.log('addxtypeChild:' + cntr);
237         var cn = this;
238         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
239         
240         
241         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
242                     (typeof(tree['flexy:foreach']) != 'undefined');
243           
244         
245         
246          skip_children = false;
247         // render the element if it's not BODY.
248         if (!is_body) {
249            
250             cn = Roo.factory(tree);
251            
252             cn.parentType = this.xtype; //??
253             cn.parentId = this.id;
254             
255             var build_from_html =  Roo.XComponent.build_from_html;
256             
257             
258             // does the container contain child eleemnts with 'xtype' attributes.
259             // that match this xtype..
260             // note - when we render we create these as well..
261             // so we should check to see if body has xtype set.
262             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
263                
264                 var self_cntr_el = Roo.get(this[cntr](false));
265                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
266                 if (echild) { 
267                     //Roo.log(Roo.XComponent.build_from_html);
268                     //Roo.log("got echild:");
269                     //Roo.log(echild);
270                 }
271                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
272                 // and are not displayed -this causes this to use up the wrong element when matching.
273                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
274                 
275                 
276                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
277                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
278                   
279                   
280                   
281                     cn.el = echild;
282                   //  Roo.log("GOT");
283                     //echild.dom.removeAttribute('xtype');
284                 } else {
285                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
286                     Roo.debug && Roo.log(self_cntr_el);
287                     Roo.debug && Roo.log(echild);
288                     Roo.debug && Roo.log(cn);
289                 }
290             }
291            
292             
293            
294             // if object has flexy:if - then it may or may not be rendered.
295             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
296                 // skip a flexy if element.
297                 Roo.debug && Roo.log('skipping render');
298                 Roo.debug && Roo.log(tree);
299                 if (!cn.el) {
300                     Roo.debug && Roo.log('skipping all children');
301                     skip_children = true;
302                 }
303                 
304              } else {
305                  
306                 // actually if flexy:foreach is found, we really want to create 
307                 // multiple copies here...
308                 //Roo.log('render');
309                 //Roo.log(this[cntr]());
310                 // some elements do not have render methods.. like the layouts...
311                 cn.render && cn.render(this[cntr](true));
312              }
313             // then add the element..
314         }
315         
316         
317         // handle the kids..
318         
319         var nitems = [];
320         /*
321         if (typeof (tree.menu) != 'undefined') {
322             tree.menu.parentType = cn.xtype;
323             tree.menu.triggerEl = cn.el;
324             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
325             
326         }
327         */
328         if (!tree.items || !tree.items.length) {
329             cn.items = nitems;
330             //Roo.log(["no children", this]);
331             
332             return cn;
333         }
334          
335         var items = tree.items;
336         delete tree.items;
337         
338         //Roo.log(items.length);
339             // add the items..
340         if (!skip_children) {    
341             for(var i =0;i < items.length;i++) {
342               //  Roo.log(['add child', items[i]]);
343                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
344             }
345         }
346         
347         cn.items = nitems;
348         
349         //Roo.log("fire childrenrendered");
350         
351         cn.fireEvent('childrenrendered', this);
352         
353         return cn;
354     },
355     /**
356      * Show a component - removes 'hidden' class
357      */
358     show : function()
359     {
360         if (this.el) {
361             this.el.removeClass('hidden');
362         }
363     },
364     /**
365      * Hide a component - adds 'hidden' class
366      */
367     hide: function()
368     {
369         if (this.el && !this.el.hasClass('hidden')) {
370             this.el.addClass('hidden');
371         }
372         
373     }
374 });
375
376  /*
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             return cfg;
12705         }
12706         
12707         /*
12708          *  ComboBox with tickable selections
12709          */
12710              
12711         var align = this.labelAlign || this.parentLabelAlign();
12712         
12713         cfg = {
12714             cls : 'form-group roo-combobox-tickable' //input-group
12715         };
12716         
12717         var btn_text_select = '';
12718         var btn_text_done = '';
12719         var btn_text_cancel = '';
12720         
12721         if (this.btn_text_show) {
12722             btn_text_select = 'Select';
12723             btn_text_done = 'Done';
12724             btn_text_cancel = 'Cancel'; 
12725         }
12726         
12727         var buttons = {
12728             tag : 'div',
12729             cls : 'tickable-buttons',
12730             cn : [
12731                 {
12732                     tag : 'button',
12733                     type : 'button',
12734                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12735                     //html : this.triggerText
12736                     html: btn_text_select
12737                 },
12738                 {
12739                     tag : 'button',
12740                     type : 'button',
12741                     name : 'ok',
12742                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12743                     //html : 'Done'
12744                     html: btn_text_done
12745                 },
12746                 {
12747                     tag : 'button',
12748                     type : 'button',
12749                     name : 'cancel',
12750                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12751                     //html : 'Cancel'
12752                     html: btn_text_cancel
12753                 }
12754             ]
12755         };
12756         
12757         if(this.editable){
12758             buttons.cn.unshift({
12759                 tag: 'input',
12760                 cls: 'roo-select2-search-field-input'
12761             });
12762         }
12763         
12764         var _this = this;
12765         
12766         Roo.each(buttons.cn, function(c){
12767             if (_this.size) {
12768                 c.cls += ' btn-' + _this.size;
12769             }
12770
12771             if (_this.disabled) {
12772                 c.disabled = true;
12773             }
12774         });
12775         
12776         var box = {
12777             tag: 'div',
12778             cn: [
12779                 {
12780                     tag: 'input',
12781                     type : 'hidden',
12782                     cls: 'form-hidden-field'
12783                 },
12784                 {
12785                     tag: 'ul',
12786                     cls: 'roo-select2-choices',
12787                     cn:[
12788                         {
12789                             tag: 'li',
12790                             cls: 'roo-select2-search-field',
12791                             cn: [
12792                                 buttons
12793                             ]
12794                         }
12795                     ]
12796                 }
12797             ]
12798         };
12799         
12800         var combobox = {
12801             cls: 'roo-select2-container input-group roo-select2-container-multi',
12802             cn: [
12803                 box
12804 //                {
12805 //                    tag: 'ul',
12806 //                    cls: 'typeahead typeahead-long dropdown-menu',
12807 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12808 //                }
12809             ]
12810         };
12811         
12812         if(this.hasFeedback && !this.allowBlank){
12813             
12814             var feedback = {
12815                 tag: 'span',
12816                 cls: 'glyphicon form-control-feedback'
12817             };
12818
12819             combobox.cn.push(feedback);
12820         }
12821         
12822         
12823         if (align ==='left' && this.fieldLabel.length) {
12824             
12825             cfg.cls += ' roo-form-group-label-left';
12826             
12827             cfg.cn = [
12828                 {
12829                     tag : 'i',
12830                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12831                     tooltip : 'This field is required'
12832                 },
12833                 {
12834                     tag: 'label',
12835                     'for' :  id,
12836                     cls : 'control-label',
12837                     html : this.fieldLabel
12838
12839                 },
12840                 {
12841                     cls : "", 
12842                     cn: [
12843                         combobox
12844                     ]
12845                 }
12846
12847             ];
12848             
12849             var labelCfg = cfg.cn[1];
12850             var contentCfg = cfg.cn[2];
12851             
12852
12853             if(this.indicatorpos == 'right'){
12854                 
12855                 cfg.cn = [
12856                     {
12857                         tag: 'label',
12858                         'for' :  id,
12859                         cls : 'control-label',
12860                         cn : [
12861                             {
12862                                 tag : 'span',
12863                                 html : this.fieldLabel
12864                             },
12865                             {
12866                                 tag : 'i',
12867                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12868                                 tooltip : 'This field is required'
12869                             }
12870                         ]
12871                     },
12872                     {
12873                         cls : "",
12874                         cn: [
12875                             combobox
12876                         ]
12877                     }
12878
12879                 ];
12880                 
12881                 
12882                 
12883                 labelCfg = cfg.cn[0];
12884                 contentCfg = cfg.cn[1];
12885             
12886             }
12887             
12888             if(this.labelWidth > 12){
12889                 labelCfg.style = "width: " + this.labelWidth + 'px';
12890             }
12891             
12892             if(this.labelWidth < 13 && this.labelmd == 0){
12893                 this.labelmd = this.labelWidth;
12894             }
12895             
12896             if(this.labellg > 0){
12897                 labelCfg.cls += ' col-lg-' + this.labellg;
12898                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12899             }
12900             
12901             if(this.labelmd > 0){
12902                 labelCfg.cls += ' col-md-' + this.labelmd;
12903                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12904             }
12905             
12906             if(this.labelsm > 0){
12907                 labelCfg.cls += ' col-sm-' + this.labelsm;
12908                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12909             }
12910             
12911             if(this.labelxs > 0){
12912                 labelCfg.cls += ' col-xs-' + this.labelxs;
12913                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12914             }
12915                 
12916                 
12917         } else if ( this.fieldLabel.length) {
12918 //                Roo.log(" label");
12919                  cfg.cn = [
12920                     {
12921                         tag : 'i',
12922                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12923                         tooltip : 'This field is required'
12924                     },
12925                     {
12926                         tag: 'label',
12927                         //cls : 'input-group-addon',
12928                         html : this.fieldLabel
12929                     },
12930                     combobox
12931                 ];
12932                 
12933                 if(this.indicatorpos == 'right'){
12934                     cfg.cn = [
12935                         {
12936                             tag: 'label',
12937                             //cls : 'input-group-addon',
12938                             html : this.fieldLabel
12939                         },
12940                         {
12941                             tag : 'i',
12942                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12943                             tooltip : 'This field is required'
12944                         },
12945                         combobox
12946                     ];
12947                     
12948                 }
12949
12950         } else {
12951             
12952 //                Roo.log(" no label && no align");
12953                 cfg = combobox
12954                      
12955                 
12956         }
12957          
12958         var settings=this;
12959         ['xs','sm','md','lg'].map(function(size){
12960             if (settings[size]) {
12961                 cfg.cls += ' col-' + size + '-' + settings[size];
12962             }
12963         });
12964         
12965         return cfg;
12966         
12967     },
12968     
12969     _initEventsCalled : false,
12970     
12971     // private
12972     initEvents: function()
12973     {   
12974         if (this._initEventsCalled) { // as we call render... prevent looping...
12975             return;
12976         }
12977         this._initEventsCalled = true;
12978         
12979         if (!this.store) {
12980             throw "can not find store for combo";
12981         }
12982         
12983         this.store = Roo.factory(this.store, Roo.data);
12984         this.store.parent = this;
12985         
12986         // if we are building from html. then this element is so complex, that we can not really
12987         // use the rendered HTML.
12988         // so we have to trash and replace the previous code.
12989         if (Roo.XComponent.build_from_html) {
12990             
12991             // remove this element....
12992             var e = this.el.dom, k=0;
12993             while (e ) { e = e.previousSibling;  ++k;}
12994
12995             this.el.remove();
12996             
12997             this.el=false;
12998             this.rendered = false;
12999             
13000             this.render(this.parent().getChildContainer(true), k);
13001             
13002             
13003             
13004         }
13005         
13006         if(Roo.isIOS && this.useNativeIOS){
13007             this.initIOSView();
13008             return;
13009         }
13010         
13011         /*
13012          * Touch Devices
13013          */
13014         
13015         if(Roo.isTouch && this.mobileTouchView){
13016             this.initTouchView();
13017             return;
13018         }
13019         
13020         if(this.tickable){
13021             this.initTickableEvents();
13022             return;
13023         }
13024         
13025         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13026         
13027         if(this.hiddenName){
13028             
13029             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13030             
13031             this.hiddenField.dom.value =
13032                 this.hiddenValue !== undefined ? this.hiddenValue :
13033                 this.value !== undefined ? this.value : '';
13034
13035             // prevent input submission
13036             this.el.dom.removeAttribute('name');
13037             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13038              
13039              
13040         }
13041         //if(Roo.isGecko){
13042         //    this.el.dom.setAttribute('autocomplete', 'off');
13043         //}
13044         
13045         var cls = 'x-combo-list';
13046         
13047         //this.list = new Roo.Layer({
13048         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13049         //});
13050         
13051         var _this = this;
13052         
13053         (function(){
13054             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13055             _this.list.setWidth(lw);
13056         }).defer(100);
13057         
13058         this.list.on('mouseover', this.onViewOver, this);
13059         this.list.on('mousemove', this.onViewMove, this);
13060         
13061         this.list.on('scroll', this.onViewScroll, this);
13062         
13063         /*
13064         this.list.swallowEvent('mousewheel');
13065         this.assetHeight = 0;
13066
13067         if(this.title){
13068             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13069             this.assetHeight += this.header.getHeight();
13070         }
13071
13072         this.innerList = this.list.createChild({cls:cls+'-inner'});
13073         this.innerList.on('mouseover', this.onViewOver, this);
13074         this.innerList.on('mousemove', this.onViewMove, this);
13075         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13076         
13077         if(this.allowBlank && !this.pageSize && !this.disableClear){
13078             this.footer = this.list.createChild({cls:cls+'-ft'});
13079             this.pageTb = new Roo.Toolbar(this.footer);
13080            
13081         }
13082         if(this.pageSize){
13083             this.footer = this.list.createChild({cls:cls+'-ft'});
13084             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13085                     {pageSize: this.pageSize});
13086             
13087         }
13088         
13089         if (this.pageTb && this.allowBlank && !this.disableClear) {
13090             var _this = this;
13091             this.pageTb.add(new Roo.Toolbar.Fill(), {
13092                 cls: 'x-btn-icon x-btn-clear',
13093                 text: '&#160;',
13094                 handler: function()
13095                 {
13096                     _this.collapse();
13097                     _this.clearValue();
13098                     _this.onSelect(false, -1);
13099                 }
13100             });
13101         }
13102         if (this.footer) {
13103             this.assetHeight += this.footer.getHeight();
13104         }
13105         */
13106             
13107         if(!this.tpl){
13108             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13109         }
13110
13111         this.view = new Roo.View(this.list, this.tpl, {
13112             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13113         });
13114         //this.view.wrapEl.setDisplayed(false);
13115         this.view.on('click', this.onViewClick, this);
13116         
13117         
13118         this.store.on('beforeload', this.onBeforeLoad, this);
13119         this.store.on('load', this.onLoad, this);
13120         this.store.on('loadexception', this.onLoadException, this);
13121         /*
13122         if(this.resizable){
13123             this.resizer = new Roo.Resizable(this.list,  {
13124                pinned:true, handles:'se'
13125             });
13126             this.resizer.on('resize', function(r, w, h){
13127                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13128                 this.listWidth = w;
13129                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13130                 this.restrictHeight();
13131             }, this);
13132             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13133         }
13134         */
13135         if(!this.editable){
13136             this.editable = true;
13137             this.setEditable(false);
13138         }
13139         
13140         /*
13141         
13142         if (typeof(this.events.add.listeners) != 'undefined') {
13143             
13144             this.addicon = this.wrap.createChild(
13145                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13146        
13147             this.addicon.on('click', function(e) {
13148                 this.fireEvent('add', this);
13149             }, this);
13150         }
13151         if (typeof(this.events.edit.listeners) != 'undefined') {
13152             
13153             this.editicon = this.wrap.createChild(
13154                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13155             if (this.addicon) {
13156                 this.editicon.setStyle('margin-left', '40px');
13157             }
13158             this.editicon.on('click', function(e) {
13159                 
13160                 // we fire even  if inothing is selected..
13161                 this.fireEvent('edit', this, this.lastData );
13162                 
13163             }, this);
13164         }
13165         */
13166         
13167         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13168             "up" : function(e){
13169                 this.inKeyMode = true;
13170                 this.selectPrev();
13171             },
13172
13173             "down" : function(e){
13174                 if(!this.isExpanded()){
13175                     this.onTriggerClick();
13176                 }else{
13177                     this.inKeyMode = true;
13178                     this.selectNext();
13179                 }
13180             },
13181
13182             "enter" : function(e){
13183 //                this.onViewClick();
13184                 //return true;
13185                 this.collapse();
13186                 
13187                 if(this.fireEvent("specialkey", this, e)){
13188                     this.onViewClick(false);
13189                 }
13190                 
13191                 return true;
13192             },
13193
13194             "esc" : function(e){
13195                 this.collapse();
13196             },
13197
13198             "tab" : function(e){
13199                 this.collapse();
13200                 
13201                 if(this.fireEvent("specialkey", this, e)){
13202                     this.onViewClick(false);
13203                 }
13204                 
13205                 return true;
13206             },
13207
13208             scope : this,
13209
13210             doRelay : function(foo, bar, hname){
13211                 if(hname == 'down' || this.scope.isExpanded()){
13212                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13213                 }
13214                 return true;
13215             },
13216
13217             forceKeyDown: true
13218         });
13219         
13220         
13221         this.queryDelay = Math.max(this.queryDelay || 10,
13222                 this.mode == 'local' ? 10 : 250);
13223         
13224         
13225         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13226         
13227         if(this.typeAhead){
13228             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13229         }
13230         if(this.editable !== false){
13231             this.inputEl().on("keyup", this.onKeyUp, this);
13232         }
13233         if(this.forceSelection){
13234             this.inputEl().on('blur', this.doForce, this);
13235         }
13236         
13237         if(this.multiple){
13238             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13239             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13240         }
13241     },
13242     
13243     initTickableEvents: function()
13244     {   
13245         this.createList();
13246         
13247         if(this.hiddenName){
13248             
13249             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13250             
13251             this.hiddenField.dom.value =
13252                 this.hiddenValue !== undefined ? this.hiddenValue :
13253                 this.value !== undefined ? this.value : '';
13254
13255             // prevent input submission
13256             this.el.dom.removeAttribute('name');
13257             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13258              
13259              
13260         }
13261         
13262 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13263         
13264         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13265         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13266         if(this.triggerList){
13267             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13268         }
13269          
13270         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13271         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13272         
13273         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13274         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13275         
13276         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13277         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13278         
13279         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13280         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13281         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13282         
13283         this.okBtn.hide();
13284         this.cancelBtn.hide();
13285         
13286         var _this = this;
13287         
13288         (function(){
13289             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13290             _this.list.setWidth(lw);
13291         }).defer(100);
13292         
13293         this.list.on('mouseover', this.onViewOver, this);
13294         this.list.on('mousemove', this.onViewMove, this);
13295         
13296         this.list.on('scroll', this.onViewScroll, this);
13297         
13298         if(!this.tpl){
13299             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>';
13300         }
13301
13302         this.view = new Roo.View(this.list, this.tpl, {
13303             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13304         });
13305         
13306         //this.view.wrapEl.setDisplayed(false);
13307         this.view.on('click', this.onViewClick, this);
13308         
13309         
13310         
13311         this.store.on('beforeload', this.onBeforeLoad, this);
13312         this.store.on('load', this.onLoad, this);
13313         this.store.on('loadexception', this.onLoadException, this);
13314         
13315         if(this.editable){
13316             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13317                 "up" : function(e){
13318                     this.inKeyMode = true;
13319                     this.selectPrev();
13320                 },
13321
13322                 "down" : function(e){
13323                     this.inKeyMode = true;
13324                     this.selectNext();
13325                 },
13326
13327                 "enter" : function(e){
13328                     if(this.fireEvent("specialkey", this, e)){
13329                         this.onViewClick(false);
13330                     }
13331                     
13332                     return true;
13333                 },
13334
13335                 "esc" : function(e){
13336                     this.onTickableFooterButtonClick(e, false, false);
13337                 },
13338
13339                 "tab" : function(e){
13340                     this.fireEvent("specialkey", this, e);
13341                     
13342                     this.onTickableFooterButtonClick(e, false, false);
13343                     
13344                     return true;
13345                 },
13346
13347                 scope : this,
13348
13349                 doRelay : function(e, fn, key){
13350                     if(this.scope.isExpanded()){
13351                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13352                     }
13353                     return true;
13354                 },
13355
13356                 forceKeyDown: true
13357             });
13358         }
13359         
13360         this.queryDelay = Math.max(this.queryDelay || 10,
13361                 this.mode == 'local' ? 10 : 250);
13362         
13363         
13364         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13365         
13366         if(this.typeAhead){
13367             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13368         }
13369         
13370         if(this.editable !== false){
13371             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13372         }
13373         
13374         this.indicator = this.indicatorEl();
13375         
13376         if(this.indicator){
13377             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13378             this.indicator.hide();
13379         }
13380         
13381     },
13382
13383     onDestroy : function(){
13384         if(this.view){
13385             this.view.setStore(null);
13386             this.view.el.removeAllListeners();
13387             this.view.el.remove();
13388             this.view.purgeListeners();
13389         }
13390         if(this.list){
13391             this.list.dom.innerHTML  = '';
13392         }
13393         
13394         if(this.store){
13395             this.store.un('beforeload', this.onBeforeLoad, this);
13396             this.store.un('load', this.onLoad, this);
13397             this.store.un('loadexception', this.onLoadException, this);
13398         }
13399         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13400     },
13401
13402     // private
13403     fireKey : function(e){
13404         if(e.isNavKeyPress() && !this.list.isVisible()){
13405             this.fireEvent("specialkey", this, e);
13406         }
13407     },
13408
13409     // private
13410     onResize: function(w, h){
13411 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13412 //        
13413 //        if(typeof w != 'number'){
13414 //            // we do not handle it!?!?
13415 //            return;
13416 //        }
13417 //        var tw = this.trigger.getWidth();
13418 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13419 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13420 //        var x = w - tw;
13421 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13422 //            
13423 //        //this.trigger.setStyle('left', x+'px');
13424 //        
13425 //        if(this.list && this.listWidth === undefined){
13426 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13427 //            this.list.setWidth(lw);
13428 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13429 //        }
13430         
13431     
13432         
13433     },
13434
13435     /**
13436      * Allow or prevent the user from directly editing the field text.  If false is passed,
13437      * the user will only be able to select from the items defined in the dropdown list.  This method
13438      * is the runtime equivalent of setting the 'editable' config option at config time.
13439      * @param {Boolean} value True to allow the user to directly edit the field text
13440      */
13441     setEditable : function(value){
13442         if(value == this.editable){
13443             return;
13444         }
13445         this.editable = value;
13446         if(!value){
13447             this.inputEl().dom.setAttribute('readOnly', true);
13448             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13449             this.inputEl().addClass('x-combo-noedit');
13450         }else{
13451             this.inputEl().dom.setAttribute('readOnly', false);
13452             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13453             this.inputEl().removeClass('x-combo-noedit');
13454         }
13455     },
13456
13457     // private
13458     
13459     onBeforeLoad : function(combo,opts){
13460         if(!this.hasFocus){
13461             return;
13462         }
13463          if (!opts.add) {
13464             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13465          }
13466         this.restrictHeight();
13467         this.selectedIndex = -1;
13468     },
13469
13470     // private
13471     onLoad : function(){
13472         
13473         this.hasQuery = false;
13474         
13475         if(!this.hasFocus){
13476             return;
13477         }
13478         
13479         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13480             this.loading.hide();
13481         }
13482         
13483         if(this.store.getCount() > 0){
13484             
13485             this.expand();
13486             this.restrictHeight();
13487             if(this.lastQuery == this.allQuery){
13488                 if(this.editable && !this.tickable){
13489                     this.inputEl().dom.select();
13490                 }
13491                 
13492                 if(
13493                     !this.selectByValue(this.value, true) &&
13494                     this.autoFocus && 
13495                     (
13496                         !this.store.lastOptions ||
13497                         typeof(this.store.lastOptions.add) == 'undefined' || 
13498                         this.store.lastOptions.add != true
13499                     )
13500                 ){
13501                     this.select(0, true);
13502                 }
13503             }else{
13504                 if(this.autoFocus){
13505                     this.selectNext();
13506                 }
13507                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13508                     this.taTask.delay(this.typeAheadDelay);
13509                 }
13510             }
13511         }else{
13512             this.onEmptyResults();
13513         }
13514         
13515         //this.el.focus();
13516     },
13517     // private
13518     onLoadException : function()
13519     {
13520         this.hasQuery = false;
13521         
13522         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13523             this.loading.hide();
13524         }
13525         
13526         if(this.tickable && this.editable){
13527             return;
13528         }
13529         
13530         this.collapse();
13531         // only causes errors at present
13532         //Roo.log(this.store.reader.jsonData);
13533         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13534             // fixme
13535             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13536         //}
13537         
13538         
13539     },
13540     // private
13541     onTypeAhead : function(){
13542         if(this.store.getCount() > 0){
13543             var r = this.store.getAt(0);
13544             var newValue = r.data[this.displayField];
13545             var len = newValue.length;
13546             var selStart = this.getRawValue().length;
13547             
13548             if(selStart != len){
13549                 this.setRawValue(newValue);
13550                 this.selectText(selStart, newValue.length);
13551             }
13552         }
13553     },
13554
13555     // private
13556     onSelect : function(record, index){
13557         
13558         if(this.fireEvent('beforeselect', this, record, index) !== false){
13559         
13560             this.setFromData(index > -1 ? record.data : false);
13561             
13562             this.collapse();
13563             this.fireEvent('select', this, record, index);
13564         }
13565     },
13566
13567     /**
13568      * Returns the currently selected field value or empty string if no value is set.
13569      * @return {String} value The selected value
13570      */
13571     getValue : function()
13572     {
13573         if(Roo.isIOS && this.useNativeIOS){
13574             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13575         }
13576         
13577         if(this.multiple){
13578             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13579         }
13580         
13581         if(this.valueField){
13582             return typeof this.value != 'undefined' ? this.value : '';
13583         }else{
13584             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13585         }
13586     },
13587     
13588     getRawValue : function()
13589     {
13590         if(Roo.isIOS && this.useNativeIOS){
13591             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13592         }
13593         
13594         var v = this.inputEl().getValue();
13595         
13596         return v;
13597     },
13598
13599     /**
13600      * Clears any text/value currently set in the field
13601      */
13602     clearValue : function(){
13603         
13604         if(this.hiddenField){
13605             this.hiddenField.dom.value = '';
13606         }
13607         this.value = '';
13608         this.setRawValue('');
13609         this.lastSelectionText = '';
13610         this.lastData = false;
13611         
13612         var close = this.closeTriggerEl();
13613         
13614         if(close){
13615             close.hide();
13616         }
13617         
13618         this.validate();
13619         
13620     },
13621
13622     /**
13623      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13624      * will be displayed in the field.  If the value does not match the data value of an existing item,
13625      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13626      * Otherwise the field will be blank (although the value will still be set).
13627      * @param {String} value The value to match
13628      */
13629     setValue : function(v)
13630     {
13631         if(Roo.isIOS && this.useNativeIOS){
13632             this.setIOSValue(v);
13633             return;
13634         }
13635         
13636         if(this.multiple){
13637             this.syncValue();
13638             return;
13639         }
13640         
13641         var text = v;
13642         if(this.valueField){
13643             var r = this.findRecord(this.valueField, v);
13644             if(r){
13645                 text = r.data[this.displayField];
13646             }else if(this.valueNotFoundText !== undefined){
13647                 text = this.valueNotFoundText;
13648             }
13649         }
13650         this.lastSelectionText = text;
13651         if(this.hiddenField){
13652             this.hiddenField.dom.value = v;
13653         }
13654         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13655         this.value = v;
13656         
13657         var close = this.closeTriggerEl();
13658         
13659         if(close){
13660             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13661         }
13662         
13663         this.validate();
13664     },
13665     /**
13666      * @property {Object} the last set data for the element
13667      */
13668     
13669     lastData : false,
13670     /**
13671      * Sets the value of the field based on a object which is related to the record format for the store.
13672      * @param {Object} value the value to set as. or false on reset?
13673      */
13674     setFromData : function(o){
13675         
13676         if(this.multiple){
13677             this.addItem(o);
13678             return;
13679         }
13680             
13681         var dv = ''; // display value
13682         var vv = ''; // value value..
13683         this.lastData = o;
13684         if (this.displayField) {
13685             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13686         } else {
13687             // this is an error condition!!!
13688             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13689         }
13690         
13691         if(this.valueField){
13692             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13693         }
13694         
13695         var close = this.closeTriggerEl();
13696         
13697         if(close){
13698             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
13699         }
13700         
13701         if(this.hiddenField){
13702             this.hiddenField.dom.value = vv;
13703             
13704             this.lastSelectionText = dv;
13705             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13706             this.value = vv;
13707             return;
13708         }
13709         // no hidden field.. - we store the value in 'value', but still display
13710         // display field!!!!
13711         this.lastSelectionText = dv;
13712         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13713         this.value = vv;
13714         
13715         
13716         
13717     },
13718     // private
13719     reset : function(){
13720         // overridden so that last data is reset..
13721         
13722         if(this.multiple){
13723             this.clearItem();
13724             return;
13725         }
13726         
13727         this.setValue(this.originalValue);
13728         //this.clearInvalid();
13729         this.lastData = false;
13730         if (this.view) {
13731             this.view.clearSelections();
13732         }
13733         
13734         this.validate();
13735     },
13736     // private
13737     findRecord : function(prop, value){
13738         var record;
13739         if(this.store.getCount() > 0){
13740             this.store.each(function(r){
13741                 if(r.data[prop] == value){
13742                     record = r;
13743                     return false;
13744                 }
13745                 return true;
13746             });
13747         }
13748         return record;
13749     },
13750     
13751     getName: function()
13752     {
13753         // returns hidden if it's set..
13754         if (!this.rendered) {return ''};
13755         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13756         
13757     },
13758     // private
13759     onViewMove : function(e, t){
13760         this.inKeyMode = false;
13761     },
13762
13763     // private
13764     onViewOver : function(e, t){
13765         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13766             return;
13767         }
13768         var item = this.view.findItemFromChild(t);
13769         
13770         if(item){
13771             var index = this.view.indexOf(item);
13772             this.select(index, false);
13773         }
13774     },
13775
13776     // private
13777     onViewClick : function(view, doFocus, el, e)
13778     {
13779         var index = this.view.getSelectedIndexes()[0];
13780         
13781         var r = this.store.getAt(index);
13782         
13783         if(this.tickable){
13784             
13785             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13786                 return;
13787             }
13788             
13789             var rm = false;
13790             var _this = this;
13791             
13792             Roo.each(this.tickItems, function(v,k){
13793                 
13794                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13795                     Roo.log(v);
13796                     _this.tickItems.splice(k, 1);
13797                     
13798                     if(typeof(e) == 'undefined' && view == false){
13799                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13800                     }
13801                     
13802                     rm = true;
13803                     return;
13804                 }
13805             });
13806             
13807             if(rm){
13808                 return;
13809             }
13810             
13811             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13812                 this.tickItems.push(r.data);
13813             }
13814             
13815             if(typeof(e) == 'undefined' && view == false){
13816                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13817             }
13818                     
13819             return;
13820         }
13821         
13822         if(r){
13823             this.onSelect(r, index);
13824         }
13825         if(doFocus !== false && !this.blockFocus){
13826             this.inputEl().focus();
13827         }
13828     },
13829
13830     // private
13831     restrictHeight : function(){
13832         //this.innerList.dom.style.height = '';
13833         //var inner = this.innerList.dom;
13834         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13835         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13836         //this.list.beginUpdate();
13837         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13838         this.list.alignTo(this.inputEl(), this.listAlign);
13839         this.list.alignTo(this.inputEl(), this.listAlign);
13840         //this.list.endUpdate();
13841     },
13842
13843     // private
13844     onEmptyResults : function(){
13845         
13846         if(this.tickable && this.editable){
13847             this.restrictHeight();
13848             return;
13849         }
13850         
13851         this.collapse();
13852     },
13853
13854     /**
13855      * Returns true if the dropdown list is expanded, else false.
13856      */
13857     isExpanded : function(){
13858         return this.list.isVisible();
13859     },
13860
13861     /**
13862      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13863      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13864      * @param {String} value The data value of the item to select
13865      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13866      * selected item if it is not currently in view (defaults to true)
13867      * @return {Boolean} True if the value matched an item in the list, else false
13868      */
13869     selectByValue : function(v, scrollIntoView){
13870         if(v !== undefined && v !== null){
13871             var r = this.findRecord(this.valueField || this.displayField, v);
13872             if(r){
13873                 this.select(this.store.indexOf(r), scrollIntoView);
13874                 return true;
13875             }
13876         }
13877         return false;
13878     },
13879
13880     /**
13881      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
13882      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13883      * @param {Number} index The zero-based index of the list item to select
13884      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13885      * selected item if it is not currently in view (defaults to true)
13886      */
13887     select : function(index, scrollIntoView){
13888         this.selectedIndex = index;
13889         this.view.select(index);
13890         if(scrollIntoView !== false){
13891             var el = this.view.getNode(index);
13892             /*
13893              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
13894              */
13895             if(el){
13896                 this.list.scrollChildIntoView(el, false);
13897             }
13898         }
13899     },
13900
13901     // private
13902     selectNext : function(){
13903         var ct = this.store.getCount();
13904         if(ct > 0){
13905             if(this.selectedIndex == -1){
13906                 this.select(0);
13907             }else if(this.selectedIndex < ct-1){
13908                 this.select(this.selectedIndex+1);
13909             }
13910         }
13911     },
13912
13913     // private
13914     selectPrev : function(){
13915         var ct = this.store.getCount();
13916         if(ct > 0){
13917             if(this.selectedIndex == -1){
13918                 this.select(0);
13919             }else if(this.selectedIndex != 0){
13920                 this.select(this.selectedIndex-1);
13921             }
13922         }
13923     },
13924
13925     // private
13926     onKeyUp : function(e){
13927         if(this.editable !== false && !e.isSpecialKey()){
13928             this.lastKey = e.getKey();
13929             this.dqTask.delay(this.queryDelay);
13930         }
13931     },
13932
13933     // private
13934     validateBlur : function(){
13935         return !this.list || !this.list.isVisible();   
13936     },
13937
13938     // private
13939     initQuery : function(){
13940         
13941         var v = this.getRawValue();
13942         
13943         if(this.tickable && this.editable){
13944             v = this.tickableInputEl().getValue();
13945         }
13946         
13947         this.doQuery(v);
13948     },
13949
13950     // private
13951     doForce : function(){
13952         if(this.inputEl().dom.value.length > 0){
13953             this.inputEl().dom.value =
13954                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
13955              
13956         }
13957     },
13958
13959     /**
13960      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
13961      * query allowing the query action to be canceled if needed.
13962      * @param {String} query The SQL query to execute
13963      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
13964      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
13965      * saved in the current store (defaults to false)
13966      */
13967     doQuery : function(q, forceAll){
13968         
13969         if(q === undefined || q === null){
13970             q = '';
13971         }
13972         var qe = {
13973             query: q,
13974             forceAll: forceAll,
13975             combo: this,
13976             cancel:false
13977         };
13978         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
13979             return false;
13980         }
13981         q = qe.query;
13982         
13983         forceAll = qe.forceAll;
13984         if(forceAll === true || (q.length >= this.minChars)){
13985             
13986             this.hasQuery = true;
13987             
13988             if(this.lastQuery != q || this.alwaysQuery){
13989                 this.lastQuery = q;
13990                 if(this.mode == 'local'){
13991                     this.selectedIndex = -1;
13992                     if(forceAll){
13993                         this.store.clearFilter();
13994                     }else{
13995                         
13996                         if(this.specialFilter){
13997                             this.fireEvent('specialfilter', this);
13998                             this.onLoad();
13999                             return;
14000                         }
14001                         
14002                         this.store.filter(this.displayField, q);
14003                     }
14004                     
14005                     this.store.fireEvent("datachanged", this.store);
14006                     
14007                     this.onLoad();
14008                     
14009                     
14010                 }else{
14011                     
14012                     this.store.baseParams[this.queryParam] = q;
14013                     
14014                     var options = {params : this.getParams(q)};
14015                     
14016                     if(this.loadNext){
14017                         options.add = true;
14018                         options.params.start = this.page * this.pageSize;
14019                     }
14020                     
14021                     this.store.load(options);
14022                     
14023                     /*
14024                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14025                      *  we should expand the list on onLoad
14026                      *  so command out it
14027                      */
14028 //                    this.expand();
14029                 }
14030             }else{
14031                 this.selectedIndex = -1;
14032                 this.onLoad();   
14033             }
14034         }
14035         
14036         this.loadNext = false;
14037     },
14038     
14039     // private
14040     getParams : function(q){
14041         var p = {};
14042         //p[this.queryParam] = q;
14043         
14044         if(this.pageSize){
14045             p.start = 0;
14046             p.limit = this.pageSize;
14047         }
14048         return p;
14049     },
14050
14051     /**
14052      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14053      */
14054     collapse : function(){
14055         if(!this.isExpanded()){
14056             return;
14057         }
14058         
14059         this.list.hide();
14060         
14061         this.hasFocus = false;
14062         
14063         if(this.tickable){
14064             this.okBtn.hide();
14065             this.cancelBtn.hide();
14066             this.trigger.show();
14067             
14068             if(this.editable){
14069                 this.tickableInputEl().dom.value = '';
14070                 this.tickableInputEl().blur();
14071             }
14072             
14073         }
14074         
14075         Roo.get(document).un('mousedown', this.collapseIf, this);
14076         Roo.get(document).un('mousewheel', this.collapseIf, this);
14077         if (!this.editable) {
14078             Roo.get(document).un('keydown', this.listKeyPress, this);
14079         }
14080         this.fireEvent('collapse', this);
14081         
14082         this.validate();
14083     },
14084
14085     // private
14086     collapseIf : function(e){
14087         var in_combo  = e.within(this.el);
14088         var in_list =  e.within(this.list);
14089         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14090         
14091         if (in_combo || in_list || is_list) {
14092             //e.stopPropagation();
14093             return;
14094         }
14095         
14096         if(this.tickable){
14097             this.onTickableFooterButtonClick(e, false, false);
14098         }
14099
14100         this.collapse();
14101         
14102     },
14103
14104     /**
14105      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14106      */
14107     expand : function(){
14108        
14109         if(this.isExpanded() || !this.hasFocus){
14110             return;
14111         }
14112         
14113         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14114         this.list.setWidth(lw);
14115         
14116         Roo.log('expand');
14117         
14118         this.list.show();
14119         
14120         this.restrictHeight();
14121         
14122         if(this.tickable){
14123             
14124             this.tickItems = Roo.apply([], this.item);
14125             
14126             this.okBtn.show();
14127             this.cancelBtn.show();
14128             this.trigger.hide();
14129             
14130             if(this.editable){
14131                 this.tickableInputEl().focus();
14132             }
14133             
14134         }
14135         
14136         Roo.get(document).on('mousedown', this.collapseIf, this);
14137         Roo.get(document).on('mousewheel', this.collapseIf, this);
14138         if (!this.editable) {
14139             Roo.get(document).on('keydown', this.listKeyPress, this);
14140         }
14141         
14142         this.fireEvent('expand', this);
14143     },
14144
14145     // private
14146     // Implements the default empty TriggerField.onTriggerClick function
14147     onTriggerClick : function(e)
14148     {
14149         Roo.log('trigger click');
14150         
14151         if(this.disabled || !this.triggerList){
14152             return;
14153         }
14154         
14155         this.page = 0;
14156         this.loadNext = false;
14157         
14158         if(this.isExpanded()){
14159             this.collapse();
14160             if (!this.blockFocus) {
14161                 this.inputEl().focus();
14162             }
14163             
14164         }else {
14165             this.hasFocus = true;
14166             if(this.triggerAction == 'all') {
14167                 this.doQuery(this.allQuery, true);
14168             } else {
14169                 this.doQuery(this.getRawValue());
14170             }
14171             if (!this.blockFocus) {
14172                 this.inputEl().focus();
14173             }
14174         }
14175     },
14176     
14177     onTickableTriggerClick : function(e)
14178     {
14179         if(this.disabled){
14180             return;
14181         }
14182         
14183         this.page = 0;
14184         this.loadNext = false;
14185         this.hasFocus = true;
14186         
14187         if(this.triggerAction == 'all') {
14188             this.doQuery(this.allQuery, true);
14189         } else {
14190             this.doQuery(this.getRawValue());
14191         }
14192     },
14193     
14194     onSearchFieldClick : function(e)
14195     {
14196         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14197             this.onTickableFooterButtonClick(e, false, false);
14198             return;
14199         }
14200         
14201         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14202             return;
14203         }
14204         
14205         this.page = 0;
14206         this.loadNext = false;
14207         this.hasFocus = true;
14208         
14209         if(this.triggerAction == 'all') {
14210             this.doQuery(this.allQuery, true);
14211         } else {
14212             this.doQuery(this.getRawValue());
14213         }
14214     },
14215     
14216     listKeyPress : function(e)
14217     {
14218         //Roo.log('listkeypress');
14219         // scroll to first matching element based on key pres..
14220         if (e.isSpecialKey()) {
14221             return false;
14222         }
14223         var k = String.fromCharCode(e.getKey()).toUpperCase();
14224         //Roo.log(k);
14225         var match  = false;
14226         var csel = this.view.getSelectedNodes();
14227         var cselitem = false;
14228         if (csel.length) {
14229             var ix = this.view.indexOf(csel[0]);
14230             cselitem  = this.store.getAt(ix);
14231             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14232                 cselitem = false;
14233             }
14234             
14235         }
14236         
14237         this.store.each(function(v) { 
14238             if (cselitem) {
14239                 // start at existing selection.
14240                 if (cselitem.id == v.id) {
14241                     cselitem = false;
14242                 }
14243                 return true;
14244             }
14245                 
14246             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14247                 match = this.store.indexOf(v);
14248                 return false;
14249             }
14250             return true;
14251         }, this);
14252         
14253         if (match === false) {
14254             return true; // no more action?
14255         }
14256         // scroll to?
14257         this.view.select(match);
14258         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14259         sn.scrollIntoView(sn.dom.parentNode, false);
14260     },
14261     
14262     onViewScroll : function(e, t){
14263         
14264         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){
14265             return;
14266         }
14267         
14268         this.hasQuery = true;
14269         
14270         this.loading = this.list.select('.loading', true).first();
14271         
14272         if(this.loading === null){
14273             this.list.createChild({
14274                 tag: 'div',
14275                 cls: 'loading roo-select2-more-results roo-select2-active',
14276                 html: 'Loading more results...'
14277             });
14278             
14279             this.loading = this.list.select('.loading', true).first();
14280             
14281             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14282             
14283             this.loading.hide();
14284         }
14285         
14286         this.loading.show();
14287         
14288         var _combo = this;
14289         
14290         this.page++;
14291         this.loadNext = true;
14292         
14293         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14294         
14295         return;
14296     },
14297     
14298     addItem : function(o)
14299     {   
14300         var dv = ''; // display value
14301         
14302         if (this.displayField) {
14303             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14304         } else {
14305             // this is an error condition!!!
14306             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14307         }
14308         
14309         if(!dv.length){
14310             return;
14311         }
14312         
14313         var choice = this.choices.createChild({
14314             tag: 'li',
14315             cls: 'roo-select2-search-choice',
14316             cn: [
14317                 {
14318                     tag: 'div',
14319                     html: dv
14320                 },
14321                 {
14322                     tag: 'a',
14323                     href: '#',
14324                     cls: 'roo-select2-search-choice-close fa fa-times',
14325                     tabindex: '-1'
14326                 }
14327             ]
14328             
14329         }, this.searchField);
14330         
14331         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14332         
14333         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14334         
14335         this.item.push(o);
14336         
14337         this.lastData = o;
14338         
14339         this.syncValue();
14340         
14341         this.inputEl().dom.value = '';
14342         
14343         this.validate();
14344     },
14345     
14346     onRemoveItem : function(e, _self, o)
14347     {
14348         e.preventDefault();
14349         
14350         this.lastItem = Roo.apply([], this.item);
14351         
14352         var index = this.item.indexOf(o.data) * 1;
14353         
14354         if( index < 0){
14355             Roo.log('not this item?!');
14356             return;
14357         }
14358         
14359         this.item.splice(index, 1);
14360         o.item.remove();
14361         
14362         this.syncValue();
14363         
14364         this.fireEvent('remove', this, e);
14365         
14366         this.validate();
14367         
14368     },
14369     
14370     syncValue : function()
14371     {
14372         if(!this.item.length){
14373             this.clearValue();
14374             return;
14375         }
14376             
14377         var value = [];
14378         var _this = this;
14379         Roo.each(this.item, function(i){
14380             if(_this.valueField){
14381                 value.push(i[_this.valueField]);
14382                 return;
14383             }
14384
14385             value.push(i);
14386         });
14387
14388         this.value = value.join(',');
14389
14390         if(this.hiddenField){
14391             this.hiddenField.dom.value = this.value;
14392         }
14393         
14394         this.store.fireEvent("datachanged", this.store);
14395         
14396         this.validate();
14397     },
14398     
14399     clearItem : function()
14400     {
14401         if(!this.multiple){
14402             return;
14403         }
14404         
14405         this.item = [];
14406         
14407         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14408            c.remove();
14409         });
14410         
14411         this.syncValue();
14412         
14413         this.validate();
14414         
14415         if(this.tickable && !Roo.isTouch){
14416             this.view.refresh();
14417         }
14418     },
14419     
14420     inputEl: function ()
14421     {
14422         if(Roo.isIOS && this.useNativeIOS){
14423             return this.el.select('select.roo-ios-select', true).first();
14424         }
14425         
14426         if(Roo.isTouch && this.mobileTouchView){
14427             return this.el.select('input.form-control',true).first();
14428         }
14429         
14430         if(this.tickable){
14431             return this.searchField;
14432         }
14433         
14434         return this.el.select('input.form-control',true).first();
14435     },
14436     
14437     onTickableFooterButtonClick : function(e, btn, el)
14438     {
14439         e.preventDefault();
14440         
14441         this.lastItem = Roo.apply([], this.item);
14442         
14443         if(btn && btn.name == 'cancel'){
14444             this.tickItems = Roo.apply([], this.item);
14445             this.collapse();
14446             return;
14447         }
14448         
14449         this.clearItem();
14450         
14451         var _this = this;
14452         
14453         Roo.each(this.tickItems, function(o){
14454             _this.addItem(o);
14455         });
14456         
14457         this.collapse();
14458         
14459     },
14460     
14461     validate : function()
14462     {
14463         var v = this.getRawValue();
14464         
14465         if(this.multiple){
14466             v = this.getValue();
14467         }
14468         
14469         if(this.disabled || this.allowBlank || v.length){
14470             this.markValid();
14471             return true;
14472         }
14473         
14474         this.markInvalid();
14475         return false;
14476     },
14477     
14478     tickableInputEl : function()
14479     {
14480         if(!this.tickable || !this.editable){
14481             return this.inputEl();
14482         }
14483         
14484         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14485     },
14486     
14487     
14488     getAutoCreateTouchView : function()
14489     {
14490         var id = Roo.id();
14491         
14492         var cfg = {
14493             cls: 'form-group' //input-group
14494         };
14495         
14496         var input =  {
14497             tag: 'input',
14498             id : id,
14499             type : this.inputType,
14500             cls : 'form-control x-combo-noedit',
14501             autocomplete: 'new-password',
14502             placeholder : this.placeholder || '',
14503             readonly : true
14504         };
14505         
14506         if (this.name) {
14507             input.name = this.name;
14508         }
14509         
14510         if (this.size) {
14511             input.cls += ' input-' + this.size;
14512         }
14513         
14514         if (this.disabled) {
14515             input.disabled = true;
14516         }
14517         
14518         var inputblock = {
14519             cls : '',
14520             cn : [
14521                 input
14522             ]
14523         };
14524         
14525         if(this.before){
14526             inputblock.cls += ' input-group';
14527             
14528             inputblock.cn.unshift({
14529                 tag :'span',
14530                 cls : 'input-group-addon',
14531                 html : this.before
14532             });
14533         }
14534         
14535         if(this.removable && !this.multiple){
14536             inputblock.cls += ' roo-removable';
14537             
14538             inputblock.cn.push({
14539                 tag: 'button',
14540                 html : 'x',
14541                 cls : 'roo-combo-removable-btn close'
14542             });
14543         }
14544
14545         if(this.hasFeedback && !this.allowBlank){
14546             
14547             inputblock.cls += ' has-feedback';
14548             
14549             inputblock.cn.push({
14550                 tag: 'span',
14551                 cls: 'glyphicon form-control-feedback'
14552             });
14553             
14554         }
14555         
14556         if (this.after) {
14557             
14558             inputblock.cls += (this.before) ? '' : ' input-group';
14559             
14560             inputblock.cn.push({
14561                 tag :'span',
14562                 cls : 'input-group-addon',
14563                 html : this.after
14564             });
14565         }
14566
14567         var box = {
14568             tag: 'div',
14569             cn: [
14570                 {
14571                     tag: 'input',
14572                     type : 'hidden',
14573                     cls: 'form-hidden-field'
14574                 },
14575                 inputblock
14576             ]
14577             
14578         };
14579         
14580         if(this.multiple){
14581             box = {
14582                 tag: 'div',
14583                 cn: [
14584                     {
14585                         tag: 'input',
14586                         type : 'hidden',
14587                         cls: 'form-hidden-field'
14588                     },
14589                     {
14590                         tag: 'ul',
14591                         cls: 'roo-select2-choices',
14592                         cn:[
14593                             {
14594                                 tag: 'li',
14595                                 cls: 'roo-select2-search-field',
14596                                 cn: [
14597
14598                                     inputblock
14599                                 ]
14600                             }
14601                         ]
14602                     }
14603                 ]
14604             }
14605         };
14606         
14607         var combobox = {
14608             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14609             cn: [
14610                 box
14611             ]
14612         };
14613         
14614         if(!this.multiple && this.showToggleBtn){
14615             
14616             var caret = {
14617                         tag: 'span',
14618                         cls: 'caret'
14619             };
14620             
14621             if (this.caret != false) {
14622                 caret = {
14623                      tag: 'i',
14624                      cls: 'fa fa-' + this.caret
14625                 };
14626                 
14627             }
14628             
14629             combobox.cn.push({
14630                 tag :'span',
14631                 cls : 'input-group-addon btn dropdown-toggle',
14632                 cn : [
14633                     caret,
14634                     {
14635                         tag: 'span',
14636                         cls: 'combobox-clear',
14637                         cn  : [
14638                             {
14639                                 tag : 'i',
14640                                 cls: 'icon-remove'
14641                             }
14642                         ]
14643                     }
14644                 ]
14645
14646             })
14647         }
14648         
14649         if(this.multiple){
14650             combobox.cls += ' roo-select2-container-multi';
14651         }
14652         
14653         var align = this.labelAlign || this.parentLabelAlign();
14654         
14655         if (align ==='left' && this.fieldLabel.length) {
14656
14657             cfg.cn = [
14658                 {
14659                    tag : 'i',
14660                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14661                    tooltip : 'This field is required'
14662                 },
14663                 {
14664                     tag: 'label',
14665                     cls : 'control-label',
14666                     html : this.fieldLabel
14667
14668                 },
14669                 {
14670                     cls : '', 
14671                     cn: [
14672                         combobox
14673                     ]
14674                 }
14675             ];
14676             
14677             var labelCfg = cfg.cn[1];
14678             var contentCfg = cfg.cn[2];
14679             
14680
14681             if(this.indicatorpos == 'right'){
14682                 cfg.cn = [
14683                     {
14684                         tag: 'label',
14685                         cls : 'control-label',
14686                         html : this.fieldLabel,
14687                         cn : [
14688                             {
14689                                tag : 'i',
14690                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14691                                tooltip : 'This field is required'
14692                             }
14693                         ]
14694                     },
14695                     {
14696                         cls : '', 
14697                         cn: [
14698                             combobox
14699                         ]
14700                     }
14701                 ];
14702             }
14703             
14704             labelCfg = cfg.cn[0];
14705             contentCfg = cfg.cn[2];
14706             
14707             if(this.labelWidth > 12){
14708                 labelCfg.style = "width: " + this.labelWidth + 'px';
14709             }
14710             
14711             if(this.labelWidth < 13 && this.labelmd == 0){
14712                 this.labelmd = this.labelWidth;
14713             }
14714             
14715             if(this.labellg > 0){
14716                 labelCfg.cls += ' col-lg-' + this.labellg;
14717                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14718             }
14719             
14720             if(this.labelmd > 0){
14721                 labelCfg.cls += ' col-md-' + this.labelmd;
14722                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14723             }
14724             
14725             if(this.labelsm > 0){
14726                 labelCfg.cls += ' col-sm-' + this.labelsm;
14727                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14728             }
14729             
14730             if(this.labelxs > 0){
14731                 labelCfg.cls += ' col-xs-' + this.labelxs;
14732                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14733             }
14734                 
14735                 
14736         } else if ( this.fieldLabel.length) {
14737             cfg.cn = [
14738                 {
14739                    tag : 'i',
14740                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14741                    tooltip : 'This field is required'
14742                 },
14743                 {
14744                     tag: 'label',
14745                     cls : 'control-label',
14746                     html : this.fieldLabel
14747
14748                 },
14749                 {
14750                     cls : '', 
14751                     cn: [
14752                         combobox
14753                     ]
14754                 }
14755             ];
14756             
14757             if(this.indicatorpos == 'right'){
14758                 cfg.cn = [
14759                     {
14760                         tag: 'label',
14761                         cls : 'control-label',
14762                         html : this.fieldLabel,
14763                         cn : [
14764                             {
14765                                tag : 'i',
14766                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14767                                tooltip : 'This field is required'
14768                             }
14769                         ]
14770                     },
14771                     {
14772                         cls : '', 
14773                         cn: [
14774                             combobox
14775                         ]
14776                     }
14777                 ];
14778             }
14779         } else {
14780             cfg.cn = combobox;    
14781         }
14782         
14783         
14784         var settings = this;
14785         
14786         ['xs','sm','md','lg'].map(function(size){
14787             if (settings[size]) {
14788                 cfg.cls += ' col-' + size + '-' + settings[size];
14789             }
14790         });
14791         
14792         return cfg;
14793     },
14794     
14795     initTouchView : function()
14796     {
14797         this.renderTouchView();
14798         
14799         this.touchViewEl.on('scroll', function(){
14800             this.el.dom.scrollTop = 0;
14801         }, this);
14802         
14803         this.originalValue = this.getValue();
14804         
14805         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14806         
14807         this.inputEl().on("click", this.showTouchView, this);
14808         if (this.triggerEl) {
14809             this.triggerEl.on("click", this.showTouchView, this);
14810         }
14811         
14812         
14813         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14814         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14815         
14816         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14817         
14818         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14819         this.store.on('load', this.onTouchViewLoad, this);
14820         this.store.on('loadexception', this.onTouchViewLoadException, this);
14821         
14822         if(this.hiddenName){
14823             
14824             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14825             
14826             this.hiddenField.dom.value =
14827                 this.hiddenValue !== undefined ? this.hiddenValue :
14828                 this.value !== undefined ? this.value : '';
14829         
14830             this.el.dom.removeAttribute('name');
14831             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14832         }
14833         
14834         if(this.multiple){
14835             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14836             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14837         }
14838         
14839         if(this.removable && !this.multiple){
14840             var close = this.closeTriggerEl();
14841             if(close){
14842                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14843                 close.on('click', this.removeBtnClick, this, close);
14844             }
14845         }
14846         /*
14847          * fix the bug in Safari iOS8
14848          */
14849         this.inputEl().on("focus", function(e){
14850             document.activeElement.blur();
14851         }, this);
14852         
14853         return;
14854         
14855         
14856     },
14857     
14858     renderTouchView : function()
14859     {
14860         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14861         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14862         
14863         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
14864         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14865         
14866         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
14867         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14868         this.touchViewBodyEl.setStyle('overflow', 'auto');
14869         
14870         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
14871         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14872         
14873         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
14874         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14875         
14876     },
14877     
14878     showTouchView : function()
14879     {
14880         if(this.disabled){
14881             return;
14882         }
14883         
14884         this.touchViewHeaderEl.hide();
14885
14886         if(this.modalTitle.length){
14887             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
14888             this.touchViewHeaderEl.show();
14889         }
14890
14891         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
14892         this.touchViewEl.show();
14893
14894         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
14895         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
14896                 Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
14897
14898         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
14899
14900         if(this.modalTitle.length){
14901             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
14902         }
14903         
14904         this.touchViewBodyEl.setHeight(bodyHeight);
14905
14906         if(this.animate){
14907             var _this = this;
14908             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
14909         }else{
14910             this.touchViewEl.addClass('in');
14911         }
14912
14913         this.doTouchViewQuery();
14914         
14915     },
14916     
14917     hideTouchView : function()
14918     {
14919         this.touchViewEl.removeClass('in');
14920
14921         if(this.animate){
14922             var _this = this;
14923             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
14924         }else{
14925             this.touchViewEl.setStyle('display', 'none');
14926         }
14927         
14928     },
14929     
14930     setTouchViewValue : function()
14931     {
14932         if(this.multiple){
14933             this.clearItem();
14934         
14935             var _this = this;
14936
14937             Roo.each(this.tickItems, function(o){
14938                 this.addItem(o);
14939             }, this);
14940         }
14941         
14942         this.hideTouchView();
14943     },
14944     
14945     doTouchViewQuery : function()
14946     {
14947         var qe = {
14948             query: '',
14949             forceAll: true,
14950             combo: this,
14951             cancel:false
14952         };
14953         
14954         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
14955             return false;
14956         }
14957         
14958         if(!this.alwaysQuery || this.mode == 'local'){
14959             this.onTouchViewLoad();
14960             return;
14961         }
14962         
14963         this.store.load();
14964     },
14965     
14966     onTouchViewBeforeLoad : function(combo,opts)
14967     {
14968         return;
14969     },
14970
14971     // private
14972     onTouchViewLoad : function()
14973     {
14974         if(this.store.getCount() < 1){
14975             this.onTouchViewEmptyResults();
14976             return;
14977         }
14978         
14979         this.clearTouchView();
14980         
14981         var rawValue = this.getRawValue();
14982         
14983         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
14984         
14985         this.tickItems = [];
14986         
14987         this.store.data.each(function(d, rowIndex){
14988             var row = this.touchViewListGroup.createChild(template);
14989             
14990             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
14991                 row.addClass(d.data.cls);
14992             }
14993             
14994             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
14995                 var cfg = {
14996                     data : d.data,
14997                     html : d.data[this.displayField]
14998                 };
14999                 
15000                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15001                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15002                 }
15003             }
15004             row.removeClass('selected');
15005             if(!this.multiple && this.valueField &&
15006                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15007             {
15008                 // radio buttons..
15009                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15010                 row.addClass('selected');
15011             }
15012             
15013             if(this.multiple && this.valueField &&
15014                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15015             {
15016                 
15017                 // checkboxes...
15018                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15019                 this.tickItems.push(d.data);
15020             }
15021             
15022             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15023             
15024         }, this);
15025         
15026         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15027         
15028         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15029
15030         if(this.modalTitle.length){
15031             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15032         }
15033
15034         var listHeight = this.touchViewListGroup.getHeight();
15035         
15036         var _this = this;
15037         
15038         if(firstChecked && listHeight > bodyHeight){
15039             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15040         }
15041         
15042     },
15043     
15044     onTouchViewLoadException : function()
15045     {
15046         this.hideTouchView();
15047     },
15048     
15049     onTouchViewEmptyResults : function()
15050     {
15051         this.clearTouchView();
15052         
15053         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15054         
15055         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15056         
15057     },
15058     
15059     clearTouchView : function()
15060     {
15061         this.touchViewListGroup.dom.innerHTML = '';
15062     },
15063     
15064     onTouchViewClick : function(e, el, o)
15065     {
15066         e.preventDefault();
15067         
15068         var row = o.row;
15069         var rowIndex = o.rowIndex;
15070         
15071         var r = this.store.getAt(rowIndex);
15072         
15073         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15074             
15075             if(!this.multiple){
15076                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15077                     c.dom.removeAttribute('checked');
15078                 }, this);
15079
15080                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15081
15082                 this.setFromData(r.data);
15083
15084                 var close = this.closeTriggerEl();
15085
15086                 if(close){
15087                     close.show();
15088                 }
15089
15090                 this.hideTouchView();
15091
15092                 this.fireEvent('select', this, r, rowIndex);
15093
15094                 return;
15095             }
15096
15097             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15098                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15099                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15100                 return;
15101             }
15102
15103             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15104             this.addItem(r.data);
15105             this.tickItems.push(r.data);
15106         }
15107     },
15108     
15109     getAutoCreateNativeIOS : function()
15110     {
15111         var cfg = {
15112             cls: 'form-group' //input-group,
15113         };
15114         
15115         var combobox =  {
15116             tag: 'select',
15117             cls : 'roo-ios-select'
15118         };
15119         
15120         if (this.name) {
15121             combobox.name = this.name;
15122         }
15123         
15124         if (this.disabled) {
15125             combobox.disabled = true;
15126         }
15127         
15128         var settings = this;
15129         
15130         ['xs','sm','md','lg'].map(function(size){
15131             if (settings[size]) {
15132                 cfg.cls += ' col-' + size + '-' + settings[size];
15133             }
15134         });
15135         
15136         cfg.cn = combobox;
15137         
15138         return cfg;
15139         
15140     },
15141     
15142     initIOSView : function()
15143     {
15144         this.store.on('load', this.onIOSViewLoad, this);
15145         
15146         return;
15147     },
15148     
15149     onIOSViewLoad : function()
15150     {
15151         if(this.store.getCount() < 1){
15152             return;
15153         }
15154         
15155         this.clearIOSView();
15156         
15157         if(this.allowBlank) {
15158             
15159             var default_text = '-- SELECT --';
15160             
15161             var opt = this.inputEl().createChild({
15162                 tag: 'option',
15163                 value : 0,
15164                 html : default_text
15165             });
15166             
15167             var o = {};
15168             o[this.valueField] = 0;
15169             o[this.displayField] = default_text;
15170             
15171             this.ios_options.push({
15172                 data : o,
15173                 el : opt
15174             });
15175             
15176         }
15177         
15178         this.store.data.each(function(d, rowIndex){
15179             
15180             var html = '';
15181             
15182             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15183                 html = d.data[this.displayField];
15184             }
15185             
15186             var value = '';
15187             
15188             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15189                 value = d.data[this.valueField];
15190             }
15191             
15192             var option = {
15193                 tag: 'option',
15194                 value : value,
15195                 html : html
15196             };
15197             
15198             if(this.value == d.data[this.valueField]){
15199                 option['selected'] = true;
15200             }
15201             
15202             var opt = this.inputEl().createChild(option);
15203             
15204             this.ios_options.push({
15205                 data : d.data,
15206                 el : opt
15207             });
15208             
15209         }, this);
15210         
15211         this.inputEl().on('change', function(){
15212            this.fireEvent('select', this);
15213         }, this);
15214         
15215     },
15216     
15217     clearIOSView: function()
15218     {
15219         this.inputEl().dom.innerHTML = '';
15220         
15221         this.ios_options = [];
15222     },
15223     
15224     setIOSValue: function(v)
15225     {
15226         this.value = v;
15227         
15228         if(!this.ios_options){
15229             return;
15230         }
15231         
15232         Roo.each(this.ios_options, function(opts){
15233            
15234            opts.el.dom.removeAttribute('selected');
15235            
15236            if(opts.data[this.valueField] != v){
15237                return;
15238            }
15239            
15240            opts.el.dom.setAttribute('selected', true);
15241            
15242         }, this);
15243     }
15244
15245     /** 
15246     * @cfg {Boolean} grow 
15247     * @hide 
15248     */
15249     /** 
15250     * @cfg {Number} growMin 
15251     * @hide 
15252     */
15253     /** 
15254     * @cfg {Number} growMax 
15255     * @hide 
15256     */
15257     /**
15258      * @hide
15259      * @method autoSize
15260      */
15261 });
15262
15263 Roo.apply(Roo.bootstrap.ComboBox,  {
15264     
15265     header : {
15266         tag: 'div',
15267         cls: 'modal-header',
15268         cn: [
15269             {
15270                 tag: 'h4',
15271                 cls: 'modal-title'
15272             }
15273         ]
15274     },
15275     
15276     body : {
15277         tag: 'div',
15278         cls: 'modal-body',
15279         cn: [
15280             {
15281                 tag: 'ul',
15282                 cls: 'list-group'
15283             }
15284         ]
15285     },
15286     
15287     listItemRadio : {
15288         tag: 'li',
15289         cls: 'list-group-item',
15290         cn: [
15291             {
15292                 tag: 'span',
15293                 cls: 'roo-combobox-list-group-item-value'
15294             },
15295             {
15296                 tag: 'div',
15297                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15298                 cn: [
15299                     {
15300                         tag: 'input',
15301                         type: 'radio'
15302                     },
15303                     {
15304                         tag: 'label'
15305                     }
15306                 ]
15307             }
15308         ]
15309     },
15310     
15311     listItemCheckbox : {
15312         tag: 'li',
15313         cls: 'list-group-item',
15314         cn: [
15315             {
15316                 tag: 'span',
15317                 cls: 'roo-combobox-list-group-item-value'
15318             },
15319             {
15320                 tag: 'div',
15321                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15322                 cn: [
15323                     {
15324                         tag: 'input',
15325                         type: 'checkbox'
15326                     },
15327                     {
15328                         tag: 'label'
15329                     }
15330                 ]
15331             }
15332         ]
15333     },
15334     
15335     emptyResult : {
15336         tag: 'div',
15337         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15338     },
15339     
15340     footer : {
15341         tag: 'div',
15342         cls: 'modal-footer',
15343         cn: [
15344             {
15345                 tag: 'div',
15346                 cls: 'row',
15347                 cn: [
15348                     {
15349                         tag: 'div',
15350                         cls: 'col-xs-6 text-left',
15351                         cn: {
15352                             tag: 'button',
15353                             cls: 'btn btn-danger roo-touch-view-cancel',
15354                             html: 'Cancel'
15355                         }
15356                     },
15357                     {
15358                         tag: 'div',
15359                         cls: 'col-xs-6 text-right',
15360                         cn: {
15361                             tag: 'button',
15362                             cls: 'btn btn-success roo-touch-view-ok',
15363                             html: 'OK'
15364                         }
15365                     }
15366                 ]
15367             }
15368         ]
15369         
15370     }
15371 });
15372
15373 Roo.apply(Roo.bootstrap.ComboBox,  {
15374     
15375     touchViewTemplate : {
15376         tag: 'div',
15377         cls: 'modal fade roo-combobox-touch-view',
15378         cn: [
15379             {
15380                 tag: 'div',
15381                 cls: 'modal-dialog',
15382                 style : 'position:fixed', // we have to fix position....
15383                 cn: [
15384                     {
15385                         tag: 'div',
15386                         cls: 'modal-content',
15387                         cn: [
15388                             Roo.bootstrap.ComboBox.header,
15389                             Roo.bootstrap.ComboBox.body,
15390                             Roo.bootstrap.ComboBox.footer
15391                         ]
15392                     }
15393                 ]
15394             }
15395         ]
15396     }
15397 });/*
15398  * Based on:
15399  * Ext JS Library 1.1.1
15400  * Copyright(c) 2006-2007, Ext JS, LLC.
15401  *
15402  * Originally Released Under LGPL - original licence link has changed is not relivant.
15403  *
15404  * Fork - LGPL
15405  * <script type="text/javascript">
15406  */
15407
15408 /**
15409  * @class Roo.View
15410  * @extends Roo.util.Observable
15411  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15412  * This class also supports single and multi selection modes. <br>
15413  * Create a data model bound view:
15414  <pre><code>
15415  var store = new Roo.data.Store(...);
15416
15417  var view = new Roo.View({
15418     el : "my-element",
15419     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15420  
15421     singleSelect: true,
15422     selectedClass: "ydataview-selected",
15423     store: store
15424  });
15425
15426  // listen for node click?
15427  view.on("click", function(vw, index, node, e){
15428  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15429  });
15430
15431  // load XML data
15432  dataModel.load("foobar.xml");
15433  </code></pre>
15434  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15435  * <br><br>
15436  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15437  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15438  * 
15439  * Note: old style constructor is still suported (container, template, config)
15440  * 
15441  * @constructor
15442  * Create a new View
15443  * @param {Object} config The config object
15444  * 
15445  */
15446 Roo.View = function(config, depreciated_tpl, depreciated_config){
15447     
15448     this.parent = false;
15449     
15450     if (typeof(depreciated_tpl) == 'undefined') {
15451         // new way.. - universal constructor.
15452         Roo.apply(this, config);
15453         this.el  = Roo.get(this.el);
15454     } else {
15455         // old format..
15456         this.el  = Roo.get(config);
15457         this.tpl = depreciated_tpl;
15458         Roo.apply(this, depreciated_config);
15459     }
15460     this.wrapEl  = this.el.wrap().wrap();
15461     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15462     
15463     
15464     if(typeof(this.tpl) == "string"){
15465         this.tpl = new Roo.Template(this.tpl);
15466     } else {
15467         // support xtype ctors..
15468         this.tpl = new Roo.factory(this.tpl, Roo);
15469     }
15470     
15471     
15472     this.tpl.compile();
15473     
15474     /** @private */
15475     this.addEvents({
15476         /**
15477          * @event beforeclick
15478          * Fires before a click is processed. Returns false to cancel the default action.
15479          * @param {Roo.View} this
15480          * @param {Number} index The index of the target node
15481          * @param {HTMLElement} node The target node
15482          * @param {Roo.EventObject} e The raw event object
15483          */
15484             "beforeclick" : true,
15485         /**
15486          * @event click
15487          * Fires when a template node is clicked.
15488          * @param {Roo.View} this
15489          * @param {Number} index The index of the target node
15490          * @param {HTMLElement} node The target node
15491          * @param {Roo.EventObject} e The raw event object
15492          */
15493             "click" : true,
15494         /**
15495          * @event dblclick
15496          * Fires when a template node is double clicked.
15497          * @param {Roo.View} this
15498          * @param {Number} index The index of the target node
15499          * @param {HTMLElement} node The target node
15500          * @param {Roo.EventObject} e The raw event object
15501          */
15502             "dblclick" : true,
15503         /**
15504          * @event contextmenu
15505          * Fires when a template node is right clicked.
15506          * @param {Roo.View} this
15507          * @param {Number} index The index of the target node
15508          * @param {HTMLElement} node The target node
15509          * @param {Roo.EventObject} e The raw event object
15510          */
15511             "contextmenu" : true,
15512         /**
15513          * @event selectionchange
15514          * Fires when the selected nodes change.
15515          * @param {Roo.View} this
15516          * @param {Array} selections Array of the selected nodes
15517          */
15518             "selectionchange" : true,
15519     
15520         /**
15521          * @event beforeselect
15522          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15523          * @param {Roo.View} this
15524          * @param {HTMLElement} node The node to be selected
15525          * @param {Array} selections Array of currently selected nodes
15526          */
15527             "beforeselect" : true,
15528         /**
15529          * @event preparedata
15530          * Fires on every row to render, to allow you to change the data.
15531          * @param {Roo.View} this
15532          * @param {Object} data to be rendered (change this)
15533          */
15534           "preparedata" : true
15535           
15536           
15537         });
15538
15539
15540
15541     this.el.on({
15542         "click": this.onClick,
15543         "dblclick": this.onDblClick,
15544         "contextmenu": this.onContextMenu,
15545         scope:this
15546     });
15547
15548     this.selections = [];
15549     this.nodes = [];
15550     this.cmp = new Roo.CompositeElementLite([]);
15551     if(this.store){
15552         this.store = Roo.factory(this.store, Roo.data);
15553         this.setStore(this.store, true);
15554     }
15555     
15556     if ( this.footer && this.footer.xtype) {
15557            
15558          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15559         
15560         this.footer.dataSource = this.store;
15561         this.footer.container = fctr;
15562         this.footer = Roo.factory(this.footer, Roo);
15563         fctr.insertFirst(this.el);
15564         
15565         // this is a bit insane - as the paging toolbar seems to detach the el..
15566 //        dom.parentNode.parentNode.parentNode
15567          // they get detached?
15568     }
15569     
15570     
15571     Roo.View.superclass.constructor.call(this);
15572     
15573     
15574 };
15575
15576 Roo.extend(Roo.View, Roo.util.Observable, {
15577     
15578      /**
15579      * @cfg {Roo.data.Store} store Data store to load data from.
15580      */
15581     store : false,
15582     
15583     /**
15584      * @cfg {String|Roo.Element} el The container element.
15585      */
15586     el : '',
15587     
15588     /**
15589      * @cfg {String|Roo.Template} tpl The template used by this View 
15590      */
15591     tpl : false,
15592     /**
15593      * @cfg {String} dataName the named area of the template to use as the data area
15594      *                          Works with domtemplates roo-name="name"
15595      */
15596     dataName: false,
15597     /**
15598      * @cfg {String} selectedClass The css class to add to selected nodes
15599      */
15600     selectedClass : "x-view-selected",
15601      /**
15602      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15603      */
15604     emptyText : "",
15605     
15606     /**
15607      * @cfg {String} text to display on mask (default Loading)
15608      */
15609     mask : false,
15610     /**
15611      * @cfg {Boolean} multiSelect Allow multiple selection
15612      */
15613     multiSelect : false,
15614     /**
15615      * @cfg {Boolean} singleSelect Allow single selection
15616      */
15617     singleSelect:  false,
15618     
15619     /**
15620      * @cfg {Boolean} toggleSelect - selecting 
15621      */
15622     toggleSelect : false,
15623     
15624     /**
15625      * @cfg {Boolean} tickable - selecting 
15626      */
15627     tickable : false,
15628     
15629     /**
15630      * Returns the element this view is bound to.
15631      * @return {Roo.Element}
15632      */
15633     getEl : function(){
15634         return this.wrapEl;
15635     },
15636     
15637     
15638
15639     /**
15640      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15641      */
15642     refresh : function(){
15643         //Roo.log('refresh');
15644         var t = this.tpl;
15645         
15646         // if we are using something like 'domtemplate', then
15647         // the what gets used is:
15648         // t.applySubtemplate(NAME, data, wrapping data..)
15649         // the outer template then get' applied with
15650         //     the store 'extra data'
15651         // and the body get's added to the
15652         //      roo-name="data" node?
15653         //      <span class='roo-tpl-{name}'></span> ?????
15654         
15655         
15656         
15657         this.clearSelections();
15658         this.el.update("");
15659         var html = [];
15660         var records = this.store.getRange();
15661         if(records.length < 1) {
15662             
15663             // is this valid??  = should it render a template??
15664             
15665             this.el.update(this.emptyText);
15666             return;
15667         }
15668         var el = this.el;
15669         if (this.dataName) {
15670             this.el.update(t.apply(this.store.meta)); //????
15671             el = this.el.child('.roo-tpl-' + this.dataName);
15672         }
15673         
15674         for(var i = 0, len = records.length; i < len; i++){
15675             var data = this.prepareData(records[i].data, i, records[i]);
15676             this.fireEvent("preparedata", this, data, i, records[i]);
15677             
15678             var d = Roo.apply({}, data);
15679             
15680             if(this.tickable){
15681                 Roo.apply(d, {'roo-id' : Roo.id()});
15682                 
15683                 var _this = this;
15684             
15685                 Roo.each(this.parent.item, function(item){
15686                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15687                         return;
15688                     }
15689                     Roo.apply(d, {'roo-data-checked' : 'checked'});
15690                 });
15691             }
15692             
15693             html[html.length] = Roo.util.Format.trim(
15694                 this.dataName ?
15695                     t.applySubtemplate(this.dataName, d, this.store.meta) :
15696                     t.apply(d)
15697             );
15698         }
15699         
15700         
15701         
15702         el.update(html.join(""));
15703         this.nodes = el.dom.childNodes;
15704         this.updateIndexes(0);
15705     },
15706     
15707
15708     /**
15709      * Function to override to reformat the data that is sent to
15710      * the template for each node.
15711      * DEPRICATED - use the preparedata event handler.
15712      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15713      * a JSON object for an UpdateManager bound view).
15714      */
15715     prepareData : function(data, index, record)
15716     {
15717         this.fireEvent("preparedata", this, data, index, record);
15718         return data;
15719     },
15720
15721     onUpdate : function(ds, record){
15722         // Roo.log('on update');   
15723         this.clearSelections();
15724         var index = this.store.indexOf(record);
15725         var n = this.nodes[index];
15726         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15727         n.parentNode.removeChild(n);
15728         this.updateIndexes(index, index);
15729     },
15730
15731     
15732     
15733 // --------- FIXME     
15734     onAdd : function(ds, records, index)
15735     {
15736         //Roo.log(['on Add', ds, records, index] );        
15737         this.clearSelections();
15738         if(this.nodes.length == 0){
15739             this.refresh();
15740             return;
15741         }
15742         var n = this.nodes[index];
15743         for(var i = 0, len = records.length; i < len; i++){
15744             var d = this.prepareData(records[i].data, i, records[i]);
15745             if(n){
15746                 this.tpl.insertBefore(n, d);
15747             }else{
15748                 
15749                 this.tpl.append(this.el, d);
15750             }
15751         }
15752         this.updateIndexes(index);
15753     },
15754
15755     onRemove : function(ds, record, index){
15756        // Roo.log('onRemove');
15757         this.clearSelections();
15758         var el = this.dataName  ?
15759             this.el.child('.roo-tpl-' + this.dataName) :
15760             this.el; 
15761         
15762         el.dom.removeChild(this.nodes[index]);
15763         this.updateIndexes(index);
15764     },
15765
15766     /**
15767      * Refresh an individual node.
15768      * @param {Number} index
15769      */
15770     refreshNode : function(index){
15771         this.onUpdate(this.store, this.store.getAt(index));
15772     },
15773
15774     updateIndexes : function(startIndex, endIndex){
15775         var ns = this.nodes;
15776         startIndex = startIndex || 0;
15777         endIndex = endIndex || ns.length - 1;
15778         for(var i = startIndex; i <= endIndex; i++){
15779             ns[i].nodeIndex = i;
15780         }
15781     },
15782
15783     /**
15784      * Changes the data store this view uses and refresh the view.
15785      * @param {Store} store
15786      */
15787     setStore : function(store, initial){
15788         if(!initial && this.store){
15789             this.store.un("datachanged", this.refresh);
15790             this.store.un("add", this.onAdd);
15791             this.store.un("remove", this.onRemove);
15792             this.store.un("update", this.onUpdate);
15793             this.store.un("clear", this.refresh);
15794             this.store.un("beforeload", this.onBeforeLoad);
15795             this.store.un("load", this.onLoad);
15796             this.store.un("loadexception", this.onLoad);
15797         }
15798         if(store){
15799           
15800             store.on("datachanged", this.refresh, this);
15801             store.on("add", this.onAdd, this);
15802             store.on("remove", this.onRemove, this);
15803             store.on("update", this.onUpdate, this);
15804             store.on("clear", this.refresh, this);
15805             store.on("beforeload", this.onBeforeLoad, this);
15806             store.on("load", this.onLoad, this);
15807             store.on("loadexception", this.onLoad, this);
15808         }
15809         
15810         if(store){
15811             this.refresh();
15812         }
15813     },
15814     /**
15815      * onbeforeLoad - masks the loading area.
15816      *
15817      */
15818     onBeforeLoad : function(store,opts)
15819     {
15820          //Roo.log('onBeforeLoad');   
15821         if (!opts.add) {
15822             this.el.update("");
15823         }
15824         this.el.mask(this.mask ? this.mask : "Loading" ); 
15825     },
15826     onLoad : function ()
15827     {
15828         this.el.unmask();
15829     },
15830     
15831
15832     /**
15833      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15834      * @param {HTMLElement} node
15835      * @return {HTMLElement} The template node
15836      */
15837     findItemFromChild : function(node){
15838         var el = this.dataName  ?
15839             this.el.child('.roo-tpl-' + this.dataName,true) :
15840             this.el.dom; 
15841         
15842         if(!node || node.parentNode == el){
15843                     return node;
15844             }
15845             var p = node.parentNode;
15846             while(p && p != el){
15847             if(p.parentNode == el){
15848                 return p;
15849             }
15850             p = p.parentNode;
15851         }
15852             return null;
15853     },
15854
15855     /** @ignore */
15856     onClick : function(e){
15857         var item = this.findItemFromChild(e.getTarget());
15858         if(item){
15859             var index = this.indexOf(item);
15860             if(this.onItemClick(item, index, e) !== false){
15861                 this.fireEvent("click", this, index, item, e);
15862             }
15863         }else{
15864             this.clearSelections();
15865         }
15866     },
15867
15868     /** @ignore */
15869     onContextMenu : function(e){
15870         var item = this.findItemFromChild(e.getTarget());
15871         if(item){
15872             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
15873         }
15874     },
15875
15876     /** @ignore */
15877     onDblClick : function(e){
15878         var item = this.findItemFromChild(e.getTarget());
15879         if(item){
15880             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
15881         }
15882     },
15883
15884     onItemClick : function(item, index, e)
15885     {
15886         if(this.fireEvent("beforeclick", this, index, item, e) === false){
15887             return false;
15888         }
15889         if (this.toggleSelect) {
15890             var m = this.isSelected(item) ? 'unselect' : 'select';
15891             //Roo.log(m);
15892             var _t = this;
15893             _t[m](item, true, false);
15894             return true;
15895         }
15896         if(this.multiSelect || this.singleSelect){
15897             if(this.multiSelect && e.shiftKey && this.lastSelection){
15898                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
15899             }else{
15900                 this.select(item, this.multiSelect && e.ctrlKey);
15901                 this.lastSelection = item;
15902             }
15903             
15904             if(!this.tickable){
15905                 e.preventDefault();
15906             }
15907             
15908         }
15909         return true;
15910     },
15911
15912     /**
15913      * Get the number of selected nodes.
15914      * @return {Number}
15915      */
15916     getSelectionCount : function(){
15917         return this.selections.length;
15918     },
15919
15920     /**
15921      * Get the currently selected nodes.
15922      * @return {Array} An array of HTMLElements
15923      */
15924     getSelectedNodes : function(){
15925         return this.selections;
15926     },
15927
15928     /**
15929      * Get the indexes of the selected nodes.
15930      * @return {Array}
15931      */
15932     getSelectedIndexes : function(){
15933         var indexes = [], s = this.selections;
15934         for(var i = 0, len = s.length; i < len; i++){
15935             indexes.push(s[i].nodeIndex);
15936         }
15937         return indexes;
15938     },
15939
15940     /**
15941      * Clear all selections
15942      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
15943      */
15944     clearSelections : function(suppressEvent){
15945         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
15946             this.cmp.elements = this.selections;
15947             this.cmp.removeClass(this.selectedClass);
15948             this.selections = [];
15949             if(!suppressEvent){
15950                 this.fireEvent("selectionchange", this, this.selections);
15951             }
15952         }
15953     },
15954
15955     /**
15956      * Returns true if the passed node is selected
15957      * @param {HTMLElement/Number} node The node or node index
15958      * @return {Boolean}
15959      */
15960     isSelected : function(node){
15961         var s = this.selections;
15962         if(s.length < 1){
15963             return false;
15964         }
15965         node = this.getNode(node);
15966         return s.indexOf(node) !== -1;
15967     },
15968
15969     /**
15970      * Selects nodes.
15971      * @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
15972      * @param {Boolean} keepExisting (optional) true to keep existing selections
15973      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
15974      */
15975     select : function(nodeInfo, keepExisting, suppressEvent){
15976         if(nodeInfo instanceof Array){
15977             if(!keepExisting){
15978                 this.clearSelections(true);
15979             }
15980             for(var i = 0, len = nodeInfo.length; i < len; i++){
15981                 this.select(nodeInfo[i], true, true);
15982             }
15983             return;
15984         } 
15985         var node = this.getNode(nodeInfo);
15986         if(!node || this.isSelected(node)){
15987             return; // already selected.
15988         }
15989         if(!keepExisting){
15990             this.clearSelections(true);
15991         }
15992         
15993         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
15994             Roo.fly(node).addClass(this.selectedClass);
15995             this.selections.push(node);
15996             if(!suppressEvent){
15997                 this.fireEvent("selectionchange", this, this.selections);
15998             }
15999         }
16000         
16001         
16002     },
16003       /**
16004      * Unselects nodes.
16005      * @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
16006      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16007      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16008      */
16009     unselect : function(nodeInfo, keepExisting, suppressEvent)
16010     {
16011         if(nodeInfo instanceof Array){
16012             Roo.each(this.selections, function(s) {
16013                 this.unselect(s, nodeInfo);
16014             }, this);
16015             return;
16016         }
16017         var node = this.getNode(nodeInfo);
16018         if(!node || !this.isSelected(node)){
16019             //Roo.log("not selected");
16020             return; // not selected.
16021         }
16022         // fireevent???
16023         var ns = [];
16024         Roo.each(this.selections, function(s) {
16025             if (s == node ) {
16026                 Roo.fly(node).removeClass(this.selectedClass);
16027
16028                 return;
16029             }
16030             ns.push(s);
16031         },this);
16032         
16033         this.selections= ns;
16034         this.fireEvent("selectionchange", this, this.selections);
16035     },
16036
16037     /**
16038      * Gets a template node.
16039      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16040      * @return {HTMLElement} The node or null if it wasn't found
16041      */
16042     getNode : function(nodeInfo){
16043         if(typeof nodeInfo == "string"){
16044             return document.getElementById(nodeInfo);
16045         }else if(typeof nodeInfo == "number"){
16046             return this.nodes[nodeInfo];
16047         }
16048         return nodeInfo;
16049     },
16050
16051     /**
16052      * Gets a range template nodes.
16053      * @param {Number} startIndex
16054      * @param {Number} endIndex
16055      * @return {Array} An array of nodes
16056      */
16057     getNodes : function(start, end){
16058         var ns = this.nodes;
16059         start = start || 0;
16060         end = typeof end == "undefined" ? ns.length - 1 : end;
16061         var nodes = [];
16062         if(start <= end){
16063             for(var i = start; i <= end; i++){
16064                 nodes.push(ns[i]);
16065             }
16066         } else{
16067             for(var i = start; i >= end; i--){
16068                 nodes.push(ns[i]);
16069             }
16070         }
16071         return nodes;
16072     },
16073
16074     /**
16075      * Finds the index of the passed node
16076      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16077      * @return {Number} The index of the node or -1
16078      */
16079     indexOf : function(node){
16080         node = this.getNode(node);
16081         if(typeof node.nodeIndex == "number"){
16082             return node.nodeIndex;
16083         }
16084         var ns = this.nodes;
16085         for(var i = 0, len = ns.length; i < len; i++){
16086             if(ns[i] == node){
16087                 return i;
16088             }
16089         }
16090         return -1;
16091     }
16092 });
16093 /*
16094  * - LGPL
16095  *
16096  * based on jquery fullcalendar
16097  * 
16098  */
16099
16100 Roo.bootstrap = Roo.bootstrap || {};
16101 /**
16102  * @class Roo.bootstrap.Calendar
16103  * @extends Roo.bootstrap.Component
16104  * Bootstrap Calendar class
16105  * @cfg {Boolean} loadMask (true|false) default false
16106  * @cfg {Object} header generate the user specific header of the calendar, default false
16107
16108  * @constructor
16109  * Create a new Container
16110  * @param {Object} config The config object
16111  */
16112
16113
16114
16115 Roo.bootstrap.Calendar = function(config){
16116     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16117      this.addEvents({
16118         /**
16119              * @event select
16120              * Fires when a date is selected
16121              * @param {DatePicker} this
16122              * @param {Date} date The selected date
16123              */
16124         'select': true,
16125         /**
16126              * @event monthchange
16127              * Fires when the displayed month changes 
16128              * @param {DatePicker} this
16129              * @param {Date} date The selected month
16130              */
16131         'monthchange': true,
16132         /**
16133              * @event evententer
16134              * Fires when mouse over an event
16135              * @param {Calendar} this
16136              * @param {event} Event
16137              */
16138         'evententer': true,
16139         /**
16140              * @event eventleave
16141              * Fires when the mouse leaves an
16142              * @param {Calendar} this
16143              * @param {event}
16144              */
16145         'eventleave': true,
16146         /**
16147              * @event eventclick
16148              * Fires when the mouse click an
16149              * @param {Calendar} this
16150              * @param {event}
16151              */
16152         'eventclick': true
16153         
16154     });
16155
16156 };
16157
16158 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16159     
16160      /**
16161      * @cfg {Number} startDay
16162      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16163      */
16164     startDay : 0,
16165     
16166     loadMask : false,
16167     
16168     header : false,
16169       
16170     getAutoCreate : function(){
16171         
16172         
16173         var fc_button = function(name, corner, style, content ) {
16174             return Roo.apply({},{
16175                 tag : 'span',
16176                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16177                          (corner.length ?
16178                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16179                             ''
16180                         ),
16181                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16182                 unselectable: 'on'
16183             });
16184         };
16185         
16186         var header = {};
16187         
16188         if(!this.header){
16189             header = {
16190                 tag : 'table',
16191                 cls : 'fc-header',
16192                 style : 'width:100%',
16193                 cn : [
16194                     {
16195                         tag: 'tr',
16196                         cn : [
16197                             {
16198                                 tag : 'td',
16199                                 cls : 'fc-header-left',
16200                                 cn : [
16201                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16202                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16203                                     { tag: 'span', cls: 'fc-header-space' },
16204                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16205
16206
16207                                 ]
16208                             },
16209
16210                             {
16211                                 tag : 'td',
16212                                 cls : 'fc-header-center',
16213                                 cn : [
16214                                     {
16215                                         tag: 'span',
16216                                         cls: 'fc-header-title',
16217                                         cn : {
16218                                             tag: 'H2',
16219                                             html : 'month / year'
16220                                         }
16221                                     }
16222
16223                                 ]
16224                             },
16225                             {
16226                                 tag : 'td',
16227                                 cls : 'fc-header-right',
16228                                 cn : [
16229                               /*      fc_button('month', 'left', '', 'month' ),
16230                                     fc_button('week', '', '', 'week' ),
16231                                     fc_button('day', 'right', '', 'day' )
16232                                 */    
16233
16234                                 ]
16235                             }
16236
16237                         ]
16238                     }
16239                 ]
16240             };
16241         }
16242         
16243         header = this.header;
16244         
16245        
16246         var cal_heads = function() {
16247             var ret = [];
16248             // fixme - handle this.
16249             
16250             for (var i =0; i < Date.dayNames.length; i++) {
16251                 var d = Date.dayNames[i];
16252                 ret.push({
16253                     tag: 'th',
16254                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16255                     html : d.substring(0,3)
16256                 });
16257                 
16258             }
16259             ret[0].cls += ' fc-first';
16260             ret[6].cls += ' fc-last';
16261             return ret;
16262         };
16263         var cal_cell = function(n) {
16264             return  {
16265                 tag: 'td',
16266                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16267                 cn : [
16268                     {
16269                         cn : [
16270                             {
16271                                 cls: 'fc-day-number',
16272                                 html: 'D'
16273                             },
16274                             {
16275                                 cls: 'fc-day-content',
16276                              
16277                                 cn : [
16278                                      {
16279                                         style: 'position: relative;' // height: 17px;
16280                                     }
16281                                 ]
16282                             }
16283                             
16284                             
16285                         ]
16286                     }
16287                 ]
16288                 
16289             }
16290         };
16291         var cal_rows = function() {
16292             
16293             var ret = [];
16294             for (var r = 0; r < 6; r++) {
16295                 var row= {
16296                     tag : 'tr',
16297                     cls : 'fc-week',
16298                     cn : []
16299                 };
16300                 
16301                 for (var i =0; i < Date.dayNames.length; i++) {
16302                     var d = Date.dayNames[i];
16303                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16304
16305                 }
16306                 row.cn[0].cls+=' fc-first';
16307                 row.cn[0].cn[0].style = 'min-height:90px';
16308                 row.cn[6].cls+=' fc-last';
16309                 ret.push(row);
16310                 
16311             }
16312             ret[0].cls += ' fc-first';
16313             ret[4].cls += ' fc-prev-last';
16314             ret[5].cls += ' fc-last';
16315             return ret;
16316             
16317         };
16318         
16319         var cal_table = {
16320             tag: 'table',
16321             cls: 'fc-border-separate',
16322             style : 'width:100%',
16323             cellspacing  : 0,
16324             cn : [
16325                 { 
16326                     tag: 'thead',
16327                     cn : [
16328                         { 
16329                             tag: 'tr',
16330                             cls : 'fc-first fc-last',
16331                             cn : cal_heads()
16332                         }
16333                     ]
16334                 },
16335                 { 
16336                     tag: 'tbody',
16337                     cn : cal_rows()
16338                 }
16339                   
16340             ]
16341         };
16342          
16343          var cfg = {
16344             cls : 'fc fc-ltr',
16345             cn : [
16346                 header,
16347                 {
16348                     cls : 'fc-content',
16349                     style : "position: relative;",
16350                     cn : [
16351                         {
16352                             cls : 'fc-view fc-view-month fc-grid',
16353                             style : 'position: relative',
16354                             unselectable : 'on',
16355                             cn : [
16356                                 {
16357                                     cls : 'fc-event-container',
16358                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16359                                 },
16360                                 cal_table
16361                             ]
16362                         }
16363                     ]
16364     
16365                 }
16366            ] 
16367             
16368         };
16369         
16370          
16371         
16372         return cfg;
16373     },
16374     
16375     
16376     initEvents : function()
16377     {
16378         if(!this.store){
16379             throw "can not find store for calendar";
16380         }
16381         
16382         var mark = {
16383             tag: "div",
16384             cls:"x-dlg-mask",
16385             style: "text-align:center",
16386             cn: [
16387                 {
16388                     tag: "div",
16389                     style: "background-color:white;width:50%;margin:250 auto",
16390                     cn: [
16391                         {
16392                             tag: "img",
16393                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16394                         },
16395                         {
16396                             tag: "span",
16397                             html: "Loading"
16398                         }
16399                         
16400                     ]
16401                 }
16402             ]
16403         };
16404         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16405         
16406         var size = this.el.select('.fc-content', true).first().getSize();
16407         this.maskEl.setSize(size.width, size.height);
16408         this.maskEl.enableDisplayMode("block");
16409         if(!this.loadMask){
16410             this.maskEl.hide();
16411         }
16412         
16413         this.store = Roo.factory(this.store, Roo.data);
16414         this.store.on('load', this.onLoad, this);
16415         this.store.on('beforeload', this.onBeforeLoad, this);
16416         
16417         this.resize();
16418         
16419         this.cells = this.el.select('.fc-day',true);
16420         //Roo.log(this.cells);
16421         this.textNodes = this.el.query('.fc-day-number');
16422         this.cells.addClassOnOver('fc-state-hover');
16423         
16424         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16425         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16426         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16427         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16428         
16429         this.on('monthchange', this.onMonthChange, this);
16430         
16431         this.update(new Date().clearTime());
16432     },
16433     
16434     resize : function() {
16435         var sz  = this.el.getSize();
16436         
16437         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16438         this.el.select('.fc-day-content div',true).setHeight(34);
16439     },
16440     
16441     
16442     // private
16443     showPrevMonth : function(e){
16444         this.update(this.activeDate.add("mo", -1));
16445     },
16446     showToday : function(e){
16447         this.update(new Date().clearTime());
16448     },
16449     // private
16450     showNextMonth : function(e){
16451         this.update(this.activeDate.add("mo", 1));
16452     },
16453
16454     // private
16455     showPrevYear : function(){
16456         this.update(this.activeDate.add("y", -1));
16457     },
16458
16459     // private
16460     showNextYear : function(){
16461         this.update(this.activeDate.add("y", 1));
16462     },
16463
16464     
16465    // private
16466     update : function(date)
16467     {
16468         var vd = this.activeDate;
16469         this.activeDate = date;
16470 //        if(vd && this.el){
16471 //            var t = date.getTime();
16472 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16473 //                Roo.log('using add remove');
16474 //                
16475 //                this.fireEvent('monthchange', this, date);
16476 //                
16477 //                this.cells.removeClass("fc-state-highlight");
16478 //                this.cells.each(function(c){
16479 //                   if(c.dateValue == t){
16480 //                       c.addClass("fc-state-highlight");
16481 //                       setTimeout(function(){
16482 //                            try{c.dom.firstChild.focus();}catch(e){}
16483 //                       }, 50);
16484 //                       return false;
16485 //                   }
16486 //                   return true;
16487 //                });
16488 //                return;
16489 //            }
16490 //        }
16491         
16492         var days = date.getDaysInMonth();
16493         
16494         var firstOfMonth = date.getFirstDateOfMonth();
16495         var startingPos = firstOfMonth.getDay()-this.startDay;
16496         
16497         if(startingPos < this.startDay){
16498             startingPos += 7;
16499         }
16500         
16501         var pm = date.add(Date.MONTH, -1);
16502         var prevStart = pm.getDaysInMonth()-startingPos;
16503 //        
16504         this.cells = this.el.select('.fc-day',true);
16505         this.textNodes = this.el.query('.fc-day-number');
16506         this.cells.addClassOnOver('fc-state-hover');
16507         
16508         var cells = this.cells.elements;
16509         var textEls = this.textNodes;
16510         
16511         Roo.each(cells, function(cell){
16512             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16513         });
16514         
16515         days += startingPos;
16516
16517         // convert everything to numbers so it's fast
16518         var day = 86400000;
16519         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16520         //Roo.log(d);
16521         //Roo.log(pm);
16522         //Roo.log(prevStart);
16523         
16524         var today = new Date().clearTime().getTime();
16525         var sel = date.clearTime().getTime();
16526         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16527         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16528         var ddMatch = this.disabledDatesRE;
16529         var ddText = this.disabledDatesText;
16530         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16531         var ddaysText = this.disabledDaysText;
16532         var format = this.format;
16533         
16534         var setCellClass = function(cal, cell){
16535             cell.row = 0;
16536             cell.events = [];
16537             cell.more = [];
16538             //Roo.log('set Cell Class');
16539             cell.title = "";
16540             var t = d.getTime();
16541             
16542             //Roo.log(d);
16543             
16544             cell.dateValue = t;
16545             if(t == today){
16546                 cell.className += " fc-today";
16547                 cell.className += " fc-state-highlight";
16548                 cell.title = cal.todayText;
16549             }
16550             if(t == sel){
16551                 // disable highlight in other month..
16552                 //cell.className += " fc-state-highlight";
16553                 
16554             }
16555             // disabling
16556             if(t < min) {
16557                 cell.className = " fc-state-disabled";
16558                 cell.title = cal.minText;
16559                 return;
16560             }
16561             if(t > max) {
16562                 cell.className = " fc-state-disabled";
16563                 cell.title = cal.maxText;
16564                 return;
16565             }
16566             if(ddays){
16567                 if(ddays.indexOf(d.getDay()) != -1){
16568                     cell.title = ddaysText;
16569                     cell.className = " fc-state-disabled";
16570                 }
16571             }
16572             if(ddMatch && format){
16573                 var fvalue = d.dateFormat(format);
16574                 if(ddMatch.test(fvalue)){
16575                     cell.title = ddText.replace("%0", fvalue);
16576                     cell.className = " fc-state-disabled";
16577                 }
16578             }
16579             
16580             if (!cell.initialClassName) {
16581                 cell.initialClassName = cell.dom.className;
16582             }
16583             
16584             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16585         };
16586
16587         var i = 0;
16588         
16589         for(; i < startingPos; i++) {
16590             textEls[i].innerHTML = (++prevStart);
16591             d.setDate(d.getDate()+1);
16592             
16593             cells[i].className = "fc-past fc-other-month";
16594             setCellClass(this, cells[i]);
16595         }
16596         
16597         var intDay = 0;
16598         
16599         for(; i < days; i++){
16600             intDay = i - startingPos + 1;
16601             textEls[i].innerHTML = (intDay);
16602             d.setDate(d.getDate()+1);
16603             
16604             cells[i].className = ''; // "x-date-active";
16605             setCellClass(this, cells[i]);
16606         }
16607         var extraDays = 0;
16608         
16609         for(; i < 42; i++) {
16610             textEls[i].innerHTML = (++extraDays);
16611             d.setDate(d.getDate()+1);
16612             
16613             cells[i].className = "fc-future fc-other-month";
16614             setCellClass(this, cells[i]);
16615         }
16616         
16617         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16618         
16619         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16620         
16621         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16622         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16623         
16624         if(totalRows != 6){
16625             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16626             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16627         }
16628         
16629         this.fireEvent('monthchange', this, date);
16630         
16631         
16632         /*
16633         if(!this.internalRender){
16634             var main = this.el.dom.firstChild;
16635             var w = main.offsetWidth;
16636             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16637             Roo.fly(main).setWidth(w);
16638             this.internalRender = true;
16639             // opera does not respect the auto grow header center column
16640             // then, after it gets a width opera refuses to recalculate
16641             // without a second pass
16642             if(Roo.isOpera && !this.secondPass){
16643                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16644                 this.secondPass = true;
16645                 this.update.defer(10, this, [date]);
16646             }
16647         }
16648         */
16649         
16650     },
16651     
16652     findCell : function(dt) {
16653         dt = dt.clearTime().getTime();
16654         var ret = false;
16655         this.cells.each(function(c){
16656             //Roo.log("check " +c.dateValue + '?=' + dt);
16657             if(c.dateValue == dt){
16658                 ret = c;
16659                 return false;
16660             }
16661             return true;
16662         });
16663         
16664         return ret;
16665     },
16666     
16667     findCells : function(ev) {
16668         var s = ev.start.clone().clearTime().getTime();
16669        // Roo.log(s);
16670         var e= ev.end.clone().clearTime().getTime();
16671        // Roo.log(e);
16672         var ret = [];
16673         this.cells.each(function(c){
16674              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16675             
16676             if(c.dateValue > e){
16677                 return ;
16678             }
16679             if(c.dateValue < s){
16680                 return ;
16681             }
16682             ret.push(c);
16683         });
16684         
16685         return ret;    
16686     },
16687     
16688 //    findBestRow: function(cells)
16689 //    {
16690 //        var ret = 0;
16691 //        
16692 //        for (var i =0 ; i < cells.length;i++) {
16693 //            ret  = Math.max(cells[i].rows || 0,ret);
16694 //        }
16695 //        return ret;
16696 //        
16697 //    },
16698     
16699     
16700     addItem : function(ev)
16701     {
16702         // look for vertical location slot in
16703         var cells = this.findCells(ev);
16704         
16705 //        ev.row = this.findBestRow(cells);
16706         
16707         // work out the location.
16708         
16709         var crow = false;
16710         var rows = [];
16711         for(var i =0; i < cells.length; i++) {
16712             
16713             cells[i].row = cells[0].row;
16714             
16715             if(i == 0){
16716                 cells[i].row = cells[i].row + 1;
16717             }
16718             
16719             if (!crow) {
16720                 crow = {
16721                     start : cells[i],
16722                     end :  cells[i]
16723                 };
16724                 continue;
16725             }
16726             if (crow.start.getY() == cells[i].getY()) {
16727                 // on same row.
16728                 crow.end = cells[i];
16729                 continue;
16730             }
16731             // different row.
16732             rows.push(crow);
16733             crow = {
16734                 start: cells[i],
16735                 end : cells[i]
16736             };
16737             
16738         }
16739         
16740         rows.push(crow);
16741         ev.els = [];
16742         ev.rows = rows;
16743         ev.cells = cells;
16744         
16745         cells[0].events.push(ev);
16746         
16747         this.calevents.push(ev);
16748     },
16749     
16750     clearEvents: function() {
16751         
16752         if(!this.calevents){
16753             return;
16754         }
16755         
16756         Roo.each(this.cells.elements, function(c){
16757             c.row = 0;
16758             c.events = [];
16759             c.more = [];
16760         });
16761         
16762         Roo.each(this.calevents, function(e) {
16763             Roo.each(e.els, function(el) {
16764                 el.un('mouseenter' ,this.onEventEnter, this);
16765                 el.un('mouseleave' ,this.onEventLeave, this);
16766                 el.remove();
16767             },this);
16768         },this);
16769         
16770         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16771             e.remove();
16772         });
16773         
16774     },
16775     
16776     renderEvents: function()
16777     {   
16778         var _this = this;
16779         
16780         this.cells.each(function(c) {
16781             
16782             if(c.row < 5){
16783                 return;
16784             }
16785             
16786             var ev = c.events;
16787             
16788             var r = 4;
16789             if(c.row != c.events.length){
16790                 r = 4 - (4 - (c.row - c.events.length));
16791             }
16792             
16793             c.events = ev.slice(0, r);
16794             c.more = ev.slice(r);
16795             
16796             if(c.more.length && c.more.length == 1){
16797                 c.events.push(c.more.pop());
16798             }
16799             
16800             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16801             
16802         });
16803             
16804         this.cells.each(function(c) {
16805             
16806             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16807             
16808             
16809             for (var e = 0; e < c.events.length; e++){
16810                 var ev = c.events[e];
16811                 var rows = ev.rows;
16812                 
16813                 for(var i = 0; i < rows.length; i++) {
16814                 
16815                     // how many rows should it span..
16816
16817                     var  cfg = {
16818                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16819                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16820
16821                         unselectable : "on",
16822                         cn : [
16823                             {
16824                                 cls: 'fc-event-inner',
16825                                 cn : [
16826     //                                {
16827     //                                  tag:'span',
16828     //                                  cls: 'fc-event-time',
16829     //                                  html : cells.length > 1 ? '' : ev.time
16830     //                                },
16831                                     {
16832                                       tag:'span',
16833                                       cls: 'fc-event-title',
16834                                       html : String.format('{0}', ev.title)
16835                                     }
16836
16837
16838                                 ]
16839                             },
16840                             {
16841                                 cls: 'ui-resizable-handle ui-resizable-e',
16842                                 html : '&nbsp;&nbsp;&nbsp'
16843                             }
16844
16845                         ]
16846                     };
16847
16848                     if (i == 0) {
16849                         cfg.cls += ' fc-event-start';
16850                     }
16851                     if ((i+1) == rows.length) {
16852                         cfg.cls += ' fc-event-end';
16853                     }
16854
16855                     var ctr = _this.el.select('.fc-event-container',true).first();
16856                     var cg = ctr.createChild(cfg);
16857
16858                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
16859                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
16860
16861                     var r = (c.more.length) ? 1 : 0;
16862                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
16863                     cg.setWidth(ebox.right - sbox.x -2);
16864
16865                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
16866                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
16867                     cg.on('click', _this.onEventClick, _this, ev);
16868
16869                     ev.els.push(cg);
16870                     
16871                 }
16872                 
16873             }
16874             
16875             
16876             if(c.more.length){
16877                 var  cfg = {
16878                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
16879                     style : 'position: absolute',
16880                     unselectable : "on",
16881                     cn : [
16882                         {
16883                             cls: 'fc-event-inner',
16884                             cn : [
16885                                 {
16886                                   tag:'span',
16887                                   cls: 'fc-event-title',
16888                                   html : 'More'
16889                                 }
16890
16891
16892                             ]
16893                         },
16894                         {
16895                             cls: 'ui-resizable-handle ui-resizable-e',
16896                             html : '&nbsp;&nbsp;&nbsp'
16897                         }
16898
16899                     ]
16900                 };
16901
16902                 var ctr = _this.el.select('.fc-event-container',true).first();
16903                 var cg = ctr.createChild(cfg);
16904
16905                 var sbox = c.select('.fc-day-content',true).first().getBox();
16906                 var ebox = c.select('.fc-day-content',true).first().getBox();
16907                 //Roo.log(cg);
16908                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
16909                 cg.setWidth(ebox.right - sbox.x -2);
16910
16911                 cg.on('click', _this.onMoreEventClick, _this, c.more);
16912                 
16913             }
16914             
16915         });
16916         
16917         
16918         
16919     },
16920     
16921     onEventEnter: function (e, el,event,d) {
16922         this.fireEvent('evententer', this, el, event);
16923     },
16924     
16925     onEventLeave: function (e, el,event,d) {
16926         this.fireEvent('eventleave', this, el, event);
16927     },
16928     
16929     onEventClick: function (e, el,event,d) {
16930         this.fireEvent('eventclick', this, el, event);
16931     },
16932     
16933     onMonthChange: function () {
16934         this.store.load();
16935     },
16936     
16937     onMoreEventClick: function(e, el, more)
16938     {
16939         var _this = this;
16940         
16941         this.calpopover.placement = 'right';
16942         this.calpopover.setTitle('More');
16943         
16944         this.calpopover.setContent('');
16945         
16946         var ctr = this.calpopover.el.select('.popover-content', true).first();
16947         
16948         Roo.each(more, function(m){
16949             var cfg = {
16950                 cls : 'fc-event-hori fc-event-draggable',
16951                 html : m.title
16952             };
16953             var cg = ctr.createChild(cfg);
16954             
16955             cg.on('click', _this.onEventClick, _this, m);
16956         });
16957         
16958         this.calpopover.show(el);
16959         
16960         
16961     },
16962     
16963     onLoad: function () 
16964     {   
16965         this.calevents = [];
16966         var cal = this;
16967         
16968         if(this.store.getCount() > 0){
16969             this.store.data.each(function(d){
16970                cal.addItem({
16971                     id : d.data.id,
16972                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
16973                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
16974                     time : d.data.start_time,
16975                     title : d.data.title,
16976                     description : d.data.description,
16977                     venue : d.data.venue
16978                 });
16979             });
16980         }
16981         
16982         this.renderEvents();
16983         
16984         if(this.calevents.length && this.loadMask){
16985             this.maskEl.hide();
16986         }
16987     },
16988     
16989     onBeforeLoad: function()
16990     {
16991         this.clearEvents();
16992         if(this.loadMask){
16993             this.maskEl.show();
16994         }
16995     }
16996 });
16997
16998  
16999  /*
17000  * - LGPL
17001  *
17002  * element
17003  * 
17004  */
17005
17006 /**
17007  * @class Roo.bootstrap.Popover
17008  * @extends Roo.bootstrap.Component
17009  * Bootstrap Popover class
17010  * @cfg {String} html contents of the popover   (or false to use children..)
17011  * @cfg {String} title of popover (or false to hide)
17012  * @cfg {String} placement how it is placed
17013  * @cfg {String} trigger click || hover (or false to trigger manually)
17014  * @cfg {String} over what (parent or false to trigger manually.)
17015  * @cfg {Number} delay - delay before showing
17016  
17017  * @constructor
17018  * Create a new Popover
17019  * @param {Object} config The config object
17020  */
17021
17022 Roo.bootstrap.Popover = function(config){
17023     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17024     
17025     this.addEvents({
17026         // raw events
17027          /**
17028          * @event show
17029          * After the popover show
17030          * 
17031          * @param {Roo.bootstrap.Popover} this
17032          */
17033         "show" : true,
17034         /**
17035          * @event hide
17036          * After the popover hide
17037          * 
17038          * @param {Roo.bootstrap.Popover} this
17039          */
17040         "hide" : true
17041     });
17042 };
17043
17044 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17045     
17046     title: 'Fill in a title',
17047     html: false,
17048     
17049     placement : 'right',
17050     trigger : 'hover', // hover
17051     
17052     delay : 0,
17053     
17054     over: 'parent',
17055     
17056     can_build_overlaid : false,
17057     
17058     getChildContainer : function()
17059     {
17060         return this.el.select('.popover-content',true).first();
17061     },
17062     
17063     getAutoCreate : function(){
17064          
17065         var cfg = {
17066            cls : 'popover roo-dynamic',
17067            style: 'display:block',
17068            cn : [
17069                 {
17070                     cls : 'arrow'
17071                 },
17072                 {
17073                     cls : 'popover-inner',
17074                     cn : [
17075                         {
17076                             tag: 'h3',
17077                             cls: 'popover-title',
17078                             html : this.title
17079                         },
17080                         {
17081                             cls : 'popover-content',
17082                             html : this.html
17083                         }
17084                     ]
17085                     
17086                 }
17087            ]
17088         };
17089         
17090         return cfg;
17091     },
17092     setTitle: function(str)
17093     {
17094         this.title = str;
17095         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17096     },
17097     setContent: function(str)
17098     {
17099         this.html = str;
17100         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17101     },
17102     // as it get's added to the bottom of the page.
17103     onRender : function(ct, position)
17104     {
17105         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17106         if(!this.el){
17107             var cfg = Roo.apply({},  this.getAutoCreate());
17108             cfg.id = Roo.id();
17109             
17110             if (this.cls) {
17111                 cfg.cls += ' ' + this.cls;
17112             }
17113             if (this.style) {
17114                 cfg.style = this.style;
17115             }
17116             //Roo.log("adding to ");
17117             this.el = Roo.get(document.body).createChild(cfg, position);
17118 //            Roo.log(this.el);
17119         }
17120         this.initEvents();
17121     },
17122     
17123     initEvents : function()
17124     {
17125         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17126         this.el.enableDisplayMode('block');
17127         this.el.hide();
17128         if (this.over === false) {
17129             return; 
17130         }
17131         if (this.triggers === false) {
17132             return;
17133         }
17134         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17135         var triggers = this.trigger ? this.trigger.split(' ') : [];
17136         Roo.each(triggers, function(trigger) {
17137         
17138             if (trigger == 'click') {
17139                 on_el.on('click', this.toggle, this);
17140             } else if (trigger != 'manual') {
17141                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17142                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17143       
17144                 on_el.on(eventIn  ,this.enter, this);
17145                 on_el.on(eventOut, this.leave, this);
17146             }
17147         }, this);
17148         
17149     },
17150     
17151     
17152     // private
17153     timeout : null,
17154     hoverState : null,
17155     
17156     toggle : function () {
17157         this.hoverState == 'in' ? this.leave() : this.enter();
17158     },
17159     
17160     enter : function () {
17161         
17162         clearTimeout(this.timeout);
17163     
17164         this.hoverState = 'in';
17165     
17166         if (!this.delay || !this.delay.show) {
17167             this.show();
17168             return;
17169         }
17170         var _t = this;
17171         this.timeout = setTimeout(function () {
17172             if (_t.hoverState == 'in') {
17173                 _t.show();
17174             }
17175         }, this.delay.show)
17176     },
17177     
17178     leave : function() {
17179         clearTimeout(this.timeout);
17180     
17181         this.hoverState = 'out';
17182     
17183         if (!this.delay || !this.delay.hide) {
17184             this.hide();
17185             return;
17186         }
17187         var _t = this;
17188         this.timeout = setTimeout(function () {
17189             if (_t.hoverState == 'out') {
17190                 _t.hide();
17191             }
17192         }, this.delay.hide)
17193     },
17194     
17195     show : function (on_el)
17196     {
17197         if (!on_el) {
17198             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17199         }
17200         
17201         // set content.
17202         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17203         if (this.html !== false) {
17204             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17205         }
17206         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17207         if (!this.title.length) {
17208             this.el.select('.popover-title',true).hide();
17209         }
17210         
17211         var placement = typeof this.placement == 'function' ?
17212             this.placement.call(this, this.el, on_el) :
17213             this.placement;
17214             
17215         var autoToken = /\s?auto?\s?/i;
17216         var autoPlace = autoToken.test(placement);
17217         if (autoPlace) {
17218             placement = placement.replace(autoToken, '') || 'top';
17219         }
17220         
17221         //this.el.detach()
17222         //this.el.setXY([0,0]);
17223         this.el.show();
17224         this.el.dom.style.display='block';
17225         this.el.addClass(placement);
17226         
17227         //this.el.appendTo(on_el);
17228         
17229         var p = this.getPosition();
17230         var box = this.el.getBox();
17231         
17232         if (autoPlace) {
17233             // fixme..
17234         }
17235         var align = Roo.bootstrap.Popover.alignment[placement];
17236         this.el.alignTo(on_el, align[0],align[1]);
17237         //var arrow = this.el.select('.arrow',true).first();
17238         //arrow.set(align[2], 
17239         
17240         this.el.addClass('in');
17241         
17242         
17243         if (this.el.hasClass('fade')) {
17244             // fade it?
17245         }
17246         
17247         this.hoverState = 'in';
17248         
17249         this.fireEvent('show', this);
17250         
17251     },
17252     hide : function()
17253     {
17254         this.el.setXY([0,0]);
17255         this.el.removeClass('in');
17256         this.el.hide();
17257         this.hoverState = null;
17258         
17259         this.fireEvent('hide', this);
17260     }
17261     
17262 });
17263
17264 Roo.bootstrap.Popover.alignment = {
17265     'left' : ['r-l', [-10,0], 'right'],
17266     'right' : ['l-r', [10,0], 'left'],
17267     'bottom' : ['t-b', [0,10], 'top'],
17268     'top' : [ 'b-t', [0,-10], 'bottom']
17269 };
17270
17271  /*
17272  * - LGPL
17273  *
17274  * Progress
17275  * 
17276  */
17277
17278 /**
17279  * @class Roo.bootstrap.Progress
17280  * @extends Roo.bootstrap.Component
17281  * Bootstrap Progress class
17282  * @cfg {Boolean} striped striped of the progress bar
17283  * @cfg {Boolean} active animated of the progress bar
17284  * 
17285  * 
17286  * @constructor
17287  * Create a new Progress
17288  * @param {Object} config The config object
17289  */
17290
17291 Roo.bootstrap.Progress = function(config){
17292     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17293 };
17294
17295 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17296     
17297     striped : false,
17298     active: false,
17299     
17300     getAutoCreate : function(){
17301         var cfg = {
17302             tag: 'div',
17303             cls: 'progress'
17304         };
17305         
17306         
17307         if(this.striped){
17308             cfg.cls += ' progress-striped';
17309         }
17310       
17311         if(this.active){
17312             cfg.cls += ' active';
17313         }
17314         
17315         
17316         return cfg;
17317     }
17318    
17319 });
17320
17321  
17322
17323  /*
17324  * - LGPL
17325  *
17326  * ProgressBar
17327  * 
17328  */
17329
17330 /**
17331  * @class Roo.bootstrap.ProgressBar
17332  * @extends Roo.bootstrap.Component
17333  * Bootstrap ProgressBar class
17334  * @cfg {Number} aria_valuenow aria-value now
17335  * @cfg {Number} aria_valuemin aria-value min
17336  * @cfg {Number} aria_valuemax aria-value max
17337  * @cfg {String} label label for the progress bar
17338  * @cfg {String} panel (success | info | warning | danger )
17339  * @cfg {String} role role of the progress bar
17340  * @cfg {String} sr_only text
17341  * 
17342  * 
17343  * @constructor
17344  * Create a new ProgressBar
17345  * @param {Object} config The config object
17346  */
17347
17348 Roo.bootstrap.ProgressBar = function(config){
17349     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17350 };
17351
17352 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17353     
17354     aria_valuenow : 0,
17355     aria_valuemin : 0,
17356     aria_valuemax : 100,
17357     label : false,
17358     panel : false,
17359     role : false,
17360     sr_only: false,
17361     
17362     getAutoCreate : function()
17363     {
17364         
17365         var cfg = {
17366             tag: 'div',
17367             cls: 'progress-bar',
17368             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17369         };
17370         
17371         if(this.sr_only){
17372             cfg.cn = {
17373                 tag: 'span',
17374                 cls: 'sr-only',
17375                 html: this.sr_only
17376             }
17377         }
17378         
17379         if(this.role){
17380             cfg.role = this.role;
17381         }
17382         
17383         if(this.aria_valuenow){
17384             cfg['aria-valuenow'] = this.aria_valuenow;
17385         }
17386         
17387         if(this.aria_valuemin){
17388             cfg['aria-valuemin'] = this.aria_valuemin;
17389         }
17390         
17391         if(this.aria_valuemax){
17392             cfg['aria-valuemax'] = this.aria_valuemax;
17393         }
17394         
17395         if(this.label && !this.sr_only){
17396             cfg.html = this.label;
17397         }
17398         
17399         if(this.panel){
17400             cfg.cls += ' progress-bar-' + this.panel;
17401         }
17402         
17403         return cfg;
17404     },
17405     
17406     update : function(aria_valuenow)
17407     {
17408         this.aria_valuenow = aria_valuenow;
17409         
17410         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17411     }
17412    
17413 });
17414
17415  
17416
17417  /*
17418  * - LGPL
17419  *
17420  * column
17421  * 
17422  */
17423
17424 /**
17425  * @class Roo.bootstrap.TabGroup
17426  * @extends Roo.bootstrap.Column
17427  * Bootstrap Column class
17428  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17429  * @cfg {Boolean} carousel true to make the group behave like a carousel
17430  * @cfg {Boolean} bullets show bullets for the panels
17431  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17432  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17433  * @cfg {Boolean} showarrow (true|false) show arrow default true
17434  * 
17435  * @constructor
17436  * Create a new TabGroup
17437  * @param {Object} config The config object
17438  */
17439
17440 Roo.bootstrap.TabGroup = function(config){
17441     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17442     if (!this.navId) {
17443         this.navId = Roo.id();
17444     }
17445     this.tabs = [];
17446     Roo.bootstrap.TabGroup.register(this);
17447     
17448 };
17449
17450 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17451     
17452     carousel : false,
17453     transition : false,
17454     bullets : 0,
17455     timer : 0,
17456     autoslide : false,
17457     slideFn : false,
17458     slideOnTouch : false,
17459     showarrow : true,
17460     
17461     getAutoCreate : function()
17462     {
17463         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17464         
17465         cfg.cls += ' tab-content';
17466         
17467         if (this.carousel) {
17468             cfg.cls += ' carousel slide';
17469             
17470             cfg.cn = [{
17471                cls : 'carousel-inner',
17472                cn : []
17473             }];
17474         
17475             if(this.bullets  && !Roo.isTouch){
17476                 
17477                 var bullets = {
17478                     cls : 'carousel-bullets',
17479                     cn : []
17480                 };
17481                
17482                 if(this.bullets_cls){
17483                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17484                 }
17485                 
17486                 bullets.cn.push({
17487                     cls : 'clear'
17488                 });
17489                 
17490                 cfg.cn[0].cn.push(bullets);
17491             }
17492             
17493             if(this.showarrow){
17494                 cfg.cn[0].cn.push({
17495                     tag : 'div',
17496                     class : 'carousel-arrow',
17497                     cn : [
17498                         {
17499                             tag : 'div',
17500                             class : 'carousel-prev',
17501                             cn : [
17502                                 {
17503                                     tag : 'i',
17504                                     class : 'fa fa-chevron-left'
17505                                 }
17506                             ]
17507                         },
17508                         {
17509                             tag : 'div',
17510                             class : 'carousel-next',
17511                             cn : [
17512                                 {
17513                                     tag : 'i',
17514                                     class : 'fa fa-chevron-right'
17515                                 }
17516                             ]
17517                         }
17518                     ]
17519                 });
17520             }
17521             
17522         }
17523         
17524         return cfg;
17525     },
17526     
17527     initEvents:  function()
17528     {
17529 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17530 //            this.el.on("touchstart", this.onTouchStart, this);
17531 //        }
17532         
17533         if(this.autoslide){
17534             var _this = this;
17535             
17536             this.slideFn = window.setInterval(function() {
17537                 _this.showPanelNext();
17538             }, this.timer);
17539         }
17540         
17541         if(this.showarrow){
17542             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17543             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17544         }
17545         
17546         
17547     },
17548     
17549 //    onTouchStart : function(e, el, o)
17550 //    {
17551 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17552 //            return;
17553 //        }
17554 //        
17555 //        this.showPanelNext();
17556 //    },
17557     
17558     
17559     getChildContainer : function()
17560     {
17561         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17562     },
17563     
17564     /**
17565     * register a Navigation item
17566     * @param {Roo.bootstrap.NavItem} the navitem to add
17567     */
17568     register : function(item)
17569     {
17570         this.tabs.push( item);
17571         item.navId = this.navId; // not really needed..
17572         this.addBullet();
17573     
17574     },
17575     
17576     getActivePanel : function()
17577     {
17578         var r = false;
17579         Roo.each(this.tabs, function(t) {
17580             if (t.active) {
17581                 r = t;
17582                 return false;
17583             }
17584             return null;
17585         });
17586         return r;
17587         
17588     },
17589     getPanelByName : function(n)
17590     {
17591         var r = false;
17592         Roo.each(this.tabs, function(t) {
17593             if (t.tabId == n) {
17594                 r = t;
17595                 return false;
17596             }
17597             return null;
17598         });
17599         return r;
17600     },
17601     indexOfPanel : function(p)
17602     {
17603         var r = false;
17604         Roo.each(this.tabs, function(t,i) {
17605             if (t.tabId == p.tabId) {
17606                 r = i;
17607                 return false;
17608             }
17609             return null;
17610         });
17611         return r;
17612     },
17613     /**
17614      * show a specific panel
17615      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17616      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17617      */
17618     showPanel : function (pan)
17619     {
17620         if(this.transition || typeof(pan) == 'undefined'){
17621             Roo.log("waiting for the transitionend");
17622             return;
17623         }
17624         
17625         if (typeof(pan) == 'number') {
17626             pan = this.tabs[pan];
17627         }
17628         
17629         if (typeof(pan) == 'string') {
17630             pan = this.getPanelByName(pan);
17631         }
17632         
17633         var cur = this.getActivePanel();
17634         
17635         if(!pan || !cur){
17636             Roo.log('pan or acitve pan is undefined');
17637             return false;
17638         }
17639         
17640         if (pan.tabId == this.getActivePanel().tabId) {
17641             return true;
17642         }
17643         
17644         if (false === cur.fireEvent('beforedeactivate')) {
17645             return false;
17646         }
17647         
17648         if(this.bullets > 0 && !Roo.isTouch){
17649             this.setActiveBullet(this.indexOfPanel(pan));
17650         }
17651         
17652         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17653             
17654             this.transition = true;
17655             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17656             var lr = dir == 'next' ? 'left' : 'right';
17657             pan.el.addClass(dir); // or prev
17658             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17659             cur.el.addClass(lr); // or right
17660             pan.el.addClass(lr);
17661             
17662             var _this = this;
17663             cur.el.on('transitionend', function() {
17664                 Roo.log("trans end?");
17665                 
17666                 pan.el.removeClass([lr,dir]);
17667                 pan.setActive(true);
17668                 
17669                 cur.el.removeClass([lr]);
17670                 cur.setActive(false);
17671                 
17672                 _this.transition = false;
17673                 
17674             }, this, { single:  true } );
17675             
17676             return true;
17677         }
17678         
17679         cur.setActive(false);
17680         pan.setActive(true);
17681         
17682         return true;
17683         
17684     },
17685     showPanelNext : function()
17686     {
17687         var i = this.indexOfPanel(this.getActivePanel());
17688         
17689         if (i >= this.tabs.length - 1 && !this.autoslide) {
17690             return;
17691         }
17692         
17693         if (i >= this.tabs.length - 1 && this.autoslide) {
17694             i = -1;
17695         }
17696         
17697         this.showPanel(this.tabs[i+1]);
17698     },
17699     
17700     showPanelPrev : function()
17701     {
17702         var i = this.indexOfPanel(this.getActivePanel());
17703         
17704         if (i  < 1 && !this.autoslide) {
17705             return;
17706         }
17707         
17708         if (i < 1 && this.autoslide) {
17709             i = this.tabs.length;
17710         }
17711         
17712         this.showPanel(this.tabs[i-1]);
17713     },
17714     
17715     
17716     addBullet: function()
17717     {
17718         if(!this.bullets || Roo.isTouch){
17719             return;
17720         }
17721         var ctr = this.el.select('.carousel-bullets',true).first();
17722         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17723         var bullet = ctr.createChild({
17724             cls : 'bullet bullet-' + i
17725         },ctr.dom.lastChild);
17726         
17727         
17728         var _this = this;
17729         
17730         bullet.on('click', (function(e, el, o, ii, t){
17731
17732             e.preventDefault();
17733
17734             this.showPanel(ii);
17735
17736             if(this.autoslide && this.slideFn){
17737                 clearInterval(this.slideFn);
17738                 this.slideFn = window.setInterval(function() {
17739                     _this.showPanelNext();
17740                 }, this.timer);
17741             }
17742
17743         }).createDelegate(this, [i, bullet], true));
17744                 
17745         
17746     },
17747      
17748     setActiveBullet : function(i)
17749     {
17750         if(Roo.isTouch){
17751             return;
17752         }
17753         
17754         Roo.each(this.el.select('.bullet', true).elements, function(el){
17755             el.removeClass('selected');
17756         });
17757
17758         var bullet = this.el.select('.bullet-' + i, true).first();
17759         
17760         if(!bullet){
17761             return;
17762         }
17763         
17764         bullet.addClass('selected');
17765     }
17766     
17767     
17768   
17769 });
17770
17771  
17772
17773  
17774  
17775 Roo.apply(Roo.bootstrap.TabGroup, {
17776     
17777     groups: {},
17778      /**
17779     * register a Navigation Group
17780     * @param {Roo.bootstrap.NavGroup} the navgroup to add
17781     */
17782     register : function(navgrp)
17783     {
17784         this.groups[navgrp.navId] = navgrp;
17785         
17786     },
17787     /**
17788     * fetch a Navigation Group based on the navigation ID
17789     * if one does not exist , it will get created.
17790     * @param {string} the navgroup to add
17791     * @returns {Roo.bootstrap.NavGroup} the navgroup 
17792     */
17793     get: function(navId) {
17794         if (typeof(this.groups[navId]) == 'undefined') {
17795             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17796         }
17797         return this.groups[navId] ;
17798     }
17799     
17800     
17801     
17802 });
17803
17804  /*
17805  * - LGPL
17806  *
17807  * TabPanel
17808  * 
17809  */
17810
17811 /**
17812  * @class Roo.bootstrap.TabPanel
17813  * @extends Roo.bootstrap.Component
17814  * Bootstrap TabPanel class
17815  * @cfg {Boolean} active panel active
17816  * @cfg {String} html panel content
17817  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17818  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17819  * @cfg {String} href click to link..
17820  * 
17821  * 
17822  * @constructor
17823  * Create a new TabPanel
17824  * @param {Object} config The config object
17825  */
17826
17827 Roo.bootstrap.TabPanel = function(config){
17828     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17829     this.addEvents({
17830         /**
17831              * @event changed
17832              * Fires when the active status changes
17833              * @param {Roo.bootstrap.TabPanel} this
17834              * @param {Boolean} state the new state
17835             
17836          */
17837         'changed': true,
17838         /**
17839              * @event beforedeactivate
17840              * Fires before a tab is de-activated - can be used to do validation on a form.
17841              * @param {Roo.bootstrap.TabPanel} this
17842              * @return {Boolean} false if there is an error
17843             
17844          */
17845         'beforedeactivate': true
17846      });
17847     
17848     this.tabId = this.tabId || Roo.id();
17849   
17850 };
17851
17852 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
17853     
17854     active: false,
17855     html: false,
17856     tabId: false,
17857     navId : false,
17858     href : '',
17859     
17860     getAutoCreate : function(){
17861         var cfg = {
17862             tag: 'div',
17863             // item is needed for carousel - not sure if it has any effect otherwise
17864             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
17865             html: this.html || ''
17866         };
17867         
17868         if(this.active){
17869             cfg.cls += ' active';
17870         }
17871         
17872         if(this.tabId){
17873             cfg.tabId = this.tabId;
17874         }
17875         
17876         
17877         return cfg;
17878     },
17879     
17880     initEvents:  function()
17881     {
17882         var p = this.parent();
17883         
17884         this.navId = this.navId || p.navId;
17885         
17886         if (typeof(this.navId) != 'undefined') {
17887             // not really needed.. but just in case.. parent should be a NavGroup.
17888             var tg = Roo.bootstrap.TabGroup.get(this.navId);
17889             
17890             tg.register(this);
17891             
17892             var i = tg.tabs.length - 1;
17893             
17894             if(this.active && tg.bullets > 0 && i < tg.bullets){
17895                 tg.setActiveBullet(i);
17896             }
17897         }
17898         
17899         this.el.on('click', this.onClick, this);
17900         
17901         if(Roo.isTouch){
17902             this.el.on("touchstart", this.onTouchStart, this);
17903             this.el.on("touchmove", this.onTouchMove, this);
17904             this.el.on("touchend", this.onTouchEnd, this);
17905         }
17906         
17907     },
17908     
17909     onRender : function(ct, position)
17910     {
17911         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
17912     },
17913     
17914     setActive : function(state)
17915     {
17916         Roo.log("panel - set active " + this.tabId + "=" + state);
17917         
17918         this.active = state;
17919         if (!state) {
17920             this.el.removeClass('active');
17921             
17922         } else  if (!this.el.hasClass('active')) {
17923             this.el.addClass('active');
17924         }
17925         
17926         this.fireEvent('changed', this, state);
17927     },
17928     
17929     onClick : function(e)
17930     {
17931         e.preventDefault();
17932         
17933         if(!this.href.length){
17934             return;
17935         }
17936         
17937         window.location.href = this.href;
17938     },
17939     
17940     startX : 0,
17941     startY : 0,
17942     endX : 0,
17943     endY : 0,
17944     swiping : false,
17945     
17946     onTouchStart : function(e)
17947     {
17948         this.swiping = false;
17949         
17950         this.startX = e.browserEvent.touches[0].clientX;
17951         this.startY = e.browserEvent.touches[0].clientY;
17952     },
17953     
17954     onTouchMove : function(e)
17955     {
17956         this.swiping = true;
17957         
17958         this.endX = e.browserEvent.touches[0].clientX;
17959         this.endY = e.browserEvent.touches[0].clientY;
17960     },
17961     
17962     onTouchEnd : function(e)
17963     {
17964         if(!this.swiping){
17965             this.onClick(e);
17966             return;
17967         }
17968         
17969         var tabGroup = this.parent();
17970         
17971         if(this.endX > this.startX){ // swiping right
17972             tabGroup.showPanelPrev();
17973             return;
17974         }
17975         
17976         if(this.startX > this.endX){ // swiping left
17977             tabGroup.showPanelNext();
17978             return;
17979         }
17980     }
17981     
17982     
17983 });
17984  
17985
17986  
17987
17988  /*
17989  * - LGPL
17990  *
17991  * DateField
17992  * 
17993  */
17994
17995 /**
17996  * @class Roo.bootstrap.DateField
17997  * @extends Roo.bootstrap.Input
17998  * Bootstrap DateField class
17999  * @cfg {Number} weekStart default 0
18000  * @cfg {String} viewMode default empty, (months|years)
18001  * @cfg {String} minViewMode default empty, (months|years)
18002  * @cfg {Number} startDate default -Infinity
18003  * @cfg {Number} endDate default Infinity
18004  * @cfg {Boolean} todayHighlight default false
18005  * @cfg {Boolean} todayBtn default false
18006  * @cfg {Boolean} calendarWeeks default false
18007  * @cfg {Object} daysOfWeekDisabled default empty
18008  * @cfg {Boolean} singleMode default false (true | false)
18009  * 
18010  * @cfg {Boolean} keyboardNavigation default true
18011  * @cfg {String} language default en
18012  * 
18013  * @constructor
18014  * Create a new DateField
18015  * @param {Object} config The config object
18016  */
18017
18018 Roo.bootstrap.DateField = function(config){
18019     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18020      this.addEvents({
18021             /**
18022              * @event show
18023              * Fires when this field show.
18024              * @param {Roo.bootstrap.DateField} this
18025              * @param {Mixed} date The date value
18026              */
18027             show : true,
18028             /**
18029              * @event show
18030              * Fires when this field hide.
18031              * @param {Roo.bootstrap.DateField} this
18032              * @param {Mixed} date The date value
18033              */
18034             hide : true,
18035             /**
18036              * @event select
18037              * Fires when select a date.
18038              * @param {Roo.bootstrap.DateField} this
18039              * @param {Mixed} date The date value
18040              */
18041             select : true,
18042             /**
18043              * @event beforeselect
18044              * Fires when before select a date.
18045              * @param {Roo.bootstrap.DateField} this
18046              * @param {Mixed} date The date value
18047              */
18048             beforeselect : true
18049         });
18050 };
18051
18052 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18053     
18054     /**
18055      * @cfg {String} format
18056      * The default date format string which can be overriden for localization support.  The format must be
18057      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18058      */
18059     format : "m/d/y",
18060     /**
18061      * @cfg {String} altFormats
18062      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18063      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18064      */
18065     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18066     
18067     weekStart : 0,
18068     
18069     viewMode : '',
18070     
18071     minViewMode : '',
18072     
18073     todayHighlight : false,
18074     
18075     todayBtn: false,
18076     
18077     language: 'en',
18078     
18079     keyboardNavigation: true,
18080     
18081     calendarWeeks: false,
18082     
18083     startDate: -Infinity,
18084     
18085     endDate: Infinity,
18086     
18087     daysOfWeekDisabled: [],
18088     
18089     _events: [],
18090     
18091     singleMode : false,
18092     
18093     UTCDate: function()
18094     {
18095         return new Date(Date.UTC.apply(Date, arguments));
18096     },
18097     
18098     UTCToday: function()
18099     {
18100         var today = new Date();
18101         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18102     },
18103     
18104     getDate: function() {
18105             var d = this.getUTCDate();
18106             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18107     },
18108     
18109     getUTCDate: function() {
18110             return this.date;
18111     },
18112     
18113     setDate: function(d) {
18114             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18115     },
18116     
18117     setUTCDate: function(d) {
18118             this.date = d;
18119             this.setValue(this.formatDate(this.date));
18120     },
18121         
18122     onRender: function(ct, position)
18123     {
18124         
18125         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18126         
18127         this.language = this.language || 'en';
18128         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18129         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18130         
18131         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18132         this.format = this.format || 'm/d/y';
18133         this.isInline = false;
18134         this.isInput = true;
18135         this.component = this.el.select('.add-on', true).first() || false;
18136         this.component = (this.component && this.component.length === 0) ? false : this.component;
18137         this.hasInput = this.component && this.inputEl().length;
18138         
18139         if (typeof(this.minViewMode === 'string')) {
18140             switch (this.minViewMode) {
18141                 case 'months':
18142                     this.minViewMode = 1;
18143                     break;
18144                 case 'years':
18145                     this.minViewMode = 2;
18146                     break;
18147                 default:
18148                     this.minViewMode = 0;
18149                     break;
18150             }
18151         }
18152         
18153         if (typeof(this.viewMode === 'string')) {
18154             switch (this.viewMode) {
18155                 case 'months':
18156                     this.viewMode = 1;
18157                     break;
18158                 case 'years':
18159                     this.viewMode = 2;
18160                     break;
18161                 default:
18162                     this.viewMode = 0;
18163                     break;
18164             }
18165         }
18166                 
18167         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18168         
18169 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18170         
18171         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18172         
18173         this.picker().on('mousedown', this.onMousedown, this);
18174         this.picker().on('click', this.onClick, this);
18175         
18176         this.picker().addClass('datepicker-dropdown');
18177         
18178         this.startViewMode = this.viewMode;
18179         
18180         if(this.singleMode){
18181             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18182                 v.setVisibilityMode(Roo.Element.DISPLAY);
18183                 v.hide();
18184             });
18185             
18186             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18187                 v.setStyle('width', '189px');
18188             });
18189         }
18190         
18191         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18192             if(!this.calendarWeeks){
18193                 v.remove();
18194                 return;
18195             }
18196             
18197             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18198             v.attr('colspan', function(i, val){
18199                 return parseInt(val) + 1;
18200             });
18201         });
18202                         
18203         
18204         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18205         
18206         this.setStartDate(this.startDate);
18207         this.setEndDate(this.endDate);
18208         
18209         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18210         
18211         this.fillDow();
18212         this.fillMonths();
18213         this.update();
18214         this.showMode();
18215         
18216         if(this.isInline) {
18217             this.show();
18218         }
18219     },
18220     
18221     picker : function()
18222     {
18223         return this.pickerEl;
18224 //        return this.el.select('.datepicker', true).first();
18225     },
18226     
18227     fillDow: function()
18228     {
18229         var dowCnt = this.weekStart;
18230         
18231         var dow = {
18232             tag: 'tr',
18233             cn: [
18234                 
18235             ]
18236         };
18237         
18238         if(this.calendarWeeks){
18239             dow.cn.push({
18240                 tag: 'th',
18241                 cls: 'cw',
18242                 html: '&nbsp;'
18243             })
18244         }
18245         
18246         while (dowCnt < this.weekStart + 7) {
18247             dow.cn.push({
18248                 tag: 'th',
18249                 cls: 'dow',
18250                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18251             });
18252         }
18253         
18254         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18255     },
18256     
18257     fillMonths: function()
18258     {    
18259         var i = 0;
18260         var months = this.picker().select('>.datepicker-months td', true).first();
18261         
18262         months.dom.innerHTML = '';
18263         
18264         while (i < 12) {
18265             var month = {
18266                 tag: 'span',
18267                 cls: 'month',
18268                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18269             };
18270             
18271             months.createChild(month);
18272         }
18273         
18274     },
18275     
18276     update: function()
18277     {
18278         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;
18279         
18280         if (this.date < this.startDate) {
18281             this.viewDate = new Date(this.startDate);
18282         } else if (this.date > this.endDate) {
18283             this.viewDate = new Date(this.endDate);
18284         } else {
18285             this.viewDate = new Date(this.date);
18286         }
18287         
18288         this.fill();
18289     },
18290     
18291     fill: function() 
18292     {
18293         var d = new Date(this.viewDate),
18294                 year = d.getUTCFullYear(),
18295                 month = d.getUTCMonth(),
18296                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18297                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18298                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18299                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18300                 currentDate = this.date && this.date.valueOf(),
18301                 today = this.UTCToday();
18302         
18303         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18304         
18305 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18306         
18307 //        this.picker.select('>tfoot th.today').
18308 //                                              .text(dates[this.language].today)
18309 //                                              .toggle(this.todayBtn !== false);
18310     
18311         this.updateNavArrows();
18312         this.fillMonths();
18313                                                 
18314         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18315         
18316         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18317          
18318         prevMonth.setUTCDate(day);
18319         
18320         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18321         
18322         var nextMonth = new Date(prevMonth);
18323         
18324         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18325         
18326         nextMonth = nextMonth.valueOf();
18327         
18328         var fillMonths = false;
18329         
18330         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18331         
18332         while(prevMonth.valueOf() < nextMonth) {
18333             var clsName = '';
18334             
18335             if (prevMonth.getUTCDay() === this.weekStart) {
18336                 if(fillMonths){
18337                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18338                 }
18339                     
18340                 fillMonths = {
18341                     tag: 'tr',
18342                     cn: []
18343                 };
18344                 
18345                 if(this.calendarWeeks){
18346                     // ISO 8601: First week contains first thursday.
18347                     // ISO also states week starts on Monday, but we can be more abstract here.
18348                     var
18349                     // Start of current week: based on weekstart/current date
18350                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18351                     // Thursday of this week
18352                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18353                     // First Thursday of year, year from thursday
18354                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18355                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18356                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18357                     
18358                     fillMonths.cn.push({
18359                         tag: 'td',
18360                         cls: 'cw',
18361                         html: calWeek
18362                     });
18363                 }
18364             }
18365             
18366             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18367                 clsName += ' old';
18368             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18369                 clsName += ' new';
18370             }
18371             if (this.todayHighlight &&
18372                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18373                 prevMonth.getUTCMonth() == today.getMonth() &&
18374                 prevMonth.getUTCDate() == today.getDate()) {
18375                 clsName += ' today';
18376             }
18377             
18378             if (currentDate && prevMonth.valueOf() === currentDate) {
18379                 clsName += ' active';
18380             }
18381             
18382             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18383                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18384                     clsName += ' disabled';
18385             }
18386             
18387             fillMonths.cn.push({
18388                 tag: 'td',
18389                 cls: 'day ' + clsName,
18390                 html: prevMonth.getDate()
18391             });
18392             
18393             prevMonth.setDate(prevMonth.getDate()+1);
18394         }
18395           
18396         var currentYear = this.date && this.date.getUTCFullYear();
18397         var currentMonth = this.date && this.date.getUTCMonth();
18398         
18399         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18400         
18401         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18402             v.removeClass('active');
18403             
18404             if(currentYear === year && k === currentMonth){
18405                 v.addClass('active');
18406             }
18407             
18408             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18409                 v.addClass('disabled');
18410             }
18411             
18412         });
18413         
18414         
18415         year = parseInt(year/10, 10) * 10;
18416         
18417         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18418         
18419         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18420         
18421         year -= 1;
18422         for (var i = -1; i < 11; i++) {
18423             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18424                 tag: 'span',
18425                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18426                 html: year
18427             });
18428             
18429             year += 1;
18430         }
18431     },
18432     
18433     showMode: function(dir) 
18434     {
18435         if (dir) {
18436             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18437         }
18438         
18439         Roo.each(this.picker().select('>div',true).elements, function(v){
18440             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18441             v.hide();
18442         });
18443         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18444     },
18445     
18446     place: function()
18447     {
18448         if(this.isInline) {
18449             return;
18450         }
18451         
18452         this.picker().removeClass(['bottom', 'top']);
18453         
18454         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18455             /*
18456              * place to the top of element!
18457              *
18458              */
18459             
18460             this.picker().addClass('top');
18461             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18462             
18463             return;
18464         }
18465         
18466         this.picker().addClass('bottom');
18467         
18468         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18469     },
18470     
18471     parseDate : function(value)
18472     {
18473         if(!value || value instanceof Date){
18474             return value;
18475         }
18476         var v = Date.parseDate(value, this.format);
18477         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18478             v = Date.parseDate(value, 'Y-m-d');
18479         }
18480         if(!v && this.altFormats){
18481             if(!this.altFormatsArray){
18482                 this.altFormatsArray = this.altFormats.split("|");
18483             }
18484             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18485                 v = Date.parseDate(value, this.altFormatsArray[i]);
18486             }
18487         }
18488         return v;
18489     },
18490     
18491     formatDate : function(date, fmt)
18492     {   
18493         return (!date || !(date instanceof Date)) ?
18494         date : date.dateFormat(fmt || this.format);
18495     },
18496     
18497     onFocus : function()
18498     {
18499         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18500         this.show();
18501     },
18502     
18503     onBlur : function()
18504     {
18505         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18506         
18507         var d = this.inputEl().getValue();
18508         
18509         this.setValue(d);
18510                 
18511         this.hide();
18512     },
18513     
18514     show : function()
18515     {
18516         this.picker().show();
18517         this.update();
18518         this.place();
18519         
18520         this.fireEvent('show', this, this.date);
18521     },
18522     
18523     hide : function()
18524     {
18525         if(this.isInline) {
18526             return;
18527         }
18528         this.picker().hide();
18529         this.viewMode = this.startViewMode;
18530         this.showMode();
18531         
18532         this.fireEvent('hide', this, this.date);
18533         
18534     },
18535     
18536     onMousedown: function(e)
18537     {
18538         e.stopPropagation();
18539         e.preventDefault();
18540     },
18541     
18542     keyup: function(e)
18543     {
18544         Roo.bootstrap.DateField.superclass.keyup.call(this);
18545         this.update();
18546     },
18547
18548     setValue: function(v)
18549     {
18550         if(this.fireEvent('beforeselect', this, v) !== false){
18551             var d = new Date(this.parseDate(v) ).clearTime();
18552         
18553             if(isNaN(d.getTime())){
18554                 this.date = this.viewDate = '';
18555                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18556                 return;
18557             }
18558
18559             v = this.formatDate(d);
18560
18561             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18562
18563             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18564
18565             this.update();
18566
18567             this.fireEvent('select', this, this.date);
18568         }
18569     },
18570     
18571     getValue: function()
18572     {
18573         return this.formatDate(this.date);
18574     },
18575     
18576     fireKey: function(e)
18577     {
18578         if (!this.picker().isVisible()){
18579             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18580                 this.show();
18581             }
18582             return;
18583         }
18584         
18585         var dateChanged = false,
18586         dir, day, month,
18587         newDate, newViewDate;
18588         
18589         switch(e.keyCode){
18590             case 27: // escape
18591                 this.hide();
18592                 e.preventDefault();
18593                 break;
18594             case 37: // left
18595             case 39: // right
18596                 if (!this.keyboardNavigation) {
18597                     break;
18598                 }
18599                 dir = e.keyCode == 37 ? -1 : 1;
18600                 
18601                 if (e.ctrlKey){
18602                     newDate = this.moveYear(this.date, dir);
18603                     newViewDate = this.moveYear(this.viewDate, dir);
18604                 } else if (e.shiftKey){
18605                     newDate = this.moveMonth(this.date, dir);
18606                     newViewDate = this.moveMonth(this.viewDate, dir);
18607                 } else {
18608                     newDate = new Date(this.date);
18609                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18610                     newViewDate = new Date(this.viewDate);
18611                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18612                 }
18613                 if (this.dateWithinRange(newDate)){
18614                     this.date = newDate;
18615                     this.viewDate = newViewDate;
18616                     this.setValue(this.formatDate(this.date));
18617 //                    this.update();
18618                     e.preventDefault();
18619                     dateChanged = true;
18620                 }
18621                 break;
18622             case 38: // up
18623             case 40: // down
18624                 if (!this.keyboardNavigation) {
18625                     break;
18626                 }
18627                 dir = e.keyCode == 38 ? -1 : 1;
18628                 if (e.ctrlKey){
18629                     newDate = this.moveYear(this.date, dir);
18630                     newViewDate = this.moveYear(this.viewDate, dir);
18631                 } else if (e.shiftKey){
18632                     newDate = this.moveMonth(this.date, dir);
18633                     newViewDate = this.moveMonth(this.viewDate, dir);
18634                 } else {
18635                     newDate = new Date(this.date);
18636                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18637                     newViewDate = new Date(this.viewDate);
18638                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18639                 }
18640                 if (this.dateWithinRange(newDate)){
18641                     this.date = newDate;
18642                     this.viewDate = newViewDate;
18643                     this.setValue(this.formatDate(this.date));
18644 //                    this.update();
18645                     e.preventDefault();
18646                     dateChanged = true;
18647                 }
18648                 break;
18649             case 13: // enter
18650                 this.setValue(this.formatDate(this.date));
18651                 this.hide();
18652                 e.preventDefault();
18653                 break;
18654             case 9: // tab
18655                 this.setValue(this.formatDate(this.date));
18656                 this.hide();
18657                 break;
18658             case 16: // shift
18659             case 17: // ctrl
18660             case 18: // alt
18661                 break;
18662             default :
18663                 this.hide();
18664                 
18665         }
18666     },
18667     
18668     
18669     onClick: function(e) 
18670     {
18671         e.stopPropagation();
18672         e.preventDefault();
18673         
18674         var target = e.getTarget();
18675         
18676         if(target.nodeName.toLowerCase() === 'i'){
18677             target = Roo.get(target).dom.parentNode;
18678         }
18679         
18680         var nodeName = target.nodeName;
18681         var className = target.className;
18682         var html = target.innerHTML;
18683         //Roo.log(nodeName);
18684         
18685         switch(nodeName.toLowerCase()) {
18686             case 'th':
18687                 switch(className) {
18688                     case 'switch':
18689                         this.showMode(1);
18690                         break;
18691                     case 'prev':
18692                     case 'next':
18693                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18694                         switch(this.viewMode){
18695                                 case 0:
18696                                         this.viewDate = this.moveMonth(this.viewDate, dir);
18697                                         break;
18698                                 case 1:
18699                                 case 2:
18700                                         this.viewDate = this.moveYear(this.viewDate, dir);
18701                                         break;
18702                         }
18703                         this.fill();
18704                         break;
18705                     case 'today':
18706                         var date = new Date();
18707                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18708 //                        this.fill()
18709                         this.setValue(this.formatDate(this.date));
18710                         
18711                         this.hide();
18712                         break;
18713                 }
18714                 break;
18715             case 'span':
18716                 if (className.indexOf('disabled') < 0) {
18717                     this.viewDate.setUTCDate(1);
18718                     if (className.indexOf('month') > -1) {
18719                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18720                     } else {
18721                         var year = parseInt(html, 10) || 0;
18722                         this.viewDate.setUTCFullYear(year);
18723                         
18724                     }
18725                     
18726                     if(this.singleMode){
18727                         this.setValue(this.formatDate(this.viewDate));
18728                         this.hide();
18729                         return;
18730                     }
18731                     
18732                     this.showMode(-1);
18733                     this.fill();
18734                 }
18735                 break;
18736                 
18737             case 'td':
18738                 //Roo.log(className);
18739                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18740                     var day = parseInt(html, 10) || 1;
18741                     var year = this.viewDate.getUTCFullYear(),
18742                         month = this.viewDate.getUTCMonth();
18743
18744                     if (className.indexOf('old') > -1) {
18745                         if(month === 0 ){
18746                             month = 11;
18747                             year -= 1;
18748                         }else{
18749                             month -= 1;
18750                         }
18751                     } else if (className.indexOf('new') > -1) {
18752                         if (month == 11) {
18753                             month = 0;
18754                             year += 1;
18755                         } else {
18756                             month += 1;
18757                         }
18758                     }
18759                     //Roo.log([year,month,day]);
18760                     this.date = this.UTCDate(year, month, day,0,0,0,0);
18761                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18762 //                    this.fill();
18763                     //Roo.log(this.formatDate(this.date));
18764                     this.setValue(this.formatDate(this.date));
18765                     this.hide();
18766                 }
18767                 break;
18768         }
18769     },
18770     
18771     setStartDate: function(startDate)
18772     {
18773         this.startDate = startDate || -Infinity;
18774         if (this.startDate !== -Infinity) {
18775             this.startDate = this.parseDate(this.startDate);
18776         }
18777         this.update();
18778         this.updateNavArrows();
18779     },
18780
18781     setEndDate: function(endDate)
18782     {
18783         this.endDate = endDate || Infinity;
18784         if (this.endDate !== Infinity) {
18785             this.endDate = this.parseDate(this.endDate);
18786         }
18787         this.update();
18788         this.updateNavArrows();
18789     },
18790     
18791     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18792     {
18793         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18794         if (typeof(this.daysOfWeekDisabled) !== 'object') {
18795             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18796         }
18797         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18798             return parseInt(d, 10);
18799         });
18800         this.update();
18801         this.updateNavArrows();
18802     },
18803     
18804     updateNavArrows: function() 
18805     {
18806         if(this.singleMode){
18807             return;
18808         }
18809         
18810         var d = new Date(this.viewDate),
18811         year = d.getUTCFullYear(),
18812         month = d.getUTCMonth();
18813         
18814         Roo.each(this.picker().select('.prev', true).elements, function(v){
18815             v.show();
18816             switch (this.viewMode) {
18817                 case 0:
18818
18819                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18820                         v.hide();
18821                     }
18822                     break;
18823                 case 1:
18824                 case 2:
18825                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18826                         v.hide();
18827                     }
18828                     break;
18829             }
18830         });
18831         
18832         Roo.each(this.picker().select('.next', true).elements, function(v){
18833             v.show();
18834             switch (this.viewMode) {
18835                 case 0:
18836
18837                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18838                         v.hide();
18839                     }
18840                     break;
18841                 case 1:
18842                 case 2:
18843                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18844                         v.hide();
18845                     }
18846                     break;
18847             }
18848         })
18849     },
18850     
18851     moveMonth: function(date, dir)
18852     {
18853         if (!dir) {
18854             return date;
18855         }
18856         var new_date = new Date(date.valueOf()),
18857         day = new_date.getUTCDate(),
18858         month = new_date.getUTCMonth(),
18859         mag = Math.abs(dir),
18860         new_month, test;
18861         dir = dir > 0 ? 1 : -1;
18862         if (mag == 1){
18863             test = dir == -1
18864             // If going back one month, make sure month is not current month
18865             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
18866             ? function(){
18867                 return new_date.getUTCMonth() == month;
18868             }
18869             // If going forward one month, make sure month is as expected
18870             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
18871             : function(){
18872                 return new_date.getUTCMonth() != new_month;
18873             };
18874             new_month = month + dir;
18875             new_date.setUTCMonth(new_month);
18876             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
18877             if (new_month < 0 || new_month > 11) {
18878                 new_month = (new_month + 12) % 12;
18879             }
18880         } else {
18881             // For magnitudes >1, move one month at a time...
18882             for (var i=0; i<mag; i++) {
18883                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
18884                 new_date = this.moveMonth(new_date, dir);
18885             }
18886             // ...then reset the day, keeping it in the new month
18887             new_month = new_date.getUTCMonth();
18888             new_date.setUTCDate(day);
18889             test = function(){
18890                 return new_month != new_date.getUTCMonth();
18891             };
18892         }
18893         // Common date-resetting loop -- if date is beyond end of month, make it
18894         // end of month
18895         while (test()){
18896             new_date.setUTCDate(--day);
18897             new_date.setUTCMonth(new_month);
18898         }
18899         return new_date;
18900     },
18901
18902     moveYear: function(date, dir)
18903     {
18904         return this.moveMonth(date, dir*12);
18905     },
18906
18907     dateWithinRange: function(date)
18908     {
18909         return date >= this.startDate && date <= this.endDate;
18910     },
18911
18912     
18913     remove: function() 
18914     {
18915         this.picker().remove();
18916     },
18917     
18918     validateValue : function(value)
18919     {
18920         if(value.length < 1)  {
18921             if(this.allowBlank){
18922                 return true;
18923             }
18924             return false;
18925         }
18926         
18927         if(value.length < this.minLength){
18928             return false;
18929         }
18930         if(value.length > this.maxLength){
18931             return false;
18932         }
18933         if(this.vtype){
18934             var vt = Roo.form.VTypes;
18935             if(!vt[this.vtype](value, this)){
18936                 return false;
18937             }
18938         }
18939         if(typeof this.validator == "function"){
18940             var msg = this.validator(value);
18941             if(msg !== true){
18942                 return false;
18943             }
18944         }
18945         
18946         if(this.regex && !this.regex.test(value)){
18947             return false;
18948         }
18949         
18950         if(typeof(this.parseDate(value)) == 'undefined'){
18951             return false;
18952         }
18953         
18954         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
18955             return false;
18956         }      
18957         
18958         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
18959             return false;
18960         } 
18961         
18962         
18963         return true;
18964     }
18965    
18966 });
18967
18968 Roo.apply(Roo.bootstrap.DateField,  {
18969     
18970     head : {
18971         tag: 'thead',
18972         cn: [
18973         {
18974             tag: 'tr',
18975             cn: [
18976             {
18977                 tag: 'th',
18978                 cls: 'prev',
18979                 html: '<i class="fa fa-arrow-left"/>'
18980             },
18981             {
18982                 tag: 'th',
18983                 cls: 'switch',
18984                 colspan: '5'
18985             },
18986             {
18987                 tag: 'th',
18988                 cls: 'next',
18989                 html: '<i class="fa fa-arrow-right"/>'
18990             }
18991
18992             ]
18993         }
18994         ]
18995     },
18996     
18997     content : {
18998         tag: 'tbody',
18999         cn: [
19000         {
19001             tag: 'tr',
19002             cn: [
19003             {
19004                 tag: 'td',
19005                 colspan: '7'
19006             }
19007             ]
19008         }
19009         ]
19010     },
19011     
19012     footer : {
19013         tag: 'tfoot',
19014         cn: [
19015         {
19016             tag: 'tr',
19017             cn: [
19018             {
19019                 tag: 'th',
19020                 colspan: '7',
19021                 cls: 'today'
19022             }
19023                     
19024             ]
19025         }
19026         ]
19027     },
19028     
19029     dates:{
19030         en: {
19031             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19032             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19033             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19034             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19035             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19036             today: "Today"
19037         }
19038     },
19039     
19040     modes: [
19041     {
19042         clsName: 'days',
19043         navFnc: 'Month',
19044         navStep: 1
19045     },
19046     {
19047         clsName: 'months',
19048         navFnc: 'FullYear',
19049         navStep: 1
19050     },
19051     {
19052         clsName: 'years',
19053         navFnc: 'FullYear',
19054         navStep: 10
19055     }]
19056 });
19057
19058 Roo.apply(Roo.bootstrap.DateField,  {
19059   
19060     template : {
19061         tag: 'div',
19062         cls: 'datepicker dropdown-menu roo-dynamic',
19063         cn: [
19064         {
19065             tag: 'div',
19066             cls: 'datepicker-days',
19067             cn: [
19068             {
19069                 tag: 'table',
19070                 cls: 'table-condensed',
19071                 cn:[
19072                 Roo.bootstrap.DateField.head,
19073                 {
19074                     tag: 'tbody'
19075                 },
19076                 Roo.bootstrap.DateField.footer
19077                 ]
19078             }
19079             ]
19080         },
19081         {
19082             tag: 'div',
19083             cls: 'datepicker-months',
19084             cn: [
19085             {
19086                 tag: 'table',
19087                 cls: 'table-condensed',
19088                 cn:[
19089                 Roo.bootstrap.DateField.head,
19090                 Roo.bootstrap.DateField.content,
19091                 Roo.bootstrap.DateField.footer
19092                 ]
19093             }
19094             ]
19095         },
19096         {
19097             tag: 'div',
19098             cls: 'datepicker-years',
19099             cn: [
19100             {
19101                 tag: 'table',
19102                 cls: 'table-condensed',
19103                 cn:[
19104                 Roo.bootstrap.DateField.head,
19105                 Roo.bootstrap.DateField.content,
19106                 Roo.bootstrap.DateField.footer
19107                 ]
19108             }
19109             ]
19110         }
19111         ]
19112     }
19113 });
19114
19115  
19116
19117  /*
19118  * - LGPL
19119  *
19120  * TimeField
19121  * 
19122  */
19123
19124 /**
19125  * @class Roo.bootstrap.TimeField
19126  * @extends Roo.bootstrap.Input
19127  * Bootstrap DateField class
19128  * 
19129  * 
19130  * @constructor
19131  * Create a new TimeField
19132  * @param {Object} config The config object
19133  */
19134
19135 Roo.bootstrap.TimeField = function(config){
19136     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19137     this.addEvents({
19138             /**
19139              * @event show
19140              * Fires when this field show.
19141              * @param {Roo.bootstrap.DateField} thisthis
19142              * @param {Mixed} date The date value
19143              */
19144             show : true,
19145             /**
19146              * @event show
19147              * Fires when this field hide.
19148              * @param {Roo.bootstrap.DateField} this
19149              * @param {Mixed} date The date value
19150              */
19151             hide : true,
19152             /**
19153              * @event select
19154              * Fires when select a date.
19155              * @param {Roo.bootstrap.DateField} this
19156              * @param {Mixed} date The date value
19157              */
19158             select : true
19159         });
19160 };
19161
19162 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19163     
19164     /**
19165      * @cfg {String} format
19166      * The default time format string which can be overriden for localization support.  The format must be
19167      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19168      */
19169     format : "H:i",
19170        
19171     onRender: function(ct, position)
19172     {
19173         
19174         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19175                 
19176         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19177         
19178         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19179         
19180         this.pop = this.picker().select('>.datepicker-time',true).first();
19181         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19182         
19183         this.picker().on('mousedown', this.onMousedown, this);
19184         this.picker().on('click', this.onClick, this);
19185         
19186         this.picker().addClass('datepicker-dropdown');
19187     
19188         this.fillTime();
19189         this.update();
19190             
19191         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19192         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19193         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19194         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19195         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19196         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19197
19198     },
19199     
19200     fireKey: function(e){
19201         if (!this.picker().isVisible()){
19202             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19203                 this.show();
19204             }
19205             return;
19206         }
19207
19208         e.preventDefault();
19209         
19210         switch(e.keyCode){
19211             case 27: // escape
19212                 this.hide();
19213                 break;
19214             case 37: // left
19215             case 39: // right
19216                 this.onTogglePeriod();
19217                 break;
19218             case 38: // up
19219                 this.onIncrementMinutes();
19220                 break;
19221             case 40: // down
19222                 this.onDecrementMinutes();
19223                 break;
19224             case 13: // enter
19225             case 9: // tab
19226                 this.setTime();
19227                 break;
19228         }
19229     },
19230     
19231     onClick: function(e) {
19232         e.stopPropagation();
19233         e.preventDefault();
19234     },
19235     
19236     picker : function()
19237     {
19238         return this.el.select('.datepicker', true).first();
19239     },
19240     
19241     fillTime: function()
19242     {    
19243         var time = this.pop.select('tbody', true).first();
19244         
19245         time.dom.innerHTML = '';
19246         
19247         time.createChild({
19248             tag: 'tr',
19249             cn: [
19250                 {
19251                     tag: 'td',
19252                     cn: [
19253                         {
19254                             tag: 'a',
19255                             href: '#',
19256                             cls: 'btn',
19257                             cn: [
19258                                 {
19259                                     tag: 'span',
19260                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19261                                 }
19262                             ]
19263                         } 
19264                     ]
19265                 },
19266                 {
19267                     tag: 'td',
19268                     cls: 'separator'
19269                 },
19270                 {
19271                     tag: 'td',
19272                     cn: [
19273                         {
19274                             tag: 'a',
19275                             href: '#',
19276                             cls: 'btn',
19277                             cn: [
19278                                 {
19279                                     tag: 'span',
19280                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19281                                 }
19282                             ]
19283                         }
19284                     ]
19285                 },
19286                 {
19287                     tag: 'td',
19288                     cls: 'separator'
19289                 }
19290             ]
19291         });
19292         
19293         time.createChild({
19294             tag: 'tr',
19295             cn: [
19296                 {
19297                     tag: 'td',
19298                     cn: [
19299                         {
19300                             tag: 'span',
19301                             cls: 'timepicker-hour',
19302                             html: '00'
19303                         }  
19304                     ]
19305                 },
19306                 {
19307                     tag: 'td',
19308                     cls: 'separator',
19309                     html: ':'
19310                 },
19311                 {
19312                     tag: 'td',
19313                     cn: [
19314                         {
19315                             tag: 'span',
19316                             cls: 'timepicker-minute',
19317                             html: '00'
19318                         }  
19319                     ]
19320                 },
19321                 {
19322                     tag: 'td',
19323                     cls: 'separator'
19324                 },
19325                 {
19326                     tag: 'td',
19327                     cn: [
19328                         {
19329                             tag: 'button',
19330                             type: 'button',
19331                             cls: 'btn btn-primary period',
19332                             html: 'AM'
19333                             
19334                         }
19335                     ]
19336                 }
19337             ]
19338         });
19339         
19340         time.createChild({
19341             tag: 'tr',
19342             cn: [
19343                 {
19344                     tag: 'td',
19345                     cn: [
19346                         {
19347                             tag: 'a',
19348                             href: '#',
19349                             cls: 'btn',
19350                             cn: [
19351                                 {
19352                                     tag: 'span',
19353                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19354                                 }
19355                             ]
19356                         }
19357                     ]
19358                 },
19359                 {
19360                     tag: 'td',
19361                     cls: 'separator'
19362                 },
19363                 {
19364                     tag: 'td',
19365                     cn: [
19366                         {
19367                             tag: 'a',
19368                             href: '#',
19369                             cls: 'btn',
19370                             cn: [
19371                                 {
19372                                     tag: 'span',
19373                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19374                                 }
19375                             ]
19376                         }
19377                     ]
19378                 },
19379                 {
19380                     tag: 'td',
19381                     cls: 'separator'
19382                 }
19383             ]
19384         });
19385         
19386     },
19387     
19388     update: function()
19389     {
19390         
19391         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19392         
19393         this.fill();
19394     },
19395     
19396     fill: function() 
19397     {
19398         var hours = this.time.getHours();
19399         var minutes = this.time.getMinutes();
19400         var period = 'AM';
19401         
19402         if(hours > 11){
19403             period = 'PM';
19404         }
19405         
19406         if(hours == 0){
19407             hours = 12;
19408         }
19409         
19410         
19411         if(hours > 12){
19412             hours = hours - 12;
19413         }
19414         
19415         if(hours < 10){
19416             hours = '0' + hours;
19417         }
19418         
19419         if(minutes < 10){
19420             minutes = '0' + minutes;
19421         }
19422         
19423         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19424         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19425         this.pop.select('button', true).first().dom.innerHTML = period;
19426         
19427     },
19428     
19429     place: function()
19430     {   
19431         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19432         
19433         var cls = ['bottom'];
19434         
19435         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19436             cls.pop();
19437             cls.push('top');
19438         }
19439         
19440         cls.push('right');
19441         
19442         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19443             cls.pop();
19444             cls.push('left');
19445         }
19446         
19447         this.picker().addClass(cls.join('-'));
19448         
19449         var _this = this;
19450         
19451         Roo.each(cls, function(c){
19452             if(c == 'bottom'){
19453                 _this.picker().setTop(_this.inputEl().getHeight());
19454                 return;
19455             }
19456             if(c == 'top'){
19457                 _this.picker().setTop(0 - _this.picker().getHeight());
19458                 return;
19459             }
19460             
19461             if(c == 'left'){
19462                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19463                 return;
19464             }
19465             if(c == 'right'){
19466                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19467                 return;
19468             }
19469         });
19470         
19471     },
19472   
19473     onFocus : function()
19474     {
19475         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19476         this.show();
19477     },
19478     
19479     onBlur : function()
19480     {
19481         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19482         this.hide();
19483     },
19484     
19485     show : function()
19486     {
19487         this.picker().show();
19488         this.pop.show();
19489         this.update();
19490         this.place();
19491         
19492         this.fireEvent('show', this, this.date);
19493     },
19494     
19495     hide : function()
19496     {
19497         this.picker().hide();
19498         this.pop.hide();
19499         
19500         this.fireEvent('hide', this, this.date);
19501     },
19502     
19503     setTime : function()
19504     {
19505         this.hide();
19506         this.setValue(this.time.format(this.format));
19507         
19508         this.fireEvent('select', this, this.date);
19509         
19510         
19511     },
19512     
19513     onMousedown: function(e){
19514         e.stopPropagation();
19515         e.preventDefault();
19516     },
19517     
19518     onIncrementHours: function()
19519     {
19520         Roo.log('onIncrementHours');
19521         this.time = this.time.add(Date.HOUR, 1);
19522         this.update();
19523         
19524     },
19525     
19526     onDecrementHours: function()
19527     {
19528         Roo.log('onDecrementHours');
19529         this.time = this.time.add(Date.HOUR, -1);
19530         this.update();
19531     },
19532     
19533     onIncrementMinutes: function()
19534     {
19535         Roo.log('onIncrementMinutes');
19536         this.time = this.time.add(Date.MINUTE, 1);
19537         this.update();
19538     },
19539     
19540     onDecrementMinutes: function()
19541     {
19542         Roo.log('onDecrementMinutes');
19543         this.time = this.time.add(Date.MINUTE, -1);
19544         this.update();
19545     },
19546     
19547     onTogglePeriod: function()
19548     {
19549         Roo.log('onTogglePeriod');
19550         this.time = this.time.add(Date.HOUR, 12);
19551         this.update();
19552     }
19553     
19554    
19555 });
19556
19557 Roo.apply(Roo.bootstrap.TimeField,  {
19558     
19559     content : {
19560         tag: 'tbody',
19561         cn: [
19562             {
19563                 tag: 'tr',
19564                 cn: [
19565                 {
19566                     tag: 'td',
19567                     colspan: '7'
19568                 }
19569                 ]
19570             }
19571         ]
19572     },
19573     
19574     footer : {
19575         tag: 'tfoot',
19576         cn: [
19577             {
19578                 tag: 'tr',
19579                 cn: [
19580                 {
19581                     tag: 'th',
19582                     colspan: '7',
19583                     cls: '',
19584                     cn: [
19585                         {
19586                             tag: 'button',
19587                             cls: 'btn btn-info ok',
19588                             html: 'OK'
19589                         }
19590                     ]
19591                 }
19592
19593                 ]
19594             }
19595         ]
19596     }
19597 });
19598
19599 Roo.apply(Roo.bootstrap.TimeField,  {
19600   
19601     template : {
19602         tag: 'div',
19603         cls: 'datepicker dropdown-menu',
19604         cn: [
19605             {
19606                 tag: 'div',
19607                 cls: 'datepicker-time',
19608                 cn: [
19609                 {
19610                     tag: 'table',
19611                     cls: 'table-condensed',
19612                     cn:[
19613                     Roo.bootstrap.TimeField.content,
19614                     Roo.bootstrap.TimeField.footer
19615                     ]
19616                 }
19617                 ]
19618             }
19619         ]
19620     }
19621 });
19622
19623  
19624
19625  /*
19626  * - LGPL
19627  *
19628  * MonthField
19629  * 
19630  */
19631
19632 /**
19633  * @class Roo.bootstrap.MonthField
19634  * @extends Roo.bootstrap.Input
19635  * Bootstrap MonthField class
19636  * 
19637  * @cfg {String} language default en
19638  * 
19639  * @constructor
19640  * Create a new MonthField
19641  * @param {Object} config The config object
19642  */
19643
19644 Roo.bootstrap.MonthField = function(config){
19645     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19646     
19647     this.addEvents({
19648         /**
19649          * @event show
19650          * Fires when this field show.
19651          * @param {Roo.bootstrap.MonthField} this
19652          * @param {Mixed} date The date value
19653          */
19654         show : true,
19655         /**
19656          * @event show
19657          * Fires when this field hide.
19658          * @param {Roo.bootstrap.MonthField} this
19659          * @param {Mixed} date The date value
19660          */
19661         hide : true,
19662         /**
19663          * @event select
19664          * Fires when select a date.
19665          * @param {Roo.bootstrap.MonthField} this
19666          * @param {String} oldvalue The old value
19667          * @param {String} newvalue The new value
19668          */
19669         select : true
19670     });
19671 };
19672
19673 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
19674     
19675     onRender: function(ct, position)
19676     {
19677         
19678         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19679         
19680         this.language = this.language || 'en';
19681         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19682         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19683         
19684         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19685         this.isInline = false;
19686         this.isInput = true;
19687         this.component = this.el.select('.add-on', true).first() || false;
19688         this.component = (this.component && this.component.length === 0) ? false : this.component;
19689         this.hasInput = this.component && this.inputEL().length;
19690         
19691         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19692         
19693         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19694         
19695         this.picker().on('mousedown', this.onMousedown, this);
19696         this.picker().on('click', this.onClick, this);
19697         
19698         this.picker().addClass('datepicker-dropdown');
19699         
19700         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19701             v.setStyle('width', '189px');
19702         });
19703         
19704         this.fillMonths();
19705         
19706         this.update();
19707         
19708         if(this.isInline) {
19709             this.show();
19710         }
19711         
19712     },
19713     
19714     setValue: function(v, suppressEvent)
19715     {   
19716         var o = this.getValue();
19717         
19718         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19719         
19720         this.update();
19721
19722         if(suppressEvent !== true){
19723             this.fireEvent('select', this, o, v);
19724         }
19725         
19726     },
19727     
19728     getValue: function()
19729     {
19730         return this.value;
19731     },
19732     
19733     onClick: function(e) 
19734     {
19735         e.stopPropagation();
19736         e.preventDefault();
19737         
19738         var target = e.getTarget();
19739         
19740         if(target.nodeName.toLowerCase() === 'i'){
19741             target = Roo.get(target).dom.parentNode;
19742         }
19743         
19744         var nodeName = target.nodeName;
19745         var className = target.className;
19746         var html = target.innerHTML;
19747         
19748         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19749             return;
19750         }
19751         
19752         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19753         
19754         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19755         
19756         this.hide();
19757                         
19758     },
19759     
19760     picker : function()
19761     {
19762         return this.pickerEl;
19763     },
19764     
19765     fillMonths: function()
19766     {    
19767         var i = 0;
19768         var months = this.picker().select('>.datepicker-months td', true).first();
19769         
19770         months.dom.innerHTML = '';
19771         
19772         while (i < 12) {
19773             var month = {
19774                 tag: 'span',
19775                 cls: 'month',
19776                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19777             };
19778             
19779             months.createChild(month);
19780         }
19781         
19782     },
19783     
19784     update: function()
19785     {
19786         var _this = this;
19787         
19788         if(typeof(this.vIndex) == 'undefined' && this.value.length){
19789             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19790         }
19791         
19792         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19793             e.removeClass('active');
19794             
19795             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19796                 e.addClass('active');
19797             }
19798         })
19799     },
19800     
19801     place: function()
19802     {
19803         if(this.isInline) {
19804             return;
19805         }
19806         
19807         this.picker().removeClass(['bottom', 'top']);
19808         
19809         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19810             /*
19811              * place to the top of element!
19812              *
19813              */
19814             
19815             this.picker().addClass('top');
19816             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19817             
19818             return;
19819         }
19820         
19821         this.picker().addClass('bottom');
19822         
19823         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19824     },
19825     
19826     onFocus : function()
19827     {
19828         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19829         this.show();
19830     },
19831     
19832     onBlur : function()
19833     {
19834         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19835         
19836         var d = this.inputEl().getValue();
19837         
19838         this.setValue(d);
19839                 
19840         this.hide();
19841     },
19842     
19843     show : function()
19844     {
19845         this.picker().show();
19846         this.picker().select('>.datepicker-months', true).first().show();
19847         this.update();
19848         this.place();
19849         
19850         this.fireEvent('show', this, this.date);
19851     },
19852     
19853     hide : function()
19854     {
19855         if(this.isInline) {
19856             return;
19857         }
19858         this.picker().hide();
19859         this.fireEvent('hide', this, this.date);
19860         
19861     },
19862     
19863     onMousedown: function(e)
19864     {
19865         e.stopPropagation();
19866         e.preventDefault();
19867     },
19868     
19869     keyup: function(e)
19870     {
19871         Roo.bootstrap.MonthField.superclass.keyup.call(this);
19872         this.update();
19873     },
19874
19875     fireKey: function(e)
19876     {
19877         if (!this.picker().isVisible()){
19878             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
19879                 this.show();
19880             }
19881             return;
19882         }
19883         
19884         var dir;
19885         
19886         switch(e.keyCode){
19887             case 27: // escape
19888                 this.hide();
19889                 e.preventDefault();
19890                 break;
19891             case 37: // left
19892             case 39: // right
19893                 dir = e.keyCode == 37 ? -1 : 1;
19894                 
19895                 this.vIndex = this.vIndex + dir;
19896                 
19897                 if(this.vIndex < 0){
19898                     this.vIndex = 0;
19899                 }
19900                 
19901                 if(this.vIndex > 11){
19902                     this.vIndex = 11;
19903                 }
19904                 
19905                 if(isNaN(this.vIndex)){
19906                     this.vIndex = 0;
19907                 }
19908                 
19909                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19910                 
19911                 break;
19912             case 38: // up
19913             case 40: // down
19914                 
19915                 dir = e.keyCode == 38 ? -1 : 1;
19916                 
19917                 this.vIndex = this.vIndex + dir * 4;
19918                 
19919                 if(this.vIndex < 0){
19920                     this.vIndex = 0;
19921                 }
19922                 
19923                 if(this.vIndex > 11){
19924                     this.vIndex = 11;
19925                 }
19926                 
19927                 if(isNaN(this.vIndex)){
19928                     this.vIndex = 0;
19929                 }
19930                 
19931                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19932                 break;
19933                 
19934             case 13: // enter
19935                 
19936                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19937                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19938                 }
19939                 
19940                 this.hide();
19941                 e.preventDefault();
19942                 break;
19943             case 9: // tab
19944                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
19945                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19946                 }
19947                 this.hide();
19948                 break;
19949             case 16: // shift
19950             case 17: // ctrl
19951             case 18: // alt
19952                 break;
19953             default :
19954                 this.hide();
19955                 
19956         }
19957     },
19958     
19959     remove: function() 
19960     {
19961         this.picker().remove();
19962     }
19963    
19964 });
19965
19966 Roo.apply(Roo.bootstrap.MonthField,  {
19967     
19968     content : {
19969         tag: 'tbody',
19970         cn: [
19971         {
19972             tag: 'tr',
19973             cn: [
19974             {
19975                 tag: 'td',
19976                 colspan: '7'
19977             }
19978             ]
19979         }
19980         ]
19981     },
19982     
19983     dates:{
19984         en: {
19985             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19986             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
19987         }
19988     }
19989 });
19990
19991 Roo.apply(Roo.bootstrap.MonthField,  {
19992   
19993     template : {
19994         tag: 'div',
19995         cls: 'datepicker dropdown-menu roo-dynamic',
19996         cn: [
19997             {
19998                 tag: 'div',
19999                 cls: 'datepicker-months',
20000                 cn: [
20001                 {
20002                     tag: 'table',
20003                     cls: 'table-condensed',
20004                     cn:[
20005                         Roo.bootstrap.DateField.content
20006                     ]
20007                 }
20008                 ]
20009             }
20010         ]
20011     }
20012 });
20013
20014  
20015
20016  
20017  /*
20018  * - LGPL
20019  *
20020  * CheckBox
20021  * 
20022  */
20023
20024 /**
20025  * @class Roo.bootstrap.CheckBox
20026  * @extends Roo.bootstrap.Input
20027  * Bootstrap CheckBox class
20028  * 
20029  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20030  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20031  * @cfg {String} boxLabel The text that appears beside the checkbox
20032  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20033  * @cfg {Boolean} checked initnal the element
20034  * @cfg {Boolean} inline inline the element (default false)
20035  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20036  * 
20037  * @constructor
20038  * Create a new CheckBox
20039  * @param {Object} config The config object
20040  */
20041
20042 Roo.bootstrap.CheckBox = function(config){
20043     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20044    
20045     this.addEvents({
20046         /**
20047         * @event check
20048         * Fires when the element is checked or unchecked.
20049         * @param {Roo.bootstrap.CheckBox} this This input
20050         * @param {Boolean} checked The new checked value
20051         */
20052        check : true
20053     });
20054     
20055 };
20056
20057 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20058   
20059     inputType: 'checkbox',
20060     inputValue: 1,
20061     valueOff: 0,
20062     boxLabel: false,
20063     checked: false,
20064     weight : false,
20065     inline: false,
20066     
20067     getAutoCreate : function()
20068     {
20069         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20070         
20071         var id = Roo.id();
20072         
20073         var cfg = {};
20074         
20075         cfg.cls = 'form-group ' + this.inputType; //input-group
20076         
20077         if(this.inline){
20078             cfg.cls += ' ' + this.inputType + '-inline';
20079         }
20080         
20081         var input =  {
20082             tag: 'input',
20083             id : id,
20084             type : this.inputType,
20085             value : this.inputValue,
20086             cls : 'roo-' + this.inputType, //'form-box',
20087             placeholder : this.placeholder || ''
20088             
20089         };
20090         
20091         if(this.inputType != 'radio'){
20092             var hidden =  {
20093                 tag: 'input',
20094                 type : 'hidden',
20095                 cls : 'roo-hidden-value',
20096                 value : this.checked ? this.valueOff : this.inputValue
20097             };
20098         }
20099         
20100             
20101         if (this.weight) { // Validity check?
20102             cfg.cls += " " + this.inputType + "-" + this.weight;
20103         }
20104         
20105         if (this.disabled) {
20106             input.disabled=true;
20107         }
20108         
20109         if(this.checked){
20110             input.checked = this.checked;
20111             
20112         }
20113         
20114         
20115         if (this.name) {
20116             
20117             input.name = this.name;
20118             
20119             if(this.inputType != 'radio'){
20120                 hidden.name = this.name;
20121                 input.name = '_hidden_' + this.name;
20122             }
20123         }
20124         
20125         if (this.size) {
20126             input.cls += ' input-' + this.size;
20127         }
20128         
20129         var settings=this;
20130         
20131         ['xs','sm','md','lg'].map(function(size){
20132             if (settings[size]) {
20133                 cfg.cls += ' col-' + size + '-' + settings[size];
20134             }
20135         });
20136         
20137         var inputblock = input;
20138          
20139         if (this.before || this.after) {
20140             
20141             inputblock = {
20142                 cls : 'input-group',
20143                 cn :  [] 
20144             };
20145             
20146             if (this.before) {
20147                 inputblock.cn.push({
20148                     tag :'span',
20149                     cls : 'input-group-addon',
20150                     html : this.before
20151                 });
20152             }
20153             
20154             inputblock.cn.push(input);
20155             
20156             if(this.inputType != 'radio'){
20157                 inputblock.cn.push(hidden);
20158             }
20159             
20160             if (this.after) {
20161                 inputblock.cn.push({
20162                     tag :'span',
20163                     cls : 'input-group-addon',
20164                     html : this.after
20165                 });
20166             }
20167             
20168         }
20169         
20170         if (align ==='left' && this.fieldLabel.length) {
20171 //                Roo.log("left and has label");
20172             cfg.cn = [
20173                 {
20174                     tag: 'label',
20175                     'for' :  id,
20176                     cls : 'control-label',
20177                     html : this.fieldLabel
20178
20179                 },
20180                 {
20181                     cls : "", 
20182                     cn: [
20183                         inputblock
20184                     ]
20185                 }
20186             ];
20187             
20188             if(this.labelWidth > 12){
20189                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20190             }
20191             
20192             if(this.labelWidth < 13 && this.labelmd == 0){
20193                 this.labelmd = this.labelWidth;
20194             }
20195             
20196             if(this.labellg > 0){
20197                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20198                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20199             }
20200             
20201             if(this.labelmd > 0){
20202                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20203                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20204             }
20205             
20206             if(this.labelsm > 0){
20207                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20208                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20209             }
20210             
20211             if(this.labelxs > 0){
20212                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20213                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20214             }
20215             
20216         } else if ( this.fieldLabel.length) {
20217 //                Roo.log(" label");
20218                 cfg.cn = [
20219                    
20220                     {
20221                         tag: this.boxLabel ? 'span' : 'label',
20222                         'for': id,
20223                         cls: 'control-label box-input-label',
20224                         //cls : 'input-group-addon',
20225                         html : this.fieldLabel
20226                         
20227                     },
20228                     
20229                     inputblock
20230                     
20231                 ];
20232
20233         } else {
20234             
20235 //                Roo.log(" no label && no align");
20236                 cfg.cn = [  inputblock ] ;
20237                 
20238                 
20239         }
20240         
20241         if(this.boxLabel){
20242              var boxLabelCfg = {
20243                 tag: 'label',
20244                 //'for': id, // box label is handled by onclick - so no for...
20245                 cls: 'box-label',
20246                 html: this.boxLabel
20247             };
20248             
20249             if(this.tooltip){
20250                 boxLabelCfg.tooltip = this.tooltip;
20251             }
20252              
20253             cfg.cn.push(boxLabelCfg);
20254         }
20255         
20256         if(this.inputType != 'radio'){
20257             cfg.cn.push(hidden);
20258         }
20259         
20260         return cfg;
20261         
20262     },
20263     
20264     /**
20265      * return the real input element.
20266      */
20267     inputEl: function ()
20268     {
20269         return this.el.select('input.roo-' + this.inputType,true).first();
20270     },
20271     hiddenEl: function ()
20272     {
20273         return this.el.select('input.roo-hidden-value',true).first();
20274     },
20275     
20276     labelEl: function()
20277     {
20278         return this.el.select('label.control-label',true).first();
20279     },
20280     /* depricated... */
20281     
20282     label: function()
20283     {
20284         return this.labelEl();
20285     },
20286     
20287     boxLabelEl: function()
20288     {
20289         return this.el.select('label.box-label',true).first();
20290     },
20291     
20292     initEvents : function()
20293     {
20294 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20295         
20296         this.inputEl().on('click', this.onClick,  this);
20297         
20298         if (this.boxLabel) { 
20299             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20300         }
20301         
20302         this.startValue = this.getValue();
20303         
20304         if(this.groupId){
20305             Roo.bootstrap.CheckBox.register(this);
20306         }
20307     },
20308     
20309     onClick : function()
20310     {   
20311         this.setChecked(!this.checked);
20312     },
20313     
20314     setChecked : function(state,suppressEvent)
20315     {
20316         this.startValue = this.getValue();
20317
20318         if(this.inputType == 'radio'){
20319             
20320             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20321                 e.dom.checked = false;
20322             });
20323             
20324             this.inputEl().dom.checked = true;
20325             
20326             this.inputEl().dom.value = this.inputValue;
20327             
20328             if(suppressEvent !== true){
20329                 this.fireEvent('check', this, true);
20330             }
20331             
20332             this.validate();
20333             
20334             return;
20335         }
20336         
20337         this.checked = state;
20338         
20339         this.inputEl().dom.checked = state;
20340         
20341         
20342         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20343         
20344         if(suppressEvent !== true){
20345             this.fireEvent('check', this, state);
20346         }
20347         
20348         this.validate();
20349     },
20350     
20351     getValue : function()
20352     {
20353         if(this.inputType == 'radio'){
20354             return this.getGroupValue();
20355         }
20356         
20357         return this.hiddenEl().dom.value;
20358         
20359     },
20360     
20361     getGroupValue : function()
20362     {
20363         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20364             return '';
20365         }
20366         
20367         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20368     },
20369     
20370     setValue : function(v,suppressEvent)
20371     {
20372         if(this.inputType == 'radio'){
20373             this.setGroupValue(v, suppressEvent);
20374             return;
20375         }
20376         
20377         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20378         
20379         this.validate();
20380     },
20381     
20382     setGroupValue : function(v, suppressEvent)
20383     {
20384         this.startValue = this.getValue();
20385         
20386         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20387             e.dom.checked = false;
20388             
20389             if(e.dom.value == v){
20390                 e.dom.checked = true;
20391             }
20392         });
20393         
20394         if(suppressEvent !== true){
20395             this.fireEvent('check', this, true);
20396         }
20397
20398         this.validate();
20399         
20400         return;
20401     },
20402     
20403     validate : function()
20404     {
20405         if(
20406                 this.disabled || 
20407                 (this.inputType == 'radio' && this.validateRadio()) ||
20408                 (this.inputType == 'checkbox' && this.validateCheckbox())
20409         ){
20410             this.markValid();
20411             return true;
20412         }
20413         
20414         this.markInvalid();
20415         return false;
20416     },
20417     
20418     validateRadio : function()
20419     {
20420         if(this.allowBlank){
20421             return true;
20422         }
20423         
20424         var valid = false;
20425         
20426         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20427             if(!e.dom.checked){
20428                 return;
20429             }
20430             
20431             valid = true;
20432             
20433             return false;
20434         });
20435         
20436         return valid;
20437     },
20438     
20439     validateCheckbox : function()
20440     {
20441         if(!this.groupId){
20442             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20443             //return (this.getValue() == this.inputValue) ? true : false;
20444         }
20445         
20446         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20447         
20448         if(!group){
20449             return false;
20450         }
20451         
20452         var r = false;
20453         
20454         for(var i in group){
20455             if(r){
20456                 break;
20457             }
20458             
20459             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20460         }
20461         
20462         return r;
20463     },
20464     
20465     /**
20466      * Mark this field as valid
20467      */
20468     markValid : function()
20469     {
20470         var _this = this;
20471         
20472         this.fireEvent('valid', this);
20473         
20474         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20475         
20476         if(this.groupId){
20477             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20478         }
20479         
20480         if(label){
20481             label.markValid();
20482         }
20483
20484         if(this.inputType == 'radio'){
20485             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20486                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20487                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20488             });
20489             
20490             return;
20491         }
20492
20493         if(!this.groupId){
20494             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20495             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20496             return;
20497         }
20498         
20499         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20500         
20501         if(!group){
20502             return;
20503         }
20504         
20505         for(var i in group){
20506             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20507             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20508         }
20509     },
20510     
20511      /**
20512      * Mark this field as invalid
20513      * @param {String} msg The validation message
20514      */
20515     markInvalid : function(msg)
20516     {
20517         if(this.allowBlank){
20518             return;
20519         }
20520         
20521         var _this = this;
20522         
20523         this.fireEvent('invalid', this, msg);
20524         
20525         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20526         
20527         if(this.groupId){
20528             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20529         }
20530         
20531         if(label){
20532             label.markInvalid();
20533         }
20534             
20535         if(this.inputType == 'radio'){
20536             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20537                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20538                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20539             });
20540             
20541             return;
20542         }
20543         
20544         if(!this.groupId){
20545             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20546             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20547             return;
20548         }
20549         
20550         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20551         
20552         if(!group){
20553             return;
20554         }
20555         
20556         for(var i in group){
20557             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20558             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20559         }
20560         
20561     },
20562     
20563     clearInvalid : function()
20564     {
20565         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20566         
20567         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20568         
20569         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20570         
20571         if (label) {
20572             label.iconEl.removeClass(label.validClass);
20573             label.iconEl.removeClass(label.invalidClass);
20574         }
20575     },
20576     
20577     disable : function()
20578     {
20579         if(this.inputType != 'radio'){
20580             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20581             return;
20582         }
20583         
20584         var _this = this;
20585         
20586         if(this.rendered){
20587             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20588                 _this.getActionEl().addClass(this.disabledClass);
20589                 e.dom.disabled = true;
20590             });
20591         }
20592         
20593         this.disabled = true;
20594         this.fireEvent("disable", this);
20595         return this;
20596     },
20597
20598     enable : function()
20599     {
20600         if(this.inputType != 'radio'){
20601             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20602             return;
20603         }
20604         
20605         var _this = this;
20606         
20607         if(this.rendered){
20608             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20609                 _this.getActionEl().removeClass(this.disabledClass);
20610                 e.dom.disabled = false;
20611             });
20612         }
20613         
20614         this.disabled = false;
20615         this.fireEvent("enable", this);
20616         return this;
20617     }
20618
20619 });
20620
20621 Roo.apply(Roo.bootstrap.CheckBox, {
20622     
20623     groups: {},
20624     
20625      /**
20626     * register a CheckBox Group
20627     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20628     */
20629     register : function(checkbox)
20630     {
20631         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20632             this.groups[checkbox.groupId] = {};
20633         }
20634         
20635         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20636             return;
20637         }
20638         
20639         this.groups[checkbox.groupId][checkbox.name] = checkbox;
20640         
20641     },
20642     /**
20643     * fetch a CheckBox Group based on the group ID
20644     * @param {string} the group ID
20645     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20646     */
20647     get: function(groupId) {
20648         if (typeof(this.groups[groupId]) == 'undefined') {
20649             return false;
20650         }
20651         
20652         return this.groups[groupId] ;
20653     }
20654     
20655     
20656 });
20657 /*
20658  * - LGPL
20659  *
20660  * RadioItem
20661  * 
20662  */
20663
20664 /**
20665  * @class Roo.bootstrap.Radio
20666  * @extends Roo.bootstrap.Component
20667  * Bootstrap Radio class
20668  * @cfg {String} boxLabel - the label associated
20669  * @cfg {String} value - the value of radio
20670  * 
20671  * @constructor
20672  * Create a new Radio
20673  * @param {Object} config The config object
20674  */
20675 Roo.bootstrap.Radio = function(config){
20676     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20677     
20678 };
20679
20680 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
20681     
20682     boxLabel : '',
20683     
20684     value : '',
20685     
20686     getAutoCreate : function()
20687     {
20688         var cfg = {
20689             tag : 'div',
20690             cls : 'form-group radio',
20691             cn : [
20692                 {
20693                     tag : 'label',
20694                     cls : 'box-label',
20695                     html : this.boxLabel
20696                 }
20697             ]
20698         };
20699         
20700         return cfg;
20701     },
20702     
20703     initEvents : function() 
20704     {
20705         this.parent().register(this);
20706         
20707         this.el.on('click', this.onClick, this);
20708         
20709     },
20710     
20711     onClick : function()
20712     {
20713         this.setChecked(true);
20714     },
20715     
20716     setChecked : function(state, suppressEvent)
20717     {
20718         this.parent().setValue(this.value, suppressEvent);
20719         
20720     }
20721     
20722 });
20723  
20724
20725  /*
20726  * - LGPL
20727  *
20728  * Input
20729  * 
20730  */
20731
20732 /**
20733  * @class Roo.bootstrap.SecurePass
20734  * @extends Roo.bootstrap.Input
20735  * Bootstrap SecurePass class
20736  *
20737  * 
20738  * @constructor
20739  * Create a new SecurePass
20740  * @param {Object} config The config object
20741  */
20742  
20743 Roo.bootstrap.SecurePass = function (config) {
20744     // these go here, so the translation tool can replace them..
20745     this.errors = {
20746         PwdEmpty: "Please type a password, and then retype it to confirm.",
20747         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20748         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20749         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20750         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20751         FNInPwd: "Your password can't contain your first name. Please type a different password.",
20752         LNInPwd: "Your password can't contain your last name. Please type a different password.",
20753         TooWeak: "Your password is Too Weak."
20754     },
20755     this.meterLabel = "Password strength:";
20756     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
20757     this.meterClass = [
20758         "roo-password-meter-tooweak", 
20759         "roo-password-meter-weak", 
20760         "roo-password-meter-medium", 
20761         "roo-password-meter-strong", 
20762         "roo-password-meter-grey"
20763     ];
20764     
20765     this.errors = {};
20766     
20767     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
20768 }
20769
20770 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
20771     /**
20772      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
20773      * {
20774      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
20775      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20776      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20777      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20778      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20779      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
20780      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
20781      * })
20782      */
20783     // private
20784     
20785     meterWidth: 300,
20786     errorMsg :'',    
20787     errors: false,
20788     imageRoot: '/',
20789     /**
20790      * @cfg {String/Object} Label for the strength meter (defaults to
20791      * 'Password strength:')
20792      */
20793     // private
20794     meterLabel: '',
20795     /**
20796      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
20797      * ['Weak', 'Medium', 'Strong'])
20798      */
20799     // private    
20800     pwdStrengths: false,    
20801     // private
20802     strength: 0,
20803     // private
20804     _lastPwd: null,
20805     // private
20806     kCapitalLetter: 0,
20807     kSmallLetter: 1,
20808     kDigit: 2,
20809     kPunctuation: 3,
20810     
20811     insecure: false,
20812     // private
20813     initEvents: function ()
20814     {
20815         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
20816
20817         if (this.el.is('input[type=password]') && Roo.isSafari) {
20818             this.el.on('keydown', this.SafariOnKeyDown, this);
20819         }
20820
20821         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
20822     },
20823     // private
20824     onRender: function (ct, position)
20825     {
20826         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
20827         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
20828         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
20829
20830         this.trigger.createChild({
20831                    cn: [
20832                     {
20833                     //id: 'PwdMeter',
20834                     tag: 'div',
20835                     cls: 'roo-password-meter-grey col-xs-12',
20836                     style: {
20837                         //width: 0,
20838                         //width: this.meterWidth + 'px'                                                
20839                         }
20840                     },
20841                     {                            
20842                          cls: 'roo-password-meter-text'                          
20843                     }
20844                 ]            
20845         });
20846
20847          
20848         if (this.hideTrigger) {
20849             this.trigger.setDisplayed(false);
20850         }
20851         this.setSize(this.width || '', this.height || '');
20852     },
20853     // private
20854     onDestroy: function ()
20855     {
20856         if (this.trigger) {
20857             this.trigger.removeAllListeners();
20858             this.trigger.remove();
20859         }
20860         if (this.wrap) {
20861             this.wrap.remove();
20862         }
20863         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
20864     },
20865     // private
20866     checkStrength: function ()
20867     {
20868         var pwd = this.inputEl().getValue();
20869         if (pwd == this._lastPwd) {
20870             return;
20871         }
20872
20873         var strength;
20874         if (this.ClientSideStrongPassword(pwd)) {
20875             strength = 3;
20876         } else if (this.ClientSideMediumPassword(pwd)) {
20877             strength = 2;
20878         } else if (this.ClientSideWeakPassword(pwd)) {
20879             strength = 1;
20880         } else {
20881             strength = 0;
20882         }
20883         
20884         Roo.log('strength1: ' + strength);
20885         
20886         //var pm = this.trigger.child('div/div/div').dom;
20887         var pm = this.trigger.child('div/div');
20888         pm.removeClass(this.meterClass);
20889         pm.addClass(this.meterClass[strength]);
20890                 
20891         
20892         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
20893                 
20894         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
20895         
20896         this._lastPwd = pwd;
20897     },
20898     reset: function ()
20899     {
20900         Roo.bootstrap.SecurePass.superclass.reset.call(this);
20901         
20902         this._lastPwd = '';
20903         
20904         var pm = this.trigger.child('div/div');
20905         pm.removeClass(this.meterClass);
20906         pm.addClass('roo-password-meter-grey');        
20907         
20908         
20909         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
20910         
20911         pt.innerHTML = '';
20912         this.inputEl().dom.type='password';
20913     },
20914     // private
20915     validateValue: function (value)
20916     {
20917         
20918         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
20919             return false;
20920         }
20921         if (value.length == 0) {
20922             if (this.allowBlank) {
20923                 this.clearInvalid();
20924                 return true;
20925             }
20926
20927             this.markInvalid(this.errors.PwdEmpty);
20928             this.errorMsg = this.errors.PwdEmpty;
20929             return false;
20930         }
20931         
20932         if(this.insecure){
20933             return true;
20934         }
20935         
20936         if ('[\x21-\x7e]*'.match(value)) {
20937             this.markInvalid(this.errors.PwdBadChar);
20938             this.errorMsg = this.errors.PwdBadChar;
20939             return false;
20940         }
20941         if (value.length < 6) {
20942             this.markInvalid(this.errors.PwdShort);
20943             this.errorMsg = this.errors.PwdShort;
20944             return false;
20945         }
20946         if (value.length > 16) {
20947             this.markInvalid(this.errors.PwdLong);
20948             this.errorMsg = this.errors.PwdLong;
20949             return false;
20950         }
20951         var strength;
20952         if (this.ClientSideStrongPassword(value)) {
20953             strength = 3;
20954         } else if (this.ClientSideMediumPassword(value)) {
20955             strength = 2;
20956         } else if (this.ClientSideWeakPassword(value)) {
20957             strength = 1;
20958         } else {
20959             strength = 0;
20960         }
20961
20962         
20963         if (strength < 2) {
20964             //this.markInvalid(this.errors.TooWeak);
20965             this.errorMsg = this.errors.TooWeak;
20966             //return false;
20967         }
20968         
20969         
20970         console.log('strength2: ' + strength);
20971         
20972         //var pm = this.trigger.child('div/div/div').dom;
20973         
20974         var pm = this.trigger.child('div/div');
20975         pm.removeClass(this.meterClass);
20976         pm.addClass(this.meterClass[strength]);
20977                 
20978         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
20979                 
20980         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
20981         
20982         this.errorMsg = ''; 
20983         return true;
20984     },
20985     // private
20986     CharacterSetChecks: function (type)
20987     {
20988         this.type = type;
20989         this.fResult = false;
20990     },
20991     // private
20992     isctype: function (character, type)
20993     {
20994         switch (type) {  
20995             case this.kCapitalLetter:
20996                 if (character >= 'A' && character <= 'Z') {
20997                     return true;
20998                 }
20999                 break;
21000             
21001             case this.kSmallLetter:
21002                 if (character >= 'a' && character <= 'z') {
21003                     return true;
21004                 }
21005                 break;
21006             
21007             case this.kDigit:
21008                 if (character >= '0' && character <= '9') {
21009                     return true;
21010                 }
21011                 break;
21012             
21013             case this.kPunctuation:
21014                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21015                     return true;
21016                 }
21017                 break;
21018             
21019             default:
21020                 return false;
21021         }
21022
21023     },
21024     // private
21025     IsLongEnough: function (pwd, size)
21026     {
21027         return !(pwd == null || isNaN(size) || pwd.length < size);
21028     },
21029     // private
21030     SpansEnoughCharacterSets: function (word, nb)
21031     {
21032         if (!this.IsLongEnough(word, nb))
21033         {
21034             return false;
21035         }
21036
21037         var characterSetChecks = new Array(
21038             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21039             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21040         );
21041         
21042         for (var index = 0; index < word.length; ++index) {
21043             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21044                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21045                     characterSetChecks[nCharSet].fResult = true;
21046                     break;
21047                 }
21048             }
21049         }
21050
21051         var nCharSets = 0;
21052         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21053             if (characterSetChecks[nCharSet].fResult) {
21054                 ++nCharSets;
21055             }
21056         }
21057
21058         if (nCharSets < nb) {
21059             return false;
21060         }
21061         return true;
21062     },
21063     // private
21064     ClientSideStrongPassword: function (pwd)
21065     {
21066         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21067     },
21068     // private
21069     ClientSideMediumPassword: function (pwd)
21070     {
21071         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21072     },
21073     // private
21074     ClientSideWeakPassword: function (pwd)
21075     {
21076         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21077     }
21078           
21079 })//<script type="text/javascript">
21080
21081 /*
21082  * Based  Ext JS Library 1.1.1
21083  * Copyright(c) 2006-2007, Ext JS, LLC.
21084  * LGPL
21085  *
21086  */
21087  
21088 /**
21089  * @class Roo.HtmlEditorCore
21090  * @extends Roo.Component
21091  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21092  *
21093  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21094  */
21095
21096 Roo.HtmlEditorCore = function(config){
21097     
21098     
21099     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21100     
21101     
21102     this.addEvents({
21103         /**
21104          * @event initialize
21105          * Fires when the editor is fully initialized (including the iframe)
21106          * @param {Roo.HtmlEditorCore} this
21107          */
21108         initialize: true,
21109         /**
21110          * @event activate
21111          * Fires when the editor is first receives the focus. Any insertion must wait
21112          * until after this event.
21113          * @param {Roo.HtmlEditorCore} this
21114          */
21115         activate: true,
21116          /**
21117          * @event beforesync
21118          * Fires before the textarea is updated with content from the editor iframe. Return false
21119          * to cancel the sync.
21120          * @param {Roo.HtmlEditorCore} this
21121          * @param {String} html
21122          */
21123         beforesync: true,
21124          /**
21125          * @event beforepush
21126          * Fires before the iframe editor is updated with content from the textarea. Return false
21127          * to cancel the push.
21128          * @param {Roo.HtmlEditorCore} this
21129          * @param {String} html
21130          */
21131         beforepush: true,
21132          /**
21133          * @event sync
21134          * Fires when the textarea is updated with content from the editor iframe.
21135          * @param {Roo.HtmlEditorCore} this
21136          * @param {String} html
21137          */
21138         sync: true,
21139          /**
21140          * @event push
21141          * Fires when the iframe editor is updated with content from the textarea.
21142          * @param {Roo.HtmlEditorCore} this
21143          * @param {String} html
21144          */
21145         push: true,
21146         
21147         /**
21148          * @event editorevent
21149          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21150          * @param {Roo.HtmlEditorCore} this
21151          */
21152         editorevent: true
21153         
21154     });
21155     
21156     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21157     
21158     // defaults : white / black...
21159     this.applyBlacklists();
21160     
21161     
21162     
21163 };
21164
21165
21166 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21167
21168
21169      /**
21170      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21171      */
21172     
21173     owner : false,
21174     
21175      /**
21176      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21177      *                        Roo.resizable.
21178      */
21179     resizable : false,
21180      /**
21181      * @cfg {Number} height (in pixels)
21182      */   
21183     height: 300,
21184    /**
21185      * @cfg {Number} width (in pixels)
21186      */   
21187     width: 500,
21188     
21189     /**
21190      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21191      * 
21192      */
21193     stylesheets: false,
21194     
21195     // id of frame..
21196     frameId: false,
21197     
21198     // private properties
21199     validationEvent : false,
21200     deferHeight: true,
21201     initialized : false,
21202     activated : false,
21203     sourceEditMode : false,
21204     onFocus : Roo.emptyFn,
21205     iframePad:3,
21206     hideMode:'offsets',
21207     
21208     clearUp: true,
21209     
21210     // blacklist + whitelisted elements..
21211     black: false,
21212     white: false,
21213      
21214     
21215
21216     /**
21217      * Protected method that will not generally be called directly. It
21218      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21219      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21220      */
21221     getDocMarkup : function(){
21222         // body styles..
21223         var st = '';
21224         
21225         // inherit styels from page...?? 
21226         if (this.stylesheets === false) {
21227             
21228             Roo.get(document.head).select('style').each(function(node) {
21229                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21230             });
21231             
21232             Roo.get(document.head).select('link').each(function(node) { 
21233                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21234             });
21235             
21236         } else if (!this.stylesheets.length) {
21237                 // simple..
21238                 st = '<style type="text/css">' +
21239                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21240                    '</style>';
21241         } else { 
21242             
21243         }
21244         
21245         st +=  '<style type="text/css">' +
21246             'IMG { cursor: pointer } ' +
21247         '</style>';
21248
21249         
21250         return '<html><head>' + st  +
21251             //<style type="text/css">' +
21252             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21253             //'</style>' +
21254             ' </head><body class="roo-htmleditor-body"></body></html>';
21255     },
21256
21257     // private
21258     onRender : function(ct, position)
21259     {
21260         var _t = this;
21261         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21262         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21263         
21264         
21265         this.el.dom.style.border = '0 none';
21266         this.el.dom.setAttribute('tabIndex', -1);
21267         this.el.addClass('x-hidden hide');
21268         
21269         
21270         
21271         if(Roo.isIE){ // fix IE 1px bogus margin
21272             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21273         }
21274        
21275         
21276         this.frameId = Roo.id();
21277         
21278          
21279         
21280         var iframe = this.owner.wrap.createChild({
21281             tag: 'iframe',
21282             cls: 'form-control', // bootstrap..
21283             id: this.frameId,
21284             name: this.frameId,
21285             frameBorder : 'no',
21286             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21287         }, this.el
21288         );
21289         
21290         
21291         this.iframe = iframe.dom;
21292
21293          this.assignDocWin();
21294         
21295         this.doc.designMode = 'on';
21296        
21297         this.doc.open();
21298         this.doc.write(this.getDocMarkup());
21299         this.doc.close();
21300
21301         
21302         var task = { // must defer to wait for browser to be ready
21303             run : function(){
21304                 //console.log("run task?" + this.doc.readyState);
21305                 this.assignDocWin();
21306                 if(this.doc.body || this.doc.readyState == 'complete'){
21307                     try {
21308                         this.doc.designMode="on";
21309                     } catch (e) {
21310                         return;
21311                     }
21312                     Roo.TaskMgr.stop(task);
21313                     this.initEditor.defer(10, this);
21314                 }
21315             },
21316             interval : 10,
21317             duration: 10000,
21318             scope: this
21319         };
21320         Roo.TaskMgr.start(task);
21321
21322     },
21323
21324     // private
21325     onResize : function(w, h)
21326     {
21327          Roo.log('resize: ' +w + ',' + h );
21328         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21329         if(!this.iframe){
21330             return;
21331         }
21332         if(typeof w == 'number'){
21333             
21334             this.iframe.style.width = w + 'px';
21335         }
21336         if(typeof h == 'number'){
21337             
21338             this.iframe.style.height = h + 'px';
21339             if(this.doc){
21340                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21341             }
21342         }
21343         
21344     },
21345
21346     /**
21347      * Toggles the editor between standard and source edit mode.
21348      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21349      */
21350     toggleSourceEdit : function(sourceEditMode){
21351         
21352         this.sourceEditMode = sourceEditMode === true;
21353         
21354         if(this.sourceEditMode){
21355  
21356             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21357             
21358         }else{
21359             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21360             //this.iframe.className = '';
21361             this.deferFocus();
21362         }
21363         //this.setSize(this.owner.wrap.getSize());
21364         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21365     },
21366
21367     
21368   
21369
21370     /**
21371      * Protected method that will not generally be called directly. If you need/want
21372      * custom HTML cleanup, this is the method you should override.
21373      * @param {String} html The HTML to be cleaned
21374      * return {String} The cleaned HTML
21375      */
21376     cleanHtml : function(html){
21377         html = String(html);
21378         if(html.length > 5){
21379             if(Roo.isSafari){ // strip safari nonsense
21380                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21381             }
21382         }
21383         if(html == '&nbsp;'){
21384             html = '';
21385         }
21386         return html;
21387     },
21388
21389     /**
21390      * HTML Editor -> Textarea
21391      * Protected method that will not generally be called directly. Syncs the contents
21392      * of the editor iframe with the textarea.
21393      */
21394     syncValue : function(){
21395         if(this.initialized){
21396             var bd = (this.doc.body || this.doc.documentElement);
21397             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21398             var html = bd.innerHTML;
21399             if(Roo.isSafari){
21400                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21401                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21402                 if(m && m[1]){
21403                     html = '<div style="'+m[0]+'">' + html + '</div>';
21404                 }
21405             }
21406             html = this.cleanHtml(html);
21407             // fix up the special chars.. normaly like back quotes in word...
21408             // however we do not want to do this with chinese..
21409             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21410                 var cc = b.charCodeAt();
21411                 if (
21412                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21413                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21414                     (cc >= 0xf900 && cc < 0xfb00 )
21415                 ) {
21416                         return b;
21417                 }
21418                 return "&#"+cc+";" 
21419             });
21420             if(this.owner.fireEvent('beforesync', this, html) !== false){
21421                 this.el.dom.value = html;
21422                 this.owner.fireEvent('sync', this, html);
21423             }
21424         }
21425     },
21426
21427     /**
21428      * Protected method that will not generally be called directly. Pushes the value of the textarea
21429      * into the iframe editor.
21430      */
21431     pushValue : function(){
21432         if(this.initialized){
21433             var v = this.el.dom.value.trim();
21434             
21435 //            if(v.length < 1){
21436 //                v = '&#160;';
21437 //            }
21438             
21439             if(this.owner.fireEvent('beforepush', this, v) !== false){
21440                 var d = (this.doc.body || this.doc.documentElement);
21441                 d.innerHTML = v;
21442                 this.cleanUpPaste();
21443                 this.el.dom.value = d.innerHTML;
21444                 this.owner.fireEvent('push', this, v);
21445             }
21446         }
21447     },
21448
21449     // private
21450     deferFocus : function(){
21451         this.focus.defer(10, this);
21452     },
21453
21454     // doc'ed in Field
21455     focus : function(){
21456         if(this.win && !this.sourceEditMode){
21457             this.win.focus();
21458         }else{
21459             this.el.focus();
21460         }
21461     },
21462     
21463     assignDocWin: function()
21464     {
21465         var iframe = this.iframe;
21466         
21467          if(Roo.isIE){
21468             this.doc = iframe.contentWindow.document;
21469             this.win = iframe.contentWindow;
21470         } else {
21471 //            if (!Roo.get(this.frameId)) {
21472 //                return;
21473 //            }
21474 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21475 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21476             
21477             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21478                 return;
21479             }
21480             
21481             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21482             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21483         }
21484     },
21485     
21486     // private
21487     initEditor : function(){
21488         //console.log("INIT EDITOR");
21489         this.assignDocWin();
21490         
21491         
21492         
21493         this.doc.designMode="on";
21494         this.doc.open();
21495         this.doc.write(this.getDocMarkup());
21496         this.doc.close();
21497         
21498         var dbody = (this.doc.body || this.doc.documentElement);
21499         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21500         // this copies styles from the containing element into thsi one..
21501         // not sure why we need all of this..
21502         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21503         
21504         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21505         //ss['background-attachment'] = 'fixed'; // w3c
21506         dbody.bgProperties = 'fixed'; // ie
21507         //Roo.DomHelper.applyStyles(dbody, ss);
21508         Roo.EventManager.on(this.doc, {
21509             //'mousedown': this.onEditorEvent,
21510             'mouseup': this.onEditorEvent,
21511             'dblclick': this.onEditorEvent,
21512             'click': this.onEditorEvent,
21513             'keyup': this.onEditorEvent,
21514             buffer:100,
21515             scope: this
21516         });
21517         if(Roo.isGecko){
21518             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21519         }
21520         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21521             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21522         }
21523         this.initialized = true;
21524
21525         this.owner.fireEvent('initialize', this);
21526         this.pushValue();
21527     },
21528
21529     // private
21530     onDestroy : function(){
21531         
21532         
21533         
21534         if(this.rendered){
21535             
21536             //for (var i =0; i < this.toolbars.length;i++) {
21537             //    // fixme - ask toolbars for heights?
21538             //    this.toolbars[i].onDestroy();
21539            // }
21540             
21541             //this.wrap.dom.innerHTML = '';
21542             //this.wrap.remove();
21543         }
21544     },
21545
21546     // private
21547     onFirstFocus : function(){
21548         
21549         this.assignDocWin();
21550         
21551         
21552         this.activated = true;
21553          
21554     
21555         if(Roo.isGecko){ // prevent silly gecko errors
21556             this.win.focus();
21557             var s = this.win.getSelection();
21558             if(!s.focusNode || s.focusNode.nodeType != 3){
21559                 var r = s.getRangeAt(0);
21560                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21561                 r.collapse(true);
21562                 this.deferFocus();
21563             }
21564             try{
21565                 this.execCmd('useCSS', true);
21566                 this.execCmd('styleWithCSS', false);
21567             }catch(e){}
21568         }
21569         this.owner.fireEvent('activate', this);
21570     },
21571
21572     // private
21573     adjustFont: function(btn){
21574         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21575         //if(Roo.isSafari){ // safari
21576         //    adjust *= 2;
21577        // }
21578         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21579         if(Roo.isSafari){ // safari
21580             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21581             v =  (v < 10) ? 10 : v;
21582             v =  (v > 48) ? 48 : v;
21583             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21584             
21585         }
21586         
21587         
21588         v = Math.max(1, v+adjust);
21589         
21590         this.execCmd('FontSize', v  );
21591     },
21592
21593     onEditorEvent : function(e)
21594     {
21595         this.owner.fireEvent('editorevent', this, e);
21596       //  this.updateToolbar();
21597         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21598     },
21599
21600     insertTag : function(tg)
21601     {
21602         // could be a bit smarter... -> wrap the current selected tRoo..
21603         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21604             
21605             range = this.createRange(this.getSelection());
21606             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21607             wrappingNode.appendChild(range.extractContents());
21608             range.insertNode(wrappingNode);
21609
21610             return;
21611             
21612             
21613             
21614         }
21615         this.execCmd("formatblock",   tg);
21616         
21617     },
21618     
21619     insertText : function(txt)
21620     {
21621         
21622         
21623         var range = this.createRange();
21624         range.deleteContents();
21625                //alert(Sender.getAttribute('label'));
21626                
21627         range.insertNode(this.doc.createTextNode(txt));
21628     } ,
21629     
21630      
21631
21632     /**
21633      * Executes a Midas editor command on the editor document and performs necessary focus and
21634      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21635      * @param {String} cmd The Midas command
21636      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21637      */
21638     relayCmd : function(cmd, value){
21639         this.win.focus();
21640         this.execCmd(cmd, value);
21641         this.owner.fireEvent('editorevent', this);
21642         //this.updateToolbar();
21643         this.owner.deferFocus();
21644     },
21645
21646     /**
21647      * Executes a Midas editor command directly on the editor document.
21648      * For visual commands, you should use {@link #relayCmd} instead.
21649      * <b>This should only be called after the editor is initialized.</b>
21650      * @param {String} cmd The Midas command
21651      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21652      */
21653     execCmd : function(cmd, value){
21654         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21655         this.syncValue();
21656     },
21657  
21658  
21659    
21660     /**
21661      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21662      * to insert tRoo.
21663      * @param {String} text | dom node.. 
21664      */
21665     insertAtCursor : function(text)
21666     {
21667         
21668         if(!this.activated){
21669             return;
21670         }
21671         /*
21672         if(Roo.isIE){
21673             this.win.focus();
21674             var r = this.doc.selection.createRange();
21675             if(r){
21676                 r.collapse(true);
21677                 r.pasteHTML(text);
21678                 this.syncValue();
21679                 this.deferFocus();
21680             
21681             }
21682             return;
21683         }
21684         */
21685         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21686             this.win.focus();
21687             
21688             
21689             // from jquery ui (MIT licenced)
21690             var range, node;
21691             var win = this.win;
21692             
21693             if (win.getSelection && win.getSelection().getRangeAt) {
21694                 range = win.getSelection().getRangeAt(0);
21695                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21696                 range.insertNode(node);
21697             } else if (win.document.selection && win.document.selection.createRange) {
21698                 // no firefox support
21699                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21700                 win.document.selection.createRange().pasteHTML(txt);
21701             } else {
21702                 // no firefox support
21703                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21704                 this.execCmd('InsertHTML', txt);
21705             } 
21706             
21707             this.syncValue();
21708             
21709             this.deferFocus();
21710         }
21711     },
21712  // private
21713     mozKeyPress : function(e){
21714         if(e.ctrlKey){
21715             var c = e.getCharCode(), cmd;
21716           
21717             if(c > 0){
21718                 c = String.fromCharCode(c).toLowerCase();
21719                 switch(c){
21720                     case 'b':
21721                         cmd = 'bold';
21722                         break;
21723                     case 'i':
21724                         cmd = 'italic';
21725                         break;
21726                     
21727                     case 'u':
21728                         cmd = 'underline';
21729                         break;
21730                     
21731                     case 'v':
21732                         this.cleanUpPaste.defer(100, this);
21733                         return;
21734                         
21735                 }
21736                 if(cmd){
21737                     this.win.focus();
21738                     this.execCmd(cmd);
21739                     this.deferFocus();
21740                     e.preventDefault();
21741                 }
21742                 
21743             }
21744         }
21745     },
21746
21747     // private
21748     fixKeys : function(){ // load time branching for fastest keydown performance
21749         if(Roo.isIE){
21750             return function(e){
21751                 var k = e.getKey(), r;
21752                 if(k == e.TAB){
21753                     e.stopEvent();
21754                     r = this.doc.selection.createRange();
21755                     if(r){
21756                         r.collapse(true);
21757                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21758                         this.deferFocus();
21759                     }
21760                     return;
21761                 }
21762                 
21763                 if(k == e.ENTER){
21764                     r = this.doc.selection.createRange();
21765                     if(r){
21766                         var target = r.parentElement();
21767                         if(!target || target.tagName.toLowerCase() != 'li'){
21768                             e.stopEvent();
21769                             r.pasteHTML('<br />');
21770                             r.collapse(false);
21771                             r.select();
21772                         }
21773                     }
21774                 }
21775                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21776                     this.cleanUpPaste.defer(100, this);
21777                     return;
21778                 }
21779                 
21780                 
21781             };
21782         }else if(Roo.isOpera){
21783             return function(e){
21784                 var k = e.getKey();
21785                 if(k == e.TAB){
21786                     e.stopEvent();
21787                     this.win.focus();
21788                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21789                     this.deferFocus();
21790                 }
21791                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21792                     this.cleanUpPaste.defer(100, this);
21793                     return;
21794                 }
21795                 
21796             };
21797         }else if(Roo.isSafari){
21798             return function(e){
21799                 var k = e.getKey();
21800                 
21801                 if(k == e.TAB){
21802                     e.stopEvent();
21803                     this.execCmd('InsertText','\t');
21804                     this.deferFocus();
21805                     return;
21806                 }
21807                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21808                     this.cleanUpPaste.defer(100, this);
21809                     return;
21810                 }
21811                 
21812              };
21813         }
21814     }(),
21815     
21816     getAllAncestors: function()
21817     {
21818         var p = this.getSelectedNode();
21819         var a = [];
21820         if (!p) {
21821             a.push(p); // push blank onto stack..
21822             p = this.getParentElement();
21823         }
21824         
21825         
21826         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
21827             a.push(p);
21828             p = p.parentNode;
21829         }
21830         a.push(this.doc.body);
21831         return a;
21832     },
21833     lastSel : false,
21834     lastSelNode : false,
21835     
21836     
21837     getSelection : function() 
21838     {
21839         this.assignDocWin();
21840         return Roo.isIE ? this.doc.selection : this.win.getSelection();
21841     },
21842     
21843     getSelectedNode: function() 
21844     {
21845         // this may only work on Gecko!!!
21846         
21847         // should we cache this!!!!
21848         
21849         
21850         
21851          
21852         var range = this.createRange(this.getSelection()).cloneRange();
21853         
21854         if (Roo.isIE) {
21855             var parent = range.parentElement();
21856             while (true) {
21857                 var testRange = range.duplicate();
21858                 testRange.moveToElementText(parent);
21859                 if (testRange.inRange(range)) {
21860                     break;
21861                 }
21862                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
21863                     break;
21864                 }
21865                 parent = parent.parentElement;
21866             }
21867             return parent;
21868         }
21869         
21870         // is ancestor a text element.
21871         var ac =  range.commonAncestorContainer;
21872         if (ac.nodeType == 3) {
21873             ac = ac.parentNode;
21874         }
21875         
21876         var ar = ac.childNodes;
21877          
21878         var nodes = [];
21879         var other_nodes = [];
21880         var has_other_nodes = false;
21881         for (var i=0;i<ar.length;i++) {
21882             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
21883                 continue;
21884             }
21885             // fullly contained node.
21886             
21887             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
21888                 nodes.push(ar[i]);
21889                 continue;
21890             }
21891             
21892             // probably selected..
21893             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
21894                 other_nodes.push(ar[i]);
21895                 continue;
21896             }
21897             // outer..
21898             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
21899                 continue;
21900             }
21901             
21902             
21903             has_other_nodes = true;
21904         }
21905         if (!nodes.length && other_nodes.length) {
21906             nodes= other_nodes;
21907         }
21908         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
21909             return false;
21910         }
21911         
21912         return nodes[0];
21913     },
21914     createRange: function(sel)
21915     {
21916         // this has strange effects when using with 
21917         // top toolbar - not sure if it's a great idea.
21918         //this.editor.contentWindow.focus();
21919         if (typeof sel != "undefined") {
21920             try {
21921                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
21922             } catch(e) {
21923                 return this.doc.createRange();
21924             }
21925         } else {
21926             return this.doc.createRange();
21927         }
21928     },
21929     getParentElement: function()
21930     {
21931         
21932         this.assignDocWin();
21933         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
21934         
21935         var range = this.createRange(sel);
21936          
21937         try {
21938             var p = range.commonAncestorContainer;
21939             while (p.nodeType == 3) { // text node
21940                 p = p.parentNode;
21941             }
21942             return p;
21943         } catch (e) {
21944             return null;
21945         }
21946     
21947     },
21948     /***
21949      *
21950      * Range intersection.. the hard stuff...
21951      *  '-1' = before
21952      *  '0' = hits..
21953      *  '1' = after.
21954      *         [ -- selected range --- ]
21955      *   [fail]                        [fail]
21956      *
21957      *    basically..
21958      *      if end is before start or  hits it. fail.
21959      *      if start is after end or hits it fail.
21960      *
21961      *   if either hits (but other is outside. - then it's not 
21962      *   
21963      *    
21964      **/
21965     
21966     
21967     // @see http://www.thismuchiknow.co.uk/?p=64.
21968     rangeIntersectsNode : function(range, node)
21969     {
21970         var nodeRange = node.ownerDocument.createRange();
21971         try {
21972             nodeRange.selectNode(node);
21973         } catch (e) {
21974             nodeRange.selectNodeContents(node);
21975         }
21976     
21977         var rangeStartRange = range.cloneRange();
21978         rangeStartRange.collapse(true);
21979     
21980         var rangeEndRange = range.cloneRange();
21981         rangeEndRange.collapse(false);
21982     
21983         var nodeStartRange = nodeRange.cloneRange();
21984         nodeStartRange.collapse(true);
21985     
21986         var nodeEndRange = nodeRange.cloneRange();
21987         nodeEndRange.collapse(false);
21988     
21989         return rangeStartRange.compareBoundaryPoints(
21990                  Range.START_TO_START, nodeEndRange) == -1 &&
21991                rangeEndRange.compareBoundaryPoints(
21992                  Range.START_TO_START, nodeStartRange) == 1;
21993         
21994          
21995     },
21996     rangeCompareNode : function(range, node)
21997     {
21998         var nodeRange = node.ownerDocument.createRange();
21999         try {
22000             nodeRange.selectNode(node);
22001         } catch (e) {
22002             nodeRange.selectNodeContents(node);
22003         }
22004         
22005         
22006         range.collapse(true);
22007     
22008         nodeRange.collapse(true);
22009      
22010         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22011         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22012          
22013         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22014         
22015         var nodeIsBefore   =  ss == 1;
22016         var nodeIsAfter    = ee == -1;
22017         
22018         if (nodeIsBefore && nodeIsAfter) {
22019             return 0; // outer
22020         }
22021         if (!nodeIsBefore && nodeIsAfter) {
22022             return 1; //right trailed.
22023         }
22024         
22025         if (nodeIsBefore && !nodeIsAfter) {
22026             return 2;  // left trailed.
22027         }
22028         // fully contined.
22029         return 3;
22030     },
22031
22032     // private? - in a new class?
22033     cleanUpPaste :  function()
22034     {
22035         // cleans up the whole document..
22036         Roo.log('cleanuppaste');
22037         
22038         this.cleanUpChildren(this.doc.body);
22039         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22040         if (clean != this.doc.body.innerHTML) {
22041             this.doc.body.innerHTML = clean;
22042         }
22043         
22044     },
22045     
22046     cleanWordChars : function(input) {// change the chars to hex code
22047         var he = Roo.HtmlEditorCore;
22048         
22049         var output = input;
22050         Roo.each(he.swapCodes, function(sw) { 
22051             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22052             
22053             output = output.replace(swapper, sw[1]);
22054         });
22055         
22056         return output;
22057     },
22058     
22059     
22060     cleanUpChildren : function (n)
22061     {
22062         if (!n.childNodes.length) {
22063             return;
22064         }
22065         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22066            this.cleanUpChild(n.childNodes[i]);
22067         }
22068     },
22069     
22070     
22071         
22072     
22073     cleanUpChild : function (node)
22074     {
22075         var ed = this;
22076         //console.log(node);
22077         if (node.nodeName == "#text") {
22078             // clean up silly Windows -- stuff?
22079             return; 
22080         }
22081         if (node.nodeName == "#comment") {
22082             node.parentNode.removeChild(node);
22083             // clean up silly Windows -- stuff?
22084             return; 
22085         }
22086         var lcname = node.tagName.toLowerCase();
22087         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22088         // whitelist of tags..
22089         
22090         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22091             // remove node.
22092             node.parentNode.removeChild(node);
22093             return;
22094             
22095         }
22096         
22097         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22098         
22099         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22100         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22101         
22102         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22103         //    remove_keep_children = true;
22104         //}
22105         
22106         if (remove_keep_children) {
22107             this.cleanUpChildren(node);
22108             // inserts everything just before this node...
22109             while (node.childNodes.length) {
22110                 var cn = node.childNodes[0];
22111                 node.removeChild(cn);
22112                 node.parentNode.insertBefore(cn, node);
22113             }
22114             node.parentNode.removeChild(node);
22115             return;
22116         }
22117         
22118         if (!node.attributes || !node.attributes.length) {
22119             this.cleanUpChildren(node);
22120             return;
22121         }
22122         
22123         function cleanAttr(n,v)
22124         {
22125             
22126             if (v.match(/^\./) || v.match(/^\//)) {
22127                 return;
22128             }
22129             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
22130                 return;
22131             }
22132             if (v.match(/^#/)) {
22133                 return;
22134             }
22135 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22136             node.removeAttribute(n);
22137             
22138         }
22139         
22140         var cwhite = this.cwhite;
22141         var cblack = this.cblack;
22142             
22143         function cleanStyle(n,v)
22144         {
22145             if (v.match(/expression/)) { //XSS?? should we even bother..
22146                 node.removeAttribute(n);
22147                 return;
22148             }
22149             
22150             var parts = v.split(/;/);
22151             var clean = [];
22152             
22153             Roo.each(parts, function(p) {
22154                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22155                 if (!p.length) {
22156                     return true;
22157                 }
22158                 var l = p.split(':').shift().replace(/\s+/g,'');
22159                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22160                 
22161                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22162 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22163                     //node.removeAttribute(n);
22164                     return true;
22165                 }
22166                 //Roo.log()
22167                 // only allow 'c whitelisted system attributes'
22168                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22169 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22170                     //node.removeAttribute(n);
22171                     return true;
22172                 }
22173                 
22174                 
22175                  
22176                 
22177                 clean.push(p);
22178                 return true;
22179             });
22180             if (clean.length) { 
22181                 node.setAttribute(n, clean.join(';'));
22182             } else {
22183                 node.removeAttribute(n);
22184             }
22185             
22186         }
22187         
22188         
22189         for (var i = node.attributes.length-1; i > -1 ; i--) {
22190             var a = node.attributes[i];
22191             //console.log(a);
22192             
22193             if (a.name.toLowerCase().substr(0,2)=='on')  {
22194                 node.removeAttribute(a.name);
22195                 continue;
22196             }
22197             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22198                 node.removeAttribute(a.name);
22199                 continue;
22200             }
22201             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22202                 cleanAttr(a.name,a.value); // fixme..
22203                 continue;
22204             }
22205             if (a.name == 'style') {
22206                 cleanStyle(a.name,a.value);
22207                 continue;
22208             }
22209             /// clean up MS crap..
22210             // tecnically this should be a list of valid class'es..
22211             
22212             
22213             if (a.name == 'class') {
22214                 if (a.value.match(/^Mso/)) {
22215                     node.className = '';
22216                 }
22217                 
22218                 if (a.value.match(/^body$/)) {
22219                     node.className = '';
22220                 }
22221                 continue;
22222             }
22223             
22224             // style cleanup!?
22225             // class cleanup?
22226             
22227         }
22228         
22229         
22230         this.cleanUpChildren(node);
22231         
22232         
22233     },
22234     
22235     /**
22236      * Clean up MS wordisms...
22237      */
22238     cleanWord : function(node)
22239     {
22240         
22241         
22242         if (!node) {
22243             this.cleanWord(this.doc.body);
22244             return;
22245         }
22246         if (node.nodeName == "#text") {
22247             // clean up silly Windows -- stuff?
22248             return; 
22249         }
22250         if (node.nodeName == "#comment") {
22251             node.parentNode.removeChild(node);
22252             // clean up silly Windows -- stuff?
22253             return; 
22254         }
22255         
22256         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22257             node.parentNode.removeChild(node);
22258             return;
22259         }
22260         
22261         // remove - but keep children..
22262         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22263             while (node.childNodes.length) {
22264                 var cn = node.childNodes[0];
22265                 node.removeChild(cn);
22266                 node.parentNode.insertBefore(cn, node);
22267             }
22268             node.parentNode.removeChild(node);
22269             this.iterateChildren(node, this.cleanWord);
22270             return;
22271         }
22272         // clean styles
22273         if (node.className.length) {
22274             
22275             var cn = node.className.split(/\W+/);
22276             var cna = [];
22277             Roo.each(cn, function(cls) {
22278                 if (cls.match(/Mso[a-zA-Z]+/)) {
22279                     return;
22280                 }
22281                 cna.push(cls);
22282             });
22283             node.className = cna.length ? cna.join(' ') : '';
22284             if (!cna.length) {
22285                 node.removeAttribute("class");
22286             }
22287         }
22288         
22289         if (node.hasAttribute("lang")) {
22290             node.removeAttribute("lang");
22291         }
22292         
22293         if (node.hasAttribute("style")) {
22294             
22295             var styles = node.getAttribute("style").split(";");
22296             var nstyle = [];
22297             Roo.each(styles, function(s) {
22298                 if (!s.match(/:/)) {
22299                     return;
22300                 }
22301                 var kv = s.split(":");
22302                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22303                     return;
22304                 }
22305                 // what ever is left... we allow.
22306                 nstyle.push(s);
22307             });
22308             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22309             if (!nstyle.length) {
22310                 node.removeAttribute('style');
22311             }
22312         }
22313         this.iterateChildren(node, this.cleanWord);
22314         
22315         
22316         
22317     },
22318     /**
22319      * iterateChildren of a Node, calling fn each time, using this as the scole..
22320      * @param {DomNode} node node to iterate children of.
22321      * @param {Function} fn method of this class to call on each item.
22322      */
22323     iterateChildren : function(node, fn)
22324     {
22325         if (!node.childNodes.length) {
22326                 return;
22327         }
22328         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22329            fn.call(this, node.childNodes[i])
22330         }
22331     },
22332     
22333     
22334     /**
22335      * cleanTableWidths.
22336      *
22337      * Quite often pasting from word etc.. results in tables with column and widths.
22338      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22339      *
22340      */
22341     cleanTableWidths : function(node)
22342     {
22343          
22344          
22345         if (!node) {
22346             this.cleanTableWidths(this.doc.body);
22347             return;
22348         }
22349         
22350         // ignore list...
22351         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22352             return; 
22353         }
22354         Roo.log(node.tagName);
22355         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22356             this.iterateChildren(node, this.cleanTableWidths);
22357             return;
22358         }
22359         if (node.hasAttribute('width')) {
22360             node.removeAttribute('width');
22361         }
22362         
22363          
22364         if (node.hasAttribute("style")) {
22365             // pretty basic...
22366             
22367             var styles = node.getAttribute("style").split(";");
22368             var nstyle = [];
22369             Roo.each(styles, function(s) {
22370                 if (!s.match(/:/)) {
22371                     return;
22372                 }
22373                 var kv = s.split(":");
22374                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22375                     return;
22376                 }
22377                 // what ever is left... we allow.
22378                 nstyle.push(s);
22379             });
22380             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22381             if (!nstyle.length) {
22382                 node.removeAttribute('style');
22383             }
22384         }
22385         
22386         this.iterateChildren(node, this.cleanTableWidths);
22387         
22388         
22389     },
22390     
22391     
22392     
22393     
22394     domToHTML : function(currentElement, depth, nopadtext) {
22395         
22396         depth = depth || 0;
22397         nopadtext = nopadtext || false;
22398     
22399         if (!currentElement) {
22400             return this.domToHTML(this.doc.body);
22401         }
22402         
22403         //Roo.log(currentElement);
22404         var j;
22405         var allText = false;
22406         var nodeName = currentElement.nodeName;
22407         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22408         
22409         if  (nodeName == '#text') {
22410             
22411             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22412         }
22413         
22414         
22415         var ret = '';
22416         if (nodeName != 'BODY') {
22417              
22418             var i = 0;
22419             // Prints the node tagName, such as <A>, <IMG>, etc
22420             if (tagName) {
22421                 var attr = [];
22422                 for(i = 0; i < currentElement.attributes.length;i++) {
22423                     // quoting?
22424                     var aname = currentElement.attributes.item(i).name;
22425                     if (!currentElement.attributes.item(i).value.length) {
22426                         continue;
22427                     }
22428                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22429                 }
22430                 
22431                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22432             } 
22433             else {
22434                 
22435                 // eack
22436             }
22437         } else {
22438             tagName = false;
22439         }
22440         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22441             return ret;
22442         }
22443         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22444             nopadtext = true;
22445         }
22446         
22447         
22448         // Traverse the tree
22449         i = 0;
22450         var currentElementChild = currentElement.childNodes.item(i);
22451         var allText = true;
22452         var innerHTML  = '';
22453         lastnode = '';
22454         while (currentElementChild) {
22455             // Formatting code (indent the tree so it looks nice on the screen)
22456             var nopad = nopadtext;
22457             if (lastnode == 'SPAN') {
22458                 nopad  = true;
22459             }
22460             // text
22461             if  (currentElementChild.nodeName == '#text') {
22462                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22463                 toadd = nopadtext ? toadd : toadd.trim();
22464                 if (!nopad && toadd.length > 80) {
22465                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22466                 }
22467                 innerHTML  += toadd;
22468                 
22469                 i++;
22470                 currentElementChild = currentElement.childNodes.item(i);
22471                 lastNode = '';
22472                 continue;
22473             }
22474             allText = false;
22475             
22476             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22477                 
22478             // Recursively traverse the tree structure of the child node
22479             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22480             lastnode = currentElementChild.nodeName;
22481             i++;
22482             currentElementChild=currentElement.childNodes.item(i);
22483         }
22484         
22485         ret += innerHTML;
22486         
22487         if (!allText) {
22488                 // The remaining code is mostly for formatting the tree
22489             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22490         }
22491         
22492         
22493         if (tagName) {
22494             ret+= "</"+tagName+">";
22495         }
22496         return ret;
22497         
22498     },
22499         
22500     applyBlacklists : function()
22501     {
22502         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22503         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22504         
22505         this.white = [];
22506         this.black = [];
22507         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22508             if (b.indexOf(tag) > -1) {
22509                 return;
22510             }
22511             this.white.push(tag);
22512             
22513         }, this);
22514         
22515         Roo.each(w, function(tag) {
22516             if (b.indexOf(tag) > -1) {
22517                 return;
22518             }
22519             if (this.white.indexOf(tag) > -1) {
22520                 return;
22521             }
22522             this.white.push(tag);
22523             
22524         }, this);
22525         
22526         
22527         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22528             if (w.indexOf(tag) > -1) {
22529                 return;
22530             }
22531             this.black.push(tag);
22532             
22533         }, this);
22534         
22535         Roo.each(b, function(tag) {
22536             if (w.indexOf(tag) > -1) {
22537                 return;
22538             }
22539             if (this.black.indexOf(tag) > -1) {
22540                 return;
22541             }
22542             this.black.push(tag);
22543             
22544         }, this);
22545         
22546         
22547         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22548         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22549         
22550         this.cwhite = [];
22551         this.cblack = [];
22552         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22553             if (b.indexOf(tag) > -1) {
22554                 return;
22555             }
22556             this.cwhite.push(tag);
22557             
22558         }, this);
22559         
22560         Roo.each(w, function(tag) {
22561             if (b.indexOf(tag) > -1) {
22562                 return;
22563             }
22564             if (this.cwhite.indexOf(tag) > -1) {
22565                 return;
22566             }
22567             this.cwhite.push(tag);
22568             
22569         }, this);
22570         
22571         
22572         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22573             if (w.indexOf(tag) > -1) {
22574                 return;
22575             }
22576             this.cblack.push(tag);
22577             
22578         }, this);
22579         
22580         Roo.each(b, function(tag) {
22581             if (w.indexOf(tag) > -1) {
22582                 return;
22583             }
22584             if (this.cblack.indexOf(tag) > -1) {
22585                 return;
22586             }
22587             this.cblack.push(tag);
22588             
22589         }, this);
22590     },
22591     
22592     setStylesheets : function(stylesheets)
22593     {
22594         if(typeof(stylesheets) == 'string'){
22595             Roo.get(this.iframe.contentDocument.head).createChild({
22596                 tag : 'link',
22597                 rel : 'stylesheet',
22598                 type : 'text/css',
22599                 href : stylesheets
22600             });
22601             
22602             return;
22603         }
22604         var _this = this;
22605      
22606         Roo.each(stylesheets, function(s) {
22607             if(!s.length){
22608                 return;
22609             }
22610             
22611             Roo.get(_this.iframe.contentDocument.head).createChild({
22612                 tag : 'link',
22613                 rel : 'stylesheet',
22614                 type : 'text/css',
22615                 href : s
22616             });
22617         });
22618
22619         
22620     },
22621     
22622     removeStylesheets : function()
22623     {
22624         var _this = this;
22625         
22626         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22627             s.remove();
22628         });
22629     }
22630     
22631     // hide stuff that is not compatible
22632     /**
22633      * @event blur
22634      * @hide
22635      */
22636     /**
22637      * @event change
22638      * @hide
22639      */
22640     /**
22641      * @event focus
22642      * @hide
22643      */
22644     /**
22645      * @event specialkey
22646      * @hide
22647      */
22648     /**
22649      * @cfg {String} fieldClass @hide
22650      */
22651     /**
22652      * @cfg {String} focusClass @hide
22653      */
22654     /**
22655      * @cfg {String} autoCreate @hide
22656      */
22657     /**
22658      * @cfg {String} inputType @hide
22659      */
22660     /**
22661      * @cfg {String} invalidClass @hide
22662      */
22663     /**
22664      * @cfg {String} invalidText @hide
22665      */
22666     /**
22667      * @cfg {String} msgFx @hide
22668      */
22669     /**
22670      * @cfg {String} validateOnBlur @hide
22671      */
22672 });
22673
22674 Roo.HtmlEditorCore.white = [
22675         'area', 'br', 'img', 'input', 'hr', 'wbr',
22676         
22677        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22678        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22679        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22680        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22681        'table',   'ul',         'xmp', 
22682        
22683        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22684       'thead',   'tr', 
22685      
22686       'dir', 'menu', 'ol', 'ul', 'dl',
22687        
22688       'embed',  'object'
22689 ];
22690
22691
22692 Roo.HtmlEditorCore.black = [
22693     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22694         'applet', // 
22695         'base',   'basefont', 'bgsound', 'blink',  'body', 
22696         'frame',  'frameset', 'head',    'html',   'ilayer', 
22697         'iframe', 'layer',  'link',     'meta',    'object',   
22698         'script', 'style' ,'title',  'xml' // clean later..
22699 ];
22700 Roo.HtmlEditorCore.clean = [
22701     'script', 'style', 'title', 'xml'
22702 ];
22703 Roo.HtmlEditorCore.remove = [
22704     'font'
22705 ];
22706 // attributes..
22707
22708 Roo.HtmlEditorCore.ablack = [
22709     'on'
22710 ];
22711     
22712 Roo.HtmlEditorCore.aclean = [ 
22713     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22714 ];
22715
22716 // protocols..
22717 Roo.HtmlEditorCore.pwhite= [
22718         'http',  'https',  'mailto'
22719 ];
22720
22721 // white listed style attributes.
22722 Roo.HtmlEditorCore.cwhite= [
22723       //  'text-align', /// default is to allow most things..
22724       
22725          
22726 //        'font-size'//??
22727 ];
22728
22729 // black listed style attributes.
22730 Roo.HtmlEditorCore.cblack= [
22731       //  'font-size' -- this can be set by the project 
22732 ];
22733
22734
22735 Roo.HtmlEditorCore.swapCodes   =[ 
22736     [    8211, "--" ], 
22737     [    8212, "--" ], 
22738     [    8216,  "'" ],  
22739     [    8217, "'" ],  
22740     [    8220, '"' ],  
22741     [    8221, '"' ],  
22742     [    8226, "*" ],  
22743     [    8230, "..." ]
22744 ]; 
22745
22746     /*
22747  * - LGPL
22748  *
22749  * HtmlEditor
22750  * 
22751  */
22752
22753 /**
22754  * @class Roo.bootstrap.HtmlEditor
22755  * @extends Roo.bootstrap.TextArea
22756  * Bootstrap HtmlEditor class
22757
22758  * @constructor
22759  * Create a new HtmlEditor
22760  * @param {Object} config The config object
22761  */
22762
22763 Roo.bootstrap.HtmlEditor = function(config){
22764     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
22765     if (!this.toolbars) {
22766         this.toolbars = [];
22767     }
22768     
22769     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22770     this.addEvents({
22771             /**
22772              * @event initialize
22773              * Fires when the editor is fully initialized (including the iframe)
22774              * @param {HtmlEditor} this
22775              */
22776             initialize: true,
22777             /**
22778              * @event activate
22779              * Fires when the editor is first receives the focus. Any insertion must wait
22780              * until after this event.
22781              * @param {HtmlEditor} this
22782              */
22783             activate: true,
22784              /**
22785              * @event beforesync
22786              * Fires before the textarea is updated with content from the editor iframe. Return false
22787              * to cancel the sync.
22788              * @param {HtmlEditor} this
22789              * @param {String} html
22790              */
22791             beforesync: true,
22792              /**
22793              * @event beforepush
22794              * Fires before the iframe editor is updated with content from the textarea. Return false
22795              * to cancel the push.
22796              * @param {HtmlEditor} this
22797              * @param {String} html
22798              */
22799             beforepush: true,
22800              /**
22801              * @event sync
22802              * Fires when the textarea is updated with content from the editor iframe.
22803              * @param {HtmlEditor} this
22804              * @param {String} html
22805              */
22806             sync: true,
22807              /**
22808              * @event push
22809              * Fires when the iframe editor is updated with content from the textarea.
22810              * @param {HtmlEditor} this
22811              * @param {String} html
22812              */
22813             push: true,
22814              /**
22815              * @event editmodechange
22816              * Fires when the editor switches edit modes
22817              * @param {HtmlEditor} this
22818              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
22819              */
22820             editmodechange: true,
22821             /**
22822              * @event editorevent
22823              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
22824              * @param {HtmlEditor} this
22825              */
22826             editorevent: true,
22827             /**
22828              * @event firstfocus
22829              * Fires when on first focus - needed by toolbars..
22830              * @param {HtmlEditor} this
22831              */
22832             firstfocus: true,
22833             /**
22834              * @event autosave
22835              * Auto save the htmlEditor value as a file into Events
22836              * @param {HtmlEditor} this
22837              */
22838             autosave: true,
22839             /**
22840              * @event savedpreview
22841              * preview the saved version of htmlEditor
22842              * @param {HtmlEditor} this
22843              */
22844             savedpreview: true
22845         });
22846 };
22847
22848
22849 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
22850     
22851     
22852       /**
22853      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
22854      */
22855     toolbars : false,
22856     
22857      /**
22858     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
22859     */
22860     btns : [],
22861    
22862      /**
22863      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
22864      *                        Roo.resizable.
22865      */
22866     resizable : false,
22867      /**
22868      * @cfg {Number} height (in pixels)
22869      */   
22870     height: 300,
22871    /**
22872      * @cfg {Number} width (in pixels)
22873      */   
22874     width: false,
22875     
22876     /**
22877      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
22878      * 
22879      */
22880     stylesheets: false,
22881     
22882     // id of frame..
22883     frameId: false,
22884     
22885     // private properties
22886     validationEvent : false,
22887     deferHeight: true,
22888     initialized : false,
22889     activated : false,
22890     
22891     onFocus : Roo.emptyFn,
22892     iframePad:3,
22893     hideMode:'offsets',
22894     
22895     tbContainer : false,
22896     
22897     toolbarContainer :function() {
22898         return this.wrap.select('.x-html-editor-tb',true).first();
22899     },
22900
22901     /**
22902      * Protected method that will not generally be called directly. It
22903      * is called when the editor creates its toolbar. Override this method if you need to
22904      * add custom toolbar buttons.
22905      * @param {HtmlEditor} editor
22906      */
22907     createToolbar : function(){
22908         Roo.log('renewing');
22909         Roo.log("create toolbars");
22910         
22911         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
22912         this.toolbars[0].render(this.toolbarContainer());
22913         
22914         return;
22915         
22916 //        if (!editor.toolbars || !editor.toolbars.length) {
22917 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
22918 //        }
22919 //        
22920 //        for (var i =0 ; i < editor.toolbars.length;i++) {
22921 //            editor.toolbars[i] = Roo.factory(
22922 //                    typeof(editor.toolbars[i]) == 'string' ?
22923 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
22924 //                Roo.bootstrap.HtmlEditor);
22925 //            editor.toolbars[i].init(editor);
22926 //        }
22927     },
22928
22929      
22930     // private
22931     onRender : function(ct, position)
22932     {
22933        // Roo.log("Call onRender: " + this.xtype);
22934         var _t = this;
22935         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
22936       
22937         this.wrap = this.inputEl().wrap({
22938             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
22939         });
22940         
22941         this.editorcore.onRender(ct, position);
22942          
22943         if (this.resizable) {
22944             this.resizeEl = new Roo.Resizable(this.wrap, {
22945                 pinned : true,
22946                 wrap: true,
22947                 dynamic : true,
22948                 minHeight : this.height,
22949                 height: this.height,
22950                 handles : this.resizable,
22951                 width: this.width,
22952                 listeners : {
22953                     resize : function(r, w, h) {
22954                         _t.onResize(w,h); // -something
22955                     }
22956                 }
22957             });
22958             
22959         }
22960         this.createToolbar(this);
22961        
22962         
22963         if(!this.width && this.resizable){
22964             this.setSize(this.wrap.getSize());
22965         }
22966         if (this.resizeEl) {
22967             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
22968             // should trigger onReize..
22969         }
22970         
22971     },
22972
22973     // private
22974     onResize : function(w, h)
22975     {
22976         Roo.log('resize: ' +w + ',' + h );
22977         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
22978         var ew = false;
22979         var eh = false;
22980         
22981         if(this.inputEl() ){
22982             if(typeof w == 'number'){
22983                 var aw = w - this.wrap.getFrameWidth('lr');
22984                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
22985                 ew = aw;
22986             }
22987             if(typeof h == 'number'){
22988                  var tbh = -11;  // fixme it needs to tool bar size!
22989                 for (var i =0; i < this.toolbars.length;i++) {
22990                     // fixme - ask toolbars for heights?
22991                     tbh += this.toolbars[i].el.getHeight();
22992                     //if (this.toolbars[i].footer) {
22993                     //    tbh += this.toolbars[i].footer.el.getHeight();
22994                     //}
22995                 }
22996               
22997                 
22998                 
22999                 
23000                 
23001                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23002                 ah -= 5; // knock a few pixes off for look..
23003                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23004                 var eh = ah;
23005             }
23006         }
23007         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23008         this.editorcore.onResize(ew,eh);
23009         
23010     },
23011
23012     /**
23013      * Toggles the editor between standard and source edit mode.
23014      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23015      */
23016     toggleSourceEdit : function(sourceEditMode)
23017     {
23018         this.editorcore.toggleSourceEdit(sourceEditMode);
23019         
23020         if(this.editorcore.sourceEditMode){
23021             Roo.log('editor - showing textarea');
23022             
23023 //            Roo.log('in');
23024 //            Roo.log(this.syncValue());
23025             this.syncValue();
23026             this.inputEl().removeClass(['hide', 'x-hidden']);
23027             this.inputEl().dom.removeAttribute('tabIndex');
23028             this.inputEl().focus();
23029         }else{
23030             Roo.log('editor - hiding textarea');
23031 //            Roo.log('out')
23032 //            Roo.log(this.pushValue()); 
23033             this.pushValue();
23034             
23035             this.inputEl().addClass(['hide', 'x-hidden']);
23036             this.inputEl().dom.setAttribute('tabIndex', -1);
23037             //this.deferFocus();
23038         }
23039          
23040         if(this.resizable){
23041             this.setSize(this.wrap.getSize());
23042         }
23043         
23044         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23045     },
23046  
23047     // private (for BoxComponent)
23048     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23049
23050     // private (for BoxComponent)
23051     getResizeEl : function(){
23052         return this.wrap;
23053     },
23054
23055     // private (for BoxComponent)
23056     getPositionEl : function(){
23057         return this.wrap;
23058     },
23059
23060     // private
23061     initEvents : function(){
23062         this.originalValue = this.getValue();
23063     },
23064
23065 //    /**
23066 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23067 //     * @method
23068 //     */
23069 //    markInvalid : Roo.emptyFn,
23070 //    /**
23071 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23072 //     * @method
23073 //     */
23074 //    clearInvalid : Roo.emptyFn,
23075
23076     setValue : function(v){
23077         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23078         this.editorcore.pushValue();
23079     },
23080
23081      
23082     // private
23083     deferFocus : function(){
23084         this.focus.defer(10, this);
23085     },
23086
23087     // doc'ed in Field
23088     focus : function(){
23089         this.editorcore.focus();
23090         
23091     },
23092       
23093
23094     // private
23095     onDestroy : function(){
23096         
23097         
23098         
23099         if(this.rendered){
23100             
23101             for (var i =0; i < this.toolbars.length;i++) {
23102                 // fixme - ask toolbars for heights?
23103                 this.toolbars[i].onDestroy();
23104             }
23105             
23106             this.wrap.dom.innerHTML = '';
23107             this.wrap.remove();
23108         }
23109     },
23110
23111     // private
23112     onFirstFocus : function(){
23113         //Roo.log("onFirstFocus");
23114         this.editorcore.onFirstFocus();
23115          for (var i =0; i < this.toolbars.length;i++) {
23116             this.toolbars[i].onFirstFocus();
23117         }
23118         
23119     },
23120     
23121     // private
23122     syncValue : function()
23123     {   
23124         this.editorcore.syncValue();
23125     },
23126     
23127     pushValue : function()
23128     {   
23129         this.editorcore.pushValue();
23130     }
23131      
23132     
23133     // hide stuff that is not compatible
23134     /**
23135      * @event blur
23136      * @hide
23137      */
23138     /**
23139      * @event change
23140      * @hide
23141      */
23142     /**
23143      * @event focus
23144      * @hide
23145      */
23146     /**
23147      * @event specialkey
23148      * @hide
23149      */
23150     /**
23151      * @cfg {String} fieldClass @hide
23152      */
23153     /**
23154      * @cfg {String} focusClass @hide
23155      */
23156     /**
23157      * @cfg {String} autoCreate @hide
23158      */
23159     /**
23160      * @cfg {String} inputType @hide
23161      */
23162     /**
23163      * @cfg {String} invalidClass @hide
23164      */
23165     /**
23166      * @cfg {String} invalidText @hide
23167      */
23168     /**
23169      * @cfg {String} msgFx @hide
23170      */
23171     /**
23172      * @cfg {String} validateOnBlur @hide
23173      */
23174 });
23175  
23176     
23177    
23178    
23179    
23180       
23181 Roo.namespace('Roo.bootstrap.htmleditor');
23182 /**
23183  * @class Roo.bootstrap.HtmlEditorToolbar1
23184  * Basic Toolbar
23185  * 
23186  * Usage:
23187  *
23188  new Roo.bootstrap.HtmlEditor({
23189     ....
23190     toolbars : [
23191         new Roo.bootstrap.HtmlEditorToolbar1({
23192             disable : { fonts: 1 , format: 1, ..., ... , ...],
23193             btns : [ .... ]
23194         })
23195     }
23196      
23197  * 
23198  * @cfg {Object} disable List of elements to disable..
23199  * @cfg {Array} btns List of additional buttons.
23200  * 
23201  * 
23202  * NEEDS Extra CSS? 
23203  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23204  */
23205  
23206 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23207 {
23208     
23209     Roo.apply(this, config);
23210     
23211     // default disabled, based on 'good practice'..
23212     this.disable = this.disable || {};
23213     Roo.applyIf(this.disable, {
23214         fontSize : true,
23215         colors : true,
23216         specialElements : true
23217     });
23218     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23219     
23220     this.editor = config.editor;
23221     this.editorcore = config.editor.editorcore;
23222     
23223     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23224     
23225     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23226     // dont call parent... till later.
23227 }
23228 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23229      
23230     bar : true,
23231     
23232     editor : false,
23233     editorcore : false,
23234     
23235     
23236     formats : [
23237         "p" ,  
23238         "h1","h2","h3","h4","h5","h6", 
23239         "pre", "code", 
23240         "abbr", "acronym", "address", "cite", "samp", "var",
23241         'div','span'
23242     ],
23243     
23244     onRender : function(ct, position)
23245     {
23246        // Roo.log("Call onRender: " + this.xtype);
23247         
23248        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23249        Roo.log(this.el);
23250        this.el.dom.style.marginBottom = '0';
23251        var _this = this;
23252        var editorcore = this.editorcore;
23253        var editor= this.editor;
23254        
23255        var children = [];
23256        var btn = function(id,cmd , toggle, handler, html){
23257        
23258             var  event = toggle ? 'toggle' : 'click';
23259        
23260             var a = {
23261                 size : 'sm',
23262                 xtype: 'Button',
23263                 xns: Roo.bootstrap,
23264                 glyphicon : id,
23265                 cmd : id || cmd,
23266                 enableToggle:toggle !== false,
23267                 html : html || '',
23268                 pressed : toggle ? false : null,
23269                 listeners : {}
23270             };
23271             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23272                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23273             };
23274             children.push(a);
23275             return a;
23276        }
23277        
23278     //    var cb_box = function...
23279         
23280         var style = {
23281                 xtype: 'Button',
23282                 size : 'sm',
23283                 xns: Roo.bootstrap,
23284                 glyphicon : 'font',
23285                 //html : 'submit'
23286                 menu : {
23287                     xtype: 'Menu',
23288                     xns: Roo.bootstrap,
23289                     items:  []
23290                 }
23291         };
23292         Roo.each(this.formats, function(f) {
23293             style.menu.items.push({
23294                 xtype :'MenuItem',
23295                 xns: Roo.bootstrap,
23296                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23297                 tagname : f,
23298                 listeners : {
23299                     click : function()
23300                     {
23301                         editorcore.insertTag(this.tagname);
23302                         editor.focus();
23303                     }
23304                 }
23305                 
23306             });
23307         });
23308         children.push(style);   
23309         
23310         btn('bold',false,true);
23311         btn('italic',false,true);
23312         btn('align-left', 'justifyleft',true);
23313         btn('align-center', 'justifycenter',true);
23314         btn('align-right' , 'justifyright',true);
23315         btn('link', false, false, function(btn) {
23316             //Roo.log("create link?");
23317             var url = prompt(this.createLinkText, this.defaultLinkValue);
23318             if(url && url != 'http:/'+'/'){
23319                 this.editorcore.relayCmd('createlink', url);
23320             }
23321         }),
23322         btn('list','insertunorderedlist',true);
23323         btn('pencil', false,true, function(btn){
23324                 Roo.log(this);
23325                 this.toggleSourceEdit(btn.pressed);
23326         });
23327         
23328         if (this.editor.btns.length > 0) {
23329             for (var i = 0; i<this.editor.btns.length; i++) {
23330                 children.push(this.editor.btns[i]);
23331             }
23332         }
23333         
23334         /*
23335         var cog = {
23336                 xtype: 'Button',
23337                 size : 'sm',
23338                 xns: Roo.bootstrap,
23339                 glyphicon : 'cog',
23340                 //html : 'submit'
23341                 menu : {
23342                     xtype: 'Menu',
23343                     xns: Roo.bootstrap,
23344                     items:  []
23345                 }
23346         };
23347         
23348         cog.menu.items.push({
23349             xtype :'MenuItem',
23350             xns: Roo.bootstrap,
23351             html : Clean styles,
23352             tagname : f,
23353             listeners : {
23354                 click : function()
23355                 {
23356                     editorcore.insertTag(this.tagname);
23357                     editor.focus();
23358                 }
23359             }
23360             
23361         });
23362        */
23363         
23364          
23365        this.xtype = 'NavSimplebar';
23366         
23367         for(var i=0;i< children.length;i++) {
23368             
23369             this.buttons.add(this.addxtypeChild(children[i]));
23370             
23371         }
23372         
23373         editor.on('editorevent', this.updateToolbar, this);
23374     },
23375     onBtnClick : function(id)
23376     {
23377        this.editorcore.relayCmd(id);
23378        this.editorcore.focus();
23379     },
23380     
23381     /**
23382      * Protected method that will not generally be called directly. It triggers
23383      * a toolbar update by reading the markup state of the current selection in the editor.
23384      */
23385     updateToolbar: function(){
23386
23387         if(!this.editorcore.activated){
23388             this.editor.onFirstFocus(); // is this neeed?
23389             return;
23390         }
23391
23392         var btns = this.buttons; 
23393         var doc = this.editorcore.doc;
23394         btns.get('bold').setActive(doc.queryCommandState('bold'));
23395         btns.get('italic').setActive(doc.queryCommandState('italic'));
23396         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23397         
23398         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23399         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23400         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23401         
23402         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23403         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23404          /*
23405         
23406         var ans = this.editorcore.getAllAncestors();
23407         if (this.formatCombo) {
23408             
23409             
23410             var store = this.formatCombo.store;
23411             this.formatCombo.setValue("");
23412             for (var i =0; i < ans.length;i++) {
23413                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23414                     // select it..
23415                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23416                     break;
23417                 }
23418             }
23419         }
23420         
23421         
23422         
23423         // hides menus... - so this cant be on a menu...
23424         Roo.bootstrap.MenuMgr.hideAll();
23425         */
23426         Roo.bootstrap.MenuMgr.hideAll();
23427         //this.editorsyncValue();
23428     },
23429     onFirstFocus: function() {
23430         this.buttons.each(function(item){
23431            item.enable();
23432         });
23433     },
23434     toggleSourceEdit : function(sourceEditMode){
23435         
23436           
23437         if(sourceEditMode){
23438             Roo.log("disabling buttons");
23439            this.buttons.each( function(item){
23440                 if(item.cmd != 'pencil'){
23441                     item.disable();
23442                 }
23443             });
23444           
23445         }else{
23446             Roo.log("enabling buttons");
23447             if(this.editorcore.initialized){
23448                 this.buttons.each( function(item){
23449                     item.enable();
23450                 });
23451             }
23452             
23453         }
23454         Roo.log("calling toggole on editor");
23455         // tell the editor that it's been pressed..
23456         this.editor.toggleSourceEdit(sourceEditMode);
23457        
23458     }
23459 });
23460
23461
23462
23463
23464
23465 /**
23466  * @class Roo.bootstrap.Table.AbstractSelectionModel
23467  * @extends Roo.util.Observable
23468  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23469  * implemented by descendant classes.  This class should not be directly instantiated.
23470  * @constructor
23471  */
23472 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23473     this.locked = false;
23474     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23475 };
23476
23477
23478 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23479     /** @ignore Called by the grid automatically. Do not call directly. */
23480     init : function(grid){
23481         this.grid = grid;
23482         this.initEvents();
23483     },
23484
23485     /**
23486      * Locks the selections.
23487      */
23488     lock : function(){
23489         this.locked = true;
23490     },
23491
23492     /**
23493      * Unlocks the selections.
23494      */
23495     unlock : function(){
23496         this.locked = false;
23497     },
23498
23499     /**
23500      * Returns true if the selections are locked.
23501      * @return {Boolean}
23502      */
23503     isLocked : function(){
23504         return this.locked;
23505     }
23506 });
23507 /**
23508  * @extends Roo.bootstrap.Table.AbstractSelectionModel
23509  * @class Roo.bootstrap.Table.RowSelectionModel
23510  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23511  * It supports multiple selections and keyboard selection/navigation. 
23512  * @constructor
23513  * @param {Object} config
23514  */
23515
23516 Roo.bootstrap.Table.RowSelectionModel = function(config){
23517     Roo.apply(this, config);
23518     this.selections = new Roo.util.MixedCollection(false, function(o){
23519         return o.id;
23520     });
23521
23522     this.last = false;
23523     this.lastActive = false;
23524
23525     this.addEvents({
23526         /**
23527              * @event selectionchange
23528              * Fires when the selection changes
23529              * @param {SelectionModel} this
23530              */
23531             "selectionchange" : true,
23532         /**
23533              * @event afterselectionchange
23534              * Fires after the selection changes (eg. by key press or clicking)
23535              * @param {SelectionModel} this
23536              */
23537             "afterselectionchange" : true,
23538         /**
23539              * @event beforerowselect
23540              * Fires when a row is selected being selected, return false to cancel.
23541              * @param {SelectionModel} this
23542              * @param {Number} rowIndex The selected index
23543              * @param {Boolean} keepExisting False if other selections will be cleared
23544              */
23545             "beforerowselect" : true,
23546         /**
23547              * @event rowselect
23548              * Fires when a row is selected.
23549              * @param {SelectionModel} this
23550              * @param {Number} rowIndex The selected index
23551              * @param {Roo.data.Record} r The record
23552              */
23553             "rowselect" : true,
23554         /**
23555              * @event rowdeselect
23556              * Fires when a row is deselected.
23557              * @param {SelectionModel} this
23558              * @param {Number} rowIndex The selected index
23559              */
23560         "rowdeselect" : true
23561     });
23562     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23563     this.locked = false;
23564  };
23565
23566 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
23567     /**
23568      * @cfg {Boolean} singleSelect
23569      * True to allow selection of only one row at a time (defaults to false)
23570      */
23571     singleSelect : false,
23572
23573     // private
23574     initEvents : function()
23575     {
23576
23577         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23578         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
23579         //}else{ // allow click to work like normal
23580          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
23581         //}
23582         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23583         this.grid.on("rowclick", this.handleMouseDown, this);
23584         
23585         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23586             "up" : function(e){
23587                 if(!e.shiftKey){
23588                     this.selectPrevious(e.shiftKey);
23589                 }else if(this.last !== false && this.lastActive !== false){
23590                     var last = this.last;
23591                     this.selectRange(this.last,  this.lastActive-1);
23592                     this.grid.getView().focusRow(this.lastActive);
23593                     if(last !== false){
23594                         this.last = last;
23595                     }
23596                 }else{
23597                     this.selectFirstRow();
23598                 }
23599                 this.fireEvent("afterselectionchange", this);
23600             },
23601             "down" : function(e){
23602                 if(!e.shiftKey){
23603                     this.selectNext(e.shiftKey);
23604                 }else if(this.last !== false && this.lastActive !== false){
23605                     var last = this.last;
23606                     this.selectRange(this.last,  this.lastActive+1);
23607                     this.grid.getView().focusRow(this.lastActive);
23608                     if(last !== false){
23609                         this.last = last;
23610                     }
23611                 }else{
23612                     this.selectFirstRow();
23613                 }
23614                 this.fireEvent("afterselectionchange", this);
23615             },
23616             scope: this
23617         });
23618         this.grid.store.on('load', function(){
23619             this.selections.clear();
23620         },this);
23621         /*
23622         var view = this.grid.view;
23623         view.on("refresh", this.onRefresh, this);
23624         view.on("rowupdated", this.onRowUpdated, this);
23625         view.on("rowremoved", this.onRemove, this);
23626         */
23627     },
23628
23629     // private
23630     onRefresh : function()
23631     {
23632         var ds = this.grid.store, i, v = this.grid.view;
23633         var s = this.selections;
23634         s.each(function(r){
23635             if((i = ds.indexOfId(r.id)) != -1){
23636                 v.onRowSelect(i);
23637             }else{
23638                 s.remove(r);
23639             }
23640         });
23641     },
23642
23643     // private
23644     onRemove : function(v, index, r){
23645         this.selections.remove(r);
23646     },
23647
23648     // private
23649     onRowUpdated : function(v, index, r){
23650         if(this.isSelected(r)){
23651             v.onRowSelect(index);
23652         }
23653     },
23654
23655     /**
23656      * Select records.
23657      * @param {Array} records The records to select
23658      * @param {Boolean} keepExisting (optional) True to keep existing selections
23659      */
23660     selectRecords : function(records, keepExisting)
23661     {
23662         if(!keepExisting){
23663             this.clearSelections();
23664         }
23665             var ds = this.grid.store;
23666         for(var i = 0, len = records.length; i < len; i++){
23667             this.selectRow(ds.indexOf(records[i]), true);
23668         }
23669     },
23670
23671     /**
23672      * Gets the number of selected rows.
23673      * @return {Number}
23674      */
23675     getCount : function(){
23676         return this.selections.length;
23677     },
23678
23679     /**
23680      * Selects the first row in the grid.
23681      */
23682     selectFirstRow : function(){
23683         this.selectRow(0);
23684     },
23685
23686     /**
23687      * Select the last row.
23688      * @param {Boolean} keepExisting (optional) True to keep existing selections
23689      */
23690     selectLastRow : function(keepExisting){
23691         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
23692         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
23693     },
23694
23695     /**
23696      * Selects the row immediately following the last selected row.
23697      * @param {Boolean} keepExisting (optional) True to keep existing selections
23698      */
23699     selectNext : function(keepExisting)
23700     {
23701             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
23702             this.selectRow(this.last+1, keepExisting);
23703             this.grid.getView().focusRow(this.last);
23704         }
23705     },
23706
23707     /**
23708      * Selects the row that precedes the last selected row.
23709      * @param {Boolean} keepExisting (optional) True to keep existing selections
23710      */
23711     selectPrevious : function(keepExisting){
23712         if(this.last){
23713             this.selectRow(this.last-1, keepExisting);
23714             this.grid.getView().focusRow(this.last);
23715         }
23716     },
23717
23718     /**
23719      * Returns the selected records
23720      * @return {Array} Array of selected records
23721      */
23722     getSelections : function(){
23723         return [].concat(this.selections.items);
23724     },
23725
23726     /**
23727      * Returns the first selected record.
23728      * @return {Record}
23729      */
23730     getSelected : function(){
23731         return this.selections.itemAt(0);
23732     },
23733
23734
23735     /**
23736      * Clears all selections.
23737      */
23738     clearSelections : function(fast)
23739     {
23740         if(this.locked) {
23741             return;
23742         }
23743         if(fast !== true){
23744                 var ds = this.grid.store;
23745             var s = this.selections;
23746             s.each(function(r){
23747                 this.deselectRow(ds.indexOfId(r.id));
23748             }, this);
23749             s.clear();
23750         }else{
23751             this.selections.clear();
23752         }
23753         this.last = false;
23754     },
23755
23756
23757     /**
23758      * Selects all rows.
23759      */
23760     selectAll : function(){
23761         if(this.locked) {
23762             return;
23763         }
23764         this.selections.clear();
23765         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
23766             this.selectRow(i, true);
23767         }
23768     },
23769
23770     /**
23771      * Returns True if there is a selection.
23772      * @return {Boolean}
23773      */
23774     hasSelection : function(){
23775         return this.selections.length > 0;
23776     },
23777
23778     /**
23779      * Returns True if the specified row is selected.
23780      * @param {Number/Record} record The record or index of the record to check
23781      * @return {Boolean}
23782      */
23783     isSelected : function(index){
23784             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
23785         return (r && this.selections.key(r.id) ? true : false);
23786     },
23787
23788     /**
23789      * Returns True if the specified record id is selected.
23790      * @param {String} id The id of record to check
23791      * @return {Boolean}
23792      */
23793     isIdSelected : function(id){
23794         return (this.selections.key(id) ? true : false);
23795     },
23796
23797
23798     // private
23799     handleMouseDBClick : function(e, t){
23800         
23801     },
23802     // private
23803     handleMouseDown : function(e, t)
23804     {
23805             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
23806         if(this.isLocked() || rowIndex < 0 ){
23807             return;
23808         };
23809         if(e.shiftKey && this.last !== false){
23810             var last = this.last;
23811             this.selectRange(last, rowIndex, e.ctrlKey);
23812             this.last = last; // reset the last
23813             t.focus();
23814     
23815         }else{
23816             var isSelected = this.isSelected(rowIndex);
23817             //Roo.log("select row:" + rowIndex);
23818             if(isSelected){
23819                 this.deselectRow(rowIndex);
23820             } else {
23821                         this.selectRow(rowIndex, true);
23822             }
23823     
23824             /*
23825                 if(e.button !== 0 && isSelected){
23826                 alert('rowIndex 2: ' + rowIndex);
23827                     view.focusRow(rowIndex);
23828                 }else if(e.ctrlKey && isSelected){
23829                     this.deselectRow(rowIndex);
23830                 }else if(!isSelected){
23831                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
23832                     view.focusRow(rowIndex);
23833                 }
23834             */
23835         }
23836         this.fireEvent("afterselectionchange", this);
23837     },
23838     // private
23839     handleDragableRowClick :  function(grid, rowIndex, e) 
23840     {
23841         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
23842             this.selectRow(rowIndex, false);
23843             grid.view.focusRow(rowIndex);
23844              this.fireEvent("afterselectionchange", this);
23845         }
23846     },
23847     
23848     /**
23849      * Selects multiple rows.
23850      * @param {Array} rows Array of the indexes of the row to select
23851      * @param {Boolean} keepExisting (optional) True to keep existing selections
23852      */
23853     selectRows : function(rows, keepExisting){
23854         if(!keepExisting){
23855             this.clearSelections();
23856         }
23857         for(var i = 0, len = rows.length; i < len; i++){
23858             this.selectRow(rows[i], true);
23859         }
23860     },
23861
23862     /**
23863      * Selects a range of rows. All rows in between startRow and endRow are also selected.
23864      * @param {Number} startRow The index of the first row in the range
23865      * @param {Number} endRow The index of the last row in the range
23866      * @param {Boolean} keepExisting (optional) True to retain existing selections
23867      */
23868     selectRange : function(startRow, endRow, keepExisting){
23869         if(this.locked) {
23870             return;
23871         }
23872         if(!keepExisting){
23873             this.clearSelections();
23874         }
23875         if(startRow <= endRow){
23876             for(var i = startRow; i <= endRow; i++){
23877                 this.selectRow(i, true);
23878             }
23879         }else{
23880             for(var i = startRow; i >= endRow; i--){
23881                 this.selectRow(i, true);
23882             }
23883         }
23884     },
23885
23886     /**
23887      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
23888      * @param {Number} startRow The index of the first row in the range
23889      * @param {Number} endRow The index of the last row in the range
23890      */
23891     deselectRange : function(startRow, endRow, preventViewNotify){
23892         if(this.locked) {
23893             return;
23894         }
23895         for(var i = startRow; i <= endRow; i++){
23896             this.deselectRow(i, preventViewNotify);
23897         }
23898     },
23899
23900     /**
23901      * Selects a row.
23902      * @param {Number} row The index of the row to select
23903      * @param {Boolean} keepExisting (optional) True to keep existing selections
23904      */
23905     selectRow : function(index, keepExisting, preventViewNotify)
23906     {
23907             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
23908             return;
23909         }
23910         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
23911             if(!keepExisting || this.singleSelect){
23912                 this.clearSelections();
23913             }
23914             
23915             var r = this.grid.store.getAt(index);
23916             //console.log('selectRow - record id :' + r.id);
23917             
23918             this.selections.add(r);
23919             this.last = this.lastActive = index;
23920             if(!preventViewNotify){
23921                 var proxy = new Roo.Element(
23922                                 this.grid.getRowDom(index)
23923                 );
23924                 proxy.addClass('bg-info info');
23925             }
23926             this.fireEvent("rowselect", this, index, r);
23927             this.fireEvent("selectionchange", this);
23928         }
23929     },
23930
23931     /**
23932      * Deselects a row.
23933      * @param {Number} row The index of the row to deselect
23934      */
23935     deselectRow : function(index, preventViewNotify)
23936     {
23937         if(this.locked) {
23938             return;
23939         }
23940         if(this.last == index){
23941             this.last = false;
23942         }
23943         if(this.lastActive == index){
23944             this.lastActive = false;
23945         }
23946         
23947         var r = this.grid.store.getAt(index);
23948         if (!r) {
23949             return;
23950         }
23951         
23952         this.selections.remove(r);
23953         //.console.log('deselectRow - record id :' + r.id);
23954         if(!preventViewNotify){
23955         
23956             var proxy = new Roo.Element(
23957                 this.grid.getRowDom(index)
23958             );
23959             proxy.removeClass('bg-info info');
23960         }
23961         this.fireEvent("rowdeselect", this, index);
23962         this.fireEvent("selectionchange", this);
23963     },
23964
23965     // private
23966     restoreLast : function(){
23967         if(this._last){
23968             this.last = this._last;
23969         }
23970     },
23971
23972     // private
23973     acceptsNav : function(row, col, cm){
23974         return !cm.isHidden(col) && cm.isCellEditable(col, row);
23975     },
23976
23977     // private
23978     onEditorKey : function(field, e){
23979         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
23980         if(k == e.TAB){
23981             e.stopEvent();
23982             ed.completeEdit();
23983             if(e.shiftKey){
23984                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
23985             }else{
23986                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
23987             }
23988         }else if(k == e.ENTER && !e.ctrlKey){
23989             e.stopEvent();
23990             ed.completeEdit();
23991             if(e.shiftKey){
23992                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
23993             }else{
23994                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
23995             }
23996         }else if(k == e.ESC){
23997             ed.cancelEdit();
23998         }
23999         if(newCell){
24000             g.startEditing(newCell[0], newCell[1]);
24001         }
24002     }
24003 });
24004 /*
24005  * Based on:
24006  * Ext JS Library 1.1.1
24007  * Copyright(c) 2006-2007, Ext JS, LLC.
24008  *
24009  * Originally Released Under LGPL - original licence link has changed is not relivant.
24010  *
24011  * Fork - LGPL
24012  * <script type="text/javascript">
24013  */
24014  
24015 /**
24016  * @class Roo.bootstrap.PagingToolbar
24017  * @extends Roo.bootstrap.NavSimplebar
24018  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24019  * @constructor
24020  * Create a new PagingToolbar
24021  * @param {Object} config The config object
24022  * @param {Roo.data.Store} store
24023  */
24024 Roo.bootstrap.PagingToolbar = function(config)
24025 {
24026     // old args format still supported... - xtype is prefered..
24027         // created from xtype...
24028     
24029     this.ds = config.dataSource;
24030     
24031     if (config.store && !this.ds) {
24032         this.store= Roo.factory(config.store, Roo.data);
24033         this.ds = this.store;
24034         this.ds.xmodule = this.xmodule || false;
24035     }
24036     
24037     this.toolbarItems = [];
24038     if (config.items) {
24039         this.toolbarItems = config.items;
24040     }
24041     
24042     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24043     
24044     this.cursor = 0;
24045     
24046     if (this.ds) { 
24047         this.bind(this.ds);
24048     }
24049     
24050     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24051     
24052 };
24053
24054 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24055     /**
24056      * @cfg {Roo.data.Store} dataSource
24057      * The underlying data store providing the paged data
24058      */
24059     /**
24060      * @cfg {String/HTMLElement/Element} container
24061      * container The id or element that will contain the toolbar
24062      */
24063     /**
24064      * @cfg {Boolean} displayInfo
24065      * True to display the displayMsg (defaults to false)
24066      */
24067     /**
24068      * @cfg {Number} pageSize
24069      * The number of records to display per page (defaults to 20)
24070      */
24071     pageSize: 20,
24072     /**
24073      * @cfg {String} displayMsg
24074      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24075      */
24076     displayMsg : 'Displaying {0} - {1} of {2}',
24077     /**
24078      * @cfg {String} emptyMsg
24079      * The message to display when no records are found (defaults to "No data to display")
24080      */
24081     emptyMsg : 'No data to display',
24082     /**
24083      * Customizable piece of the default paging text (defaults to "Page")
24084      * @type String
24085      */
24086     beforePageText : "Page",
24087     /**
24088      * Customizable piece of the default paging text (defaults to "of %0")
24089      * @type String
24090      */
24091     afterPageText : "of {0}",
24092     /**
24093      * Customizable piece of the default paging text (defaults to "First Page")
24094      * @type String
24095      */
24096     firstText : "First Page",
24097     /**
24098      * Customizable piece of the default paging text (defaults to "Previous Page")
24099      * @type String
24100      */
24101     prevText : "Previous Page",
24102     /**
24103      * Customizable piece of the default paging text (defaults to "Next Page")
24104      * @type String
24105      */
24106     nextText : "Next Page",
24107     /**
24108      * Customizable piece of the default paging text (defaults to "Last Page")
24109      * @type String
24110      */
24111     lastText : "Last Page",
24112     /**
24113      * Customizable piece of the default paging text (defaults to "Refresh")
24114      * @type String
24115      */
24116     refreshText : "Refresh",
24117
24118     buttons : false,
24119     // private
24120     onRender : function(ct, position) 
24121     {
24122         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24123         this.navgroup.parentId = this.id;
24124         this.navgroup.onRender(this.el, null);
24125         // add the buttons to the navgroup
24126         
24127         if(this.displayInfo){
24128             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24129             this.displayEl = this.el.select('.x-paging-info', true).first();
24130 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24131 //            this.displayEl = navel.el.select('span',true).first();
24132         }
24133         
24134         var _this = this;
24135         
24136         if(this.buttons){
24137             Roo.each(_this.buttons, function(e){ // this might need to use render????
24138                Roo.factory(e).onRender(_this.el, null);
24139             });
24140         }
24141             
24142         Roo.each(_this.toolbarItems, function(e) {
24143             _this.navgroup.addItem(e);
24144         });
24145         
24146         
24147         this.first = this.navgroup.addItem({
24148             tooltip: this.firstText,
24149             cls: "prev",
24150             icon : 'fa fa-backward',
24151             disabled: true,
24152             preventDefault: true,
24153             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24154         });
24155         
24156         this.prev =  this.navgroup.addItem({
24157             tooltip: this.prevText,
24158             cls: "prev",
24159             icon : 'fa fa-step-backward',
24160             disabled: true,
24161             preventDefault: true,
24162             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24163         });
24164     //this.addSeparator();
24165         
24166         
24167         var field = this.navgroup.addItem( {
24168             tagtype : 'span',
24169             cls : 'x-paging-position',
24170             
24171             html : this.beforePageText  +
24172                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24173                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24174          } ); //?? escaped?
24175         
24176         this.field = field.el.select('input', true).first();
24177         this.field.on("keydown", this.onPagingKeydown, this);
24178         this.field.on("focus", function(){this.dom.select();});
24179     
24180     
24181         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24182         //this.field.setHeight(18);
24183         //this.addSeparator();
24184         this.next = this.navgroup.addItem({
24185             tooltip: this.nextText,
24186             cls: "next",
24187             html : ' <i class="fa fa-step-forward">',
24188             disabled: true,
24189             preventDefault: true,
24190             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24191         });
24192         this.last = this.navgroup.addItem({
24193             tooltip: this.lastText,
24194             icon : 'fa fa-forward',
24195             cls: "next",
24196             disabled: true,
24197             preventDefault: true,
24198             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24199         });
24200     //this.addSeparator();
24201         this.loading = this.navgroup.addItem({
24202             tooltip: this.refreshText,
24203             icon: 'fa fa-refresh',
24204             preventDefault: true,
24205             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24206         });
24207         
24208     },
24209
24210     // private
24211     updateInfo : function(){
24212         if(this.displayEl){
24213             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24214             var msg = count == 0 ?
24215                 this.emptyMsg :
24216                 String.format(
24217                     this.displayMsg,
24218                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24219                 );
24220             this.displayEl.update(msg);
24221         }
24222     },
24223
24224     // private
24225     onLoad : function(ds, r, o)
24226     {
24227         this.cursor = o.params ? o.params.start : 0;
24228         var d = this.getPageData(),
24229             ap = d.activePage,
24230             ps = d.pages;
24231         
24232         
24233         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24234         this.field.dom.value = ap;
24235         this.first.setDisabled(ap == 1);
24236         this.prev.setDisabled(ap == 1);
24237         this.next.setDisabled(ap == ps);
24238         this.last.setDisabled(ap == ps);
24239         this.loading.enable();
24240         this.updateInfo();
24241     },
24242
24243     // private
24244     getPageData : function(){
24245         var total = this.ds.getTotalCount();
24246         return {
24247             total : total,
24248             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24249             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24250         };
24251     },
24252
24253     // private
24254     onLoadError : function(){
24255         this.loading.enable();
24256     },
24257
24258     // private
24259     onPagingKeydown : function(e){
24260         var k = e.getKey();
24261         var d = this.getPageData();
24262         if(k == e.RETURN){
24263             var v = this.field.dom.value, pageNum;
24264             if(!v || isNaN(pageNum = parseInt(v, 10))){
24265                 this.field.dom.value = d.activePage;
24266                 return;
24267             }
24268             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24269             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24270             e.stopEvent();
24271         }
24272         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))
24273         {
24274           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24275           this.field.dom.value = pageNum;
24276           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24277           e.stopEvent();
24278         }
24279         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24280         {
24281           var v = this.field.dom.value, pageNum; 
24282           var increment = (e.shiftKey) ? 10 : 1;
24283           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24284                 increment *= -1;
24285           }
24286           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24287             this.field.dom.value = d.activePage;
24288             return;
24289           }
24290           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24291           {
24292             this.field.dom.value = parseInt(v, 10) + increment;
24293             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24294             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24295           }
24296           e.stopEvent();
24297         }
24298     },
24299
24300     // private
24301     beforeLoad : function(){
24302         if(this.loading){
24303             this.loading.disable();
24304         }
24305     },
24306
24307     // private
24308     onClick : function(which){
24309         
24310         var ds = this.ds;
24311         if (!ds) {
24312             return;
24313         }
24314         
24315         switch(which){
24316             case "first":
24317                 ds.load({params:{start: 0, limit: this.pageSize}});
24318             break;
24319             case "prev":
24320                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24321             break;
24322             case "next":
24323                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24324             break;
24325             case "last":
24326                 var total = ds.getTotalCount();
24327                 var extra = total % this.pageSize;
24328                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24329                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24330             break;
24331             case "refresh":
24332                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24333             break;
24334         }
24335     },
24336
24337     /**
24338      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24339      * @param {Roo.data.Store} store The data store to unbind
24340      */
24341     unbind : function(ds){
24342         ds.un("beforeload", this.beforeLoad, this);
24343         ds.un("load", this.onLoad, this);
24344         ds.un("loadexception", this.onLoadError, this);
24345         ds.un("remove", this.updateInfo, this);
24346         ds.un("add", this.updateInfo, this);
24347         this.ds = undefined;
24348     },
24349
24350     /**
24351      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24352      * @param {Roo.data.Store} store The data store to bind
24353      */
24354     bind : function(ds){
24355         ds.on("beforeload", this.beforeLoad, this);
24356         ds.on("load", this.onLoad, this);
24357         ds.on("loadexception", this.onLoadError, this);
24358         ds.on("remove", this.updateInfo, this);
24359         ds.on("add", this.updateInfo, this);
24360         this.ds = ds;
24361     }
24362 });/*
24363  * - LGPL
24364  *
24365  * element
24366  * 
24367  */
24368
24369 /**
24370  * @class Roo.bootstrap.MessageBar
24371  * @extends Roo.bootstrap.Component
24372  * Bootstrap MessageBar class
24373  * @cfg {String} html contents of the MessageBar
24374  * @cfg {String} weight (info | success | warning | danger) default info
24375  * @cfg {String} beforeClass insert the bar before the given class
24376  * @cfg {Boolean} closable (true | false) default false
24377  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24378  * 
24379  * @constructor
24380  * Create a new Element
24381  * @param {Object} config The config object
24382  */
24383
24384 Roo.bootstrap.MessageBar = function(config){
24385     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24386 };
24387
24388 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24389     
24390     html: '',
24391     weight: 'info',
24392     closable: false,
24393     fixed: false,
24394     beforeClass: 'bootstrap-sticky-wrap',
24395     
24396     getAutoCreate : function(){
24397         
24398         var cfg = {
24399             tag: 'div',
24400             cls: 'alert alert-dismissable alert-' + this.weight,
24401             cn: [
24402                 {
24403                     tag: 'span',
24404                     cls: 'message',
24405                     html: this.html || ''
24406                 }
24407             ]
24408         };
24409         
24410         if(this.fixed){
24411             cfg.cls += ' alert-messages-fixed';
24412         }
24413         
24414         if(this.closable){
24415             cfg.cn.push({
24416                 tag: 'button',
24417                 cls: 'close',
24418                 html: 'x'
24419             });
24420         }
24421         
24422         return cfg;
24423     },
24424     
24425     onRender : function(ct, position)
24426     {
24427         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24428         
24429         if(!this.el){
24430             var cfg = Roo.apply({},  this.getAutoCreate());
24431             cfg.id = Roo.id();
24432             
24433             if (this.cls) {
24434                 cfg.cls += ' ' + this.cls;
24435             }
24436             if (this.style) {
24437                 cfg.style = this.style;
24438             }
24439             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24440             
24441             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24442         }
24443         
24444         this.el.select('>button.close').on('click', this.hide, this);
24445         
24446     },
24447     
24448     show : function()
24449     {
24450         if (!this.rendered) {
24451             this.render();
24452         }
24453         
24454         this.el.show();
24455         
24456         this.fireEvent('show', this);
24457         
24458     },
24459     
24460     hide : function()
24461     {
24462         if (!this.rendered) {
24463             this.render();
24464         }
24465         
24466         this.el.hide();
24467         
24468         this.fireEvent('hide', this);
24469     },
24470     
24471     update : function()
24472     {
24473 //        var e = this.el.dom.firstChild;
24474 //        
24475 //        if(this.closable){
24476 //            e = e.nextSibling;
24477 //        }
24478 //        
24479 //        e.data = this.html || '';
24480
24481         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24482     }
24483    
24484 });
24485
24486  
24487
24488      /*
24489  * - LGPL
24490  *
24491  * Graph
24492  * 
24493  */
24494
24495
24496 /**
24497  * @class Roo.bootstrap.Graph
24498  * @extends Roo.bootstrap.Component
24499  * Bootstrap Graph class
24500 > Prameters
24501  -sm {number} sm 4
24502  -md {number} md 5
24503  @cfg {String} graphtype  bar | vbar | pie
24504  @cfg {number} g_x coodinator | centre x (pie)
24505  @cfg {number} g_y coodinator | centre y (pie)
24506  @cfg {number} g_r radius (pie)
24507  @cfg {number} g_height height of the chart (respected by all elements in the set)
24508  @cfg {number} g_width width of the chart (respected by all elements in the set)
24509  @cfg {Object} title The title of the chart
24510     
24511  -{Array}  values
24512  -opts (object) options for the chart 
24513      o {
24514      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24515      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24516      o vgutter (number)
24517      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.
24518      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24519      o to
24520      o stretch (boolean)
24521      o }
24522  -opts (object) options for the pie
24523      o{
24524      o cut
24525      o startAngle (number)
24526      o endAngle (number)
24527      } 
24528  *
24529  * @constructor
24530  * Create a new Input
24531  * @param {Object} config The config object
24532  */
24533
24534 Roo.bootstrap.Graph = function(config){
24535     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24536     
24537     this.addEvents({
24538         // img events
24539         /**
24540          * @event click
24541          * The img click event for the img.
24542          * @param {Roo.EventObject} e
24543          */
24544         "click" : true
24545     });
24546 };
24547
24548 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
24549     
24550     sm: 4,
24551     md: 5,
24552     graphtype: 'bar',
24553     g_height: 250,
24554     g_width: 400,
24555     g_x: 50,
24556     g_y: 50,
24557     g_r: 30,
24558     opts:{
24559         //g_colors: this.colors,
24560         g_type: 'soft',
24561         g_gutter: '20%'
24562
24563     },
24564     title : false,
24565
24566     getAutoCreate : function(){
24567         
24568         var cfg = {
24569             tag: 'div',
24570             html : null
24571         };
24572         
24573         
24574         return  cfg;
24575     },
24576
24577     onRender : function(ct,position){
24578         
24579         
24580         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24581         
24582         if (typeof(Raphael) == 'undefined') {
24583             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24584             return;
24585         }
24586         
24587         this.raphael = Raphael(this.el.dom);
24588         
24589                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24590                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24591                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24592                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24593                 /*
24594                 r.text(160, 10, "Single Series Chart").attr(txtattr);
24595                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24596                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24597                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
24598                 
24599                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
24600                 r.barchart(330, 10, 300, 220, data1);
24601                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
24602                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
24603                 */
24604                 
24605                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24606                 // r.barchart(30, 30, 560, 250,  xdata, {
24607                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
24608                 //     axis : "0 0 1 1",
24609                 //     axisxlabels :  xdata
24610                 //     //yvalues : cols,
24611                    
24612                 // });
24613 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24614 //        
24615 //        this.load(null,xdata,{
24616 //                axis : "0 0 1 1",
24617 //                axisxlabels :  xdata
24618 //                });
24619
24620     },
24621
24622     load : function(graphtype,xdata,opts)
24623     {
24624         this.raphael.clear();
24625         if(!graphtype) {
24626             graphtype = this.graphtype;
24627         }
24628         if(!opts){
24629             opts = this.opts;
24630         }
24631         var r = this.raphael,
24632             fin = function () {
24633                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
24634             },
24635             fout = function () {
24636                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
24637             },
24638             pfin = function() {
24639                 this.sector.stop();
24640                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
24641
24642                 if (this.label) {
24643                     this.label[0].stop();
24644                     this.label[0].attr({ r: 7.5 });
24645                     this.label[1].attr({ "font-weight": 800 });
24646                 }
24647             },
24648             pfout = function() {
24649                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
24650
24651                 if (this.label) {
24652                     this.label[0].animate({ r: 5 }, 500, "bounce");
24653                     this.label[1].attr({ "font-weight": 400 });
24654                 }
24655             };
24656
24657         switch(graphtype){
24658             case 'bar':
24659                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24660                 break;
24661             case 'hbar':
24662                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24663                 break;
24664             case 'pie':
24665 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
24666 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
24667 //            
24668                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
24669                 
24670                 break;
24671
24672         }
24673         
24674         if(this.title){
24675             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
24676         }
24677         
24678     },
24679     
24680     setTitle: function(o)
24681     {
24682         this.title = o;
24683     },
24684     
24685     initEvents: function() {
24686         
24687         if(!this.href){
24688             this.el.on('click', this.onClick, this);
24689         }
24690     },
24691     
24692     onClick : function(e)
24693     {
24694         Roo.log('img onclick');
24695         this.fireEvent('click', this, e);
24696     }
24697    
24698 });
24699
24700  
24701 /*
24702  * - LGPL
24703  *
24704  * numberBox
24705  * 
24706  */
24707 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24708
24709 /**
24710  * @class Roo.bootstrap.dash.NumberBox
24711  * @extends Roo.bootstrap.Component
24712  * Bootstrap NumberBox class
24713  * @cfg {String} headline Box headline
24714  * @cfg {String} content Box content
24715  * @cfg {String} icon Box icon
24716  * @cfg {String} footer Footer text
24717  * @cfg {String} fhref Footer href
24718  * 
24719  * @constructor
24720  * Create a new NumberBox
24721  * @param {Object} config The config object
24722  */
24723
24724
24725 Roo.bootstrap.dash.NumberBox = function(config){
24726     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
24727     
24728 };
24729
24730 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
24731     
24732     headline : '',
24733     content : '',
24734     icon : '',
24735     footer : '',
24736     fhref : '',
24737     ficon : '',
24738     
24739     getAutoCreate : function(){
24740         
24741         var cfg = {
24742             tag : 'div',
24743             cls : 'small-box ',
24744             cn : [
24745                 {
24746                     tag : 'div',
24747                     cls : 'inner',
24748                     cn :[
24749                         {
24750                             tag : 'h3',
24751                             cls : 'roo-headline',
24752                             html : this.headline
24753                         },
24754                         {
24755                             tag : 'p',
24756                             cls : 'roo-content',
24757                             html : this.content
24758                         }
24759                     ]
24760                 }
24761             ]
24762         };
24763         
24764         if(this.icon){
24765             cfg.cn.push({
24766                 tag : 'div',
24767                 cls : 'icon',
24768                 cn :[
24769                     {
24770                         tag : 'i',
24771                         cls : 'ion ' + this.icon
24772                     }
24773                 ]
24774             });
24775         }
24776         
24777         if(this.footer){
24778             var footer = {
24779                 tag : 'a',
24780                 cls : 'small-box-footer',
24781                 href : this.fhref || '#',
24782                 html : this.footer
24783             };
24784             
24785             cfg.cn.push(footer);
24786             
24787         }
24788         
24789         return  cfg;
24790     },
24791
24792     onRender : function(ct,position){
24793         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
24794
24795
24796        
24797                 
24798     },
24799
24800     setHeadline: function (value)
24801     {
24802         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
24803     },
24804     
24805     setFooter: function (value, href)
24806     {
24807         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
24808         
24809         if(href){
24810             this.el.select('a.small-box-footer',true).first().attr('href', href);
24811         }
24812         
24813     },
24814
24815     setContent: function (value)
24816     {
24817         this.el.select('.roo-content',true).first().dom.innerHTML = value;
24818     },
24819
24820     initEvents: function() 
24821     {   
24822         
24823     }
24824     
24825 });
24826
24827  
24828 /*
24829  * - LGPL
24830  *
24831  * TabBox
24832  * 
24833  */
24834 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24835
24836 /**
24837  * @class Roo.bootstrap.dash.TabBox
24838  * @extends Roo.bootstrap.Component
24839  * Bootstrap TabBox class
24840  * @cfg {String} title Title of the TabBox
24841  * @cfg {String} icon Icon of the TabBox
24842  * @cfg {Boolean} showtabs (true|false) show the tabs default true
24843  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
24844  * 
24845  * @constructor
24846  * Create a new TabBox
24847  * @param {Object} config The config object
24848  */
24849
24850
24851 Roo.bootstrap.dash.TabBox = function(config){
24852     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
24853     this.addEvents({
24854         // raw events
24855         /**
24856          * @event addpane
24857          * When a pane is added
24858          * @param {Roo.bootstrap.dash.TabPane} pane
24859          */
24860         "addpane" : true,
24861         /**
24862          * @event activatepane
24863          * When a pane is activated
24864          * @param {Roo.bootstrap.dash.TabPane} pane
24865          */
24866         "activatepane" : true
24867         
24868          
24869     });
24870     
24871     this.panes = [];
24872 };
24873
24874 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
24875
24876     title : '',
24877     icon : false,
24878     showtabs : true,
24879     tabScrollable : false,
24880     
24881     getChildContainer : function()
24882     {
24883         return this.el.select('.tab-content', true).first();
24884     },
24885     
24886     getAutoCreate : function(){
24887         
24888         var header = {
24889             tag: 'li',
24890             cls: 'pull-left header',
24891             html: this.title,
24892             cn : []
24893         };
24894         
24895         if(this.icon){
24896             header.cn.push({
24897                 tag: 'i',
24898                 cls: 'fa ' + this.icon
24899             });
24900         }
24901         
24902         var h = {
24903             tag: 'ul',
24904             cls: 'nav nav-tabs pull-right',
24905             cn: [
24906                 header
24907             ]
24908         };
24909         
24910         if(this.tabScrollable){
24911             h = {
24912                 tag: 'div',
24913                 cls: 'tab-header',
24914                 cn: [
24915                     {
24916                         tag: 'ul',
24917                         cls: 'nav nav-tabs pull-right',
24918                         cn: [
24919                             header
24920                         ]
24921                     }
24922                 ]
24923             };
24924         }
24925         
24926         var cfg = {
24927             tag: 'div',
24928             cls: 'nav-tabs-custom',
24929             cn: [
24930                 h,
24931                 {
24932                     tag: 'div',
24933                     cls: 'tab-content no-padding',
24934                     cn: []
24935                 }
24936             ]
24937         };
24938
24939         return  cfg;
24940     },
24941     initEvents : function()
24942     {
24943         //Roo.log('add add pane handler');
24944         this.on('addpane', this.onAddPane, this);
24945     },
24946      /**
24947      * Updates the box title
24948      * @param {String} html to set the title to.
24949      */
24950     setTitle : function(value)
24951     {
24952         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
24953     },
24954     onAddPane : function(pane)
24955     {
24956         this.panes.push(pane);
24957         //Roo.log('addpane');
24958         //Roo.log(pane);
24959         // tabs are rendere left to right..
24960         if(!this.showtabs){
24961             return;
24962         }
24963         
24964         var ctr = this.el.select('.nav-tabs', true).first();
24965          
24966          
24967         var existing = ctr.select('.nav-tab',true);
24968         var qty = existing.getCount();;
24969         
24970         
24971         var tab = ctr.createChild({
24972             tag : 'li',
24973             cls : 'nav-tab' + (qty ? '' : ' active'),
24974             cn : [
24975                 {
24976                     tag : 'a',
24977                     href:'#',
24978                     html : pane.title
24979                 }
24980             ]
24981         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
24982         pane.tab = tab;
24983         
24984         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
24985         if (!qty) {
24986             pane.el.addClass('active');
24987         }
24988         
24989                 
24990     },
24991     onTabClick : function(ev,un,ob,pane)
24992     {
24993         //Roo.log('tab - prev default');
24994         ev.preventDefault();
24995         
24996         
24997         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
24998         pane.tab.addClass('active');
24999         //Roo.log(pane.title);
25000         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25001         // technically we should have a deactivate event.. but maybe add later.
25002         // and it should not de-activate the selected tab...
25003         this.fireEvent('activatepane', pane);
25004         pane.el.addClass('active');
25005         pane.fireEvent('activate');
25006         
25007         
25008     },
25009     
25010     getActivePane : function()
25011     {
25012         var r = false;
25013         Roo.each(this.panes, function(p) {
25014             if(p.el.hasClass('active')){
25015                 r = p;
25016                 return false;
25017             }
25018             
25019             return;
25020         });
25021         
25022         return r;
25023     }
25024     
25025     
25026 });
25027
25028  
25029 /*
25030  * - LGPL
25031  *
25032  * Tab pane
25033  * 
25034  */
25035 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25036 /**
25037  * @class Roo.bootstrap.TabPane
25038  * @extends Roo.bootstrap.Component
25039  * Bootstrap TabPane class
25040  * @cfg {Boolean} active (false | true) Default false
25041  * @cfg {String} title title of panel
25042
25043  * 
25044  * @constructor
25045  * Create a new TabPane
25046  * @param {Object} config The config object
25047  */
25048
25049 Roo.bootstrap.dash.TabPane = function(config){
25050     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25051     
25052     this.addEvents({
25053         // raw events
25054         /**
25055          * @event activate
25056          * When a pane is activated
25057          * @param {Roo.bootstrap.dash.TabPane} pane
25058          */
25059         "activate" : true
25060          
25061     });
25062 };
25063
25064 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25065     
25066     active : false,
25067     title : '',
25068     
25069     // the tabBox that this is attached to.
25070     tab : false,
25071      
25072     getAutoCreate : function() 
25073     {
25074         var cfg = {
25075             tag: 'div',
25076             cls: 'tab-pane'
25077         };
25078         
25079         if(this.active){
25080             cfg.cls += ' active';
25081         }
25082         
25083         return cfg;
25084     },
25085     initEvents  : function()
25086     {
25087         //Roo.log('trigger add pane handler');
25088         this.parent().fireEvent('addpane', this)
25089     },
25090     
25091      /**
25092      * Updates the tab title 
25093      * @param {String} html to set the title to.
25094      */
25095     setTitle: function(str)
25096     {
25097         if (!this.tab) {
25098             return;
25099         }
25100         this.title = str;
25101         this.tab.select('a', true).first().dom.innerHTML = str;
25102         
25103     }
25104     
25105     
25106     
25107 });
25108
25109  
25110
25111
25112  /*
25113  * - LGPL
25114  *
25115  * menu
25116  * 
25117  */
25118 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25119
25120 /**
25121  * @class Roo.bootstrap.menu.Menu
25122  * @extends Roo.bootstrap.Component
25123  * Bootstrap Menu class - container for Menu
25124  * @cfg {String} html Text of the menu
25125  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25126  * @cfg {String} icon Font awesome icon
25127  * @cfg {String} pos Menu align to (top | bottom) default bottom
25128  * 
25129  * 
25130  * @constructor
25131  * Create a new Menu
25132  * @param {Object} config The config object
25133  */
25134
25135
25136 Roo.bootstrap.menu.Menu = function(config){
25137     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25138     
25139     this.addEvents({
25140         /**
25141          * @event beforeshow
25142          * Fires before this menu is displayed
25143          * @param {Roo.bootstrap.menu.Menu} this
25144          */
25145         beforeshow : true,
25146         /**
25147          * @event beforehide
25148          * Fires before this menu is hidden
25149          * @param {Roo.bootstrap.menu.Menu} this
25150          */
25151         beforehide : true,
25152         /**
25153          * @event show
25154          * Fires after this menu is displayed
25155          * @param {Roo.bootstrap.menu.Menu} this
25156          */
25157         show : true,
25158         /**
25159          * @event hide
25160          * Fires after this menu is hidden
25161          * @param {Roo.bootstrap.menu.Menu} this
25162          */
25163         hide : true,
25164         /**
25165          * @event click
25166          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25167          * @param {Roo.bootstrap.menu.Menu} this
25168          * @param {Roo.EventObject} e
25169          */
25170         click : true
25171     });
25172     
25173 };
25174
25175 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25176     
25177     submenu : false,
25178     html : '',
25179     weight : 'default',
25180     icon : false,
25181     pos : 'bottom',
25182     
25183     
25184     getChildContainer : function() {
25185         if(this.isSubMenu){
25186             return this.el;
25187         }
25188         
25189         return this.el.select('ul.dropdown-menu', true).first();  
25190     },
25191     
25192     getAutoCreate : function()
25193     {
25194         var text = [
25195             {
25196                 tag : 'span',
25197                 cls : 'roo-menu-text',
25198                 html : this.html
25199             }
25200         ];
25201         
25202         if(this.icon){
25203             text.unshift({
25204                 tag : 'i',
25205                 cls : 'fa ' + this.icon
25206             })
25207         }
25208         
25209         
25210         var cfg = {
25211             tag : 'div',
25212             cls : 'btn-group',
25213             cn : [
25214                 {
25215                     tag : 'button',
25216                     cls : 'dropdown-button btn btn-' + this.weight,
25217                     cn : text
25218                 },
25219                 {
25220                     tag : 'button',
25221                     cls : 'dropdown-toggle btn btn-' + this.weight,
25222                     cn : [
25223                         {
25224                             tag : 'span',
25225                             cls : 'caret'
25226                         }
25227                     ]
25228                 },
25229                 {
25230                     tag : 'ul',
25231                     cls : 'dropdown-menu'
25232                 }
25233             ]
25234             
25235         };
25236         
25237         if(this.pos == 'top'){
25238             cfg.cls += ' dropup';
25239         }
25240         
25241         if(this.isSubMenu){
25242             cfg = {
25243                 tag : 'ul',
25244                 cls : 'dropdown-menu'
25245             }
25246         }
25247         
25248         return cfg;
25249     },
25250     
25251     onRender : function(ct, position)
25252     {
25253         this.isSubMenu = ct.hasClass('dropdown-submenu');
25254         
25255         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25256     },
25257     
25258     initEvents : function() 
25259     {
25260         if(this.isSubMenu){
25261             return;
25262         }
25263         
25264         this.hidden = true;
25265         
25266         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25267         this.triggerEl.on('click', this.onTriggerPress, this);
25268         
25269         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25270         this.buttonEl.on('click', this.onClick, this);
25271         
25272     },
25273     
25274     list : function()
25275     {
25276         if(this.isSubMenu){
25277             return this.el;
25278         }
25279         
25280         return this.el.select('ul.dropdown-menu', true).first();
25281     },
25282     
25283     onClick : function(e)
25284     {
25285         this.fireEvent("click", this, e);
25286     },
25287     
25288     onTriggerPress  : function(e)
25289     {   
25290         if (this.isVisible()) {
25291             this.hide();
25292         } else {
25293             this.show();
25294         }
25295     },
25296     
25297     isVisible : function(){
25298         return !this.hidden;
25299     },
25300     
25301     show : function()
25302     {
25303         this.fireEvent("beforeshow", this);
25304         
25305         this.hidden = false;
25306         this.el.addClass('open');
25307         
25308         Roo.get(document).on("mouseup", this.onMouseUp, this);
25309         
25310         this.fireEvent("show", this);
25311         
25312         
25313     },
25314     
25315     hide : function()
25316     {
25317         this.fireEvent("beforehide", this);
25318         
25319         this.hidden = true;
25320         this.el.removeClass('open');
25321         
25322         Roo.get(document).un("mouseup", this.onMouseUp);
25323         
25324         this.fireEvent("hide", this);
25325     },
25326     
25327     onMouseUp : function()
25328     {
25329         this.hide();
25330     }
25331     
25332 });
25333
25334  
25335  /*
25336  * - LGPL
25337  *
25338  * menu item
25339  * 
25340  */
25341 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25342
25343 /**
25344  * @class Roo.bootstrap.menu.Item
25345  * @extends Roo.bootstrap.Component
25346  * Bootstrap MenuItem class
25347  * @cfg {Boolean} submenu (true | false) default false
25348  * @cfg {String} html text of the item
25349  * @cfg {String} href the link
25350  * @cfg {Boolean} disable (true | false) default false
25351  * @cfg {Boolean} preventDefault (true | false) default true
25352  * @cfg {String} icon Font awesome icon
25353  * @cfg {String} pos Submenu align to (left | right) default right 
25354  * 
25355  * 
25356  * @constructor
25357  * Create a new Item
25358  * @param {Object} config The config object
25359  */
25360
25361
25362 Roo.bootstrap.menu.Item = function(config){
25363     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25364     this.addEvents({
25365         /**
25366          * @event mouseover
25367          * Fires when the mouse is hovering over this menu
25368          * @param {Roo.bootstrap.menu.Item} this
25369          * @param {Roo.EventObject} e
25370          */
25371         mouseover : true,
25372         /**
25373          * @event mouseout
25374          * Fires when the mouse exits this menu
25375          * @param {Roo.bootstrap.menu.Item} this
25376          * @param {Roo.EventObject} e
25377          */
25378         mouseout : true,
25379         // raw events
25380         /**
25381          * @event click
25382          * The raw click event for the entire grid.
25383          * @param {Roo.EventObject} e
25384          */
25385         click : true
25386     });
25387 };
25388
25389 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25390     
25391     submenu : false,
25392     href : '',
25393     html : '',
25394     preventDefault: true,
25395     disable : false,
25396     icon : false,
25397     pos : 'right',
25398     
25399     getAutoCreate : function()
25400     {
25401         var text = [
25402             {
25403                 tag : 'span',
25404                 cls : 'roo-menu-item-text',
25405                 html : this.html
25406             }
25407         ];
25408         
25409         if(this.icon){
25410             text.unshift({
25411                 tag : 'i',
25412                 cls : 'fa ' + this.icon
25413             })
25414         }
25415         
25416         var cfg = {
25417             tag : 'li',
25418             cn : [
25419                 {
25420                     tag : 'a',
25421                     href : this.href || '#',
25422                     cn : text
25423                 }
25424             ]
25425         };
25426         
25427         if(this.disable){
25428             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25429         }
25430         
25431         if(this.submenu){
25432             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25433             
25434             if(this.pos == 'left'){
25435                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25436             }
25437         }
25438         
25439         return cfg;
25440     },
25441     
25442     initEvents : function() 
25443     {
25444         this.el.on('mouseover', this.onMouseOver, this);
25445         this.el.on('mouseout', this.onMouseOut, this);
25446         
25447         this.el.select('a', true).first().on('click', this.onClick, this);
25448         
25449     },
25450     
25451     onClick : function(e)
25452     {
25453         if(this.preventDefault){
25454             e.preventDefault();
25455         }
25456         
25457         this.fireEvent("click", this, e);
25458     },
25459     
25460     onMouseOver : function(e)
25461     {
25462         if(this.submenu && this.pos == 'left'){
25463             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25464         }
25465         
25466         this.fireEvent("mouseover", this, e);
25467     },
25468     
25469     onMouseOut : function(e)
25470     {
25471         this.fireEvent("mouseout", this, e);
25472     }
25473 });
25474
25475  
25476
25477  /*
25478  * - LGPL
25479  *
25480  * menu separator
25481  * 
25482  */
25483 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25484
25485 /**
25486  * @class Roo.bootstrap.menu.Separator
25487  * @extends Roo.bootstrap.Component
25488  * Bootstrap Separator class
25489  * 
25490  * @constructor
25491  * Create a new Separator
25492  * @param {Object} config The config object
25493  */
25494
25495
25496 Roo.bootstrap.menu.Separator = function(config){
25497     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25498 };
25499
25500 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
25501     
25502     getAutoCreate : function(){
25503         var cfg = {
25504             tag : 'li',
25505             cls: 'divider'
25506         };
25507         
25508         return cfg;
25509     }
25510    
25511 });
25512
25513  
25514
25515  /*
25516  * - LGPL
25517  *
25518  * Tooltip
25519  * 
25520  */
25521
25522 /**
25523  * @class Roo.bootstrap.Tooltip
25524  * Bootstrap Tooltip class
25525  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25526  * to determine which dom element triggers the tooltip.
25527  * 
25528  * It needs to add support for additional attributes like tooltip-position
25529  * 
25530  * @constructor
25531  * Create a new Toolti
25532  * @param {Object} config The config object
25533  */
25534
25535 Roo.bootstrap.Tooltip = function(config){
25536     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25537     
25538     this.alignment = Roo.bootstrap.Tooltip.alignment;
25539     
25540     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25541         this.alignment = config.alignment;
25542     }
25543     
25544 };
25545
25546 Roo.apply(Roo.bootstrap.Tooltip, {
25547     /**
25548      * @function init initialize tooltip monitoring.
25549      * @static
25550      */
25551     currentEl : false,
25552     currentTip : false,
25553     currentRegion : false,
25554     
25555     //  init : delay?
25556     
25557     init : function()
25558     {
25559         Roo.get(document).on('mouseover', this.enter ,this);
25560         Roo.get(document).on('mouseout', this.leave, this);
25561          
25562         
25563         this.currentTip = new Roo.bootstrap.Tooltip();
25564     },
25565     
25566     enter : function(ev)
25567     {
25568         var dom = ev.getTarget();
25569         
25570         //Roo.log(['enter',dom]);
25571         var el = Roo.fly(dom);
25572         if (this.currentEl) {
25573             //Roo.log(dom);
25574             //Roo.log(this.currentEl);
25575             //Roo.log(this.currentEl.contains(dom));
25576             if (this.currentEl == el) {
25577                 return;
25578             }
25579             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25580                 return;
25581             }
25582
25583         }
25584         
25585         if (this.currentTip.el) {
25586             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25587         }    
25588         //Roo.log(ev);
25589         
25590         if(!el || el.dom == document){
25591             return;
25592         }
25593         
25594         var bindEl = el;
25595         
25596         // you can not look for children, as if el is the body.. then everythign is the child..
25597         if (!el.attr('tooltip')) { //
25598             if (!el.select("[tooltip]").elements.length) {
25599                 return;
25600             }
25601             // is the mouse over this child...?
25602             bindEl = el.select("[tooltip]").first();
25603             var xy = ev.getXY();
25604             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
25605                 //Roo.log("not in region.");
25606                 return;
25607             }
25608             //Roo.log("child element over..");
25609             
25610         }
25611         this.currentEl = bindEl;
25612         this.currentTip.bind(bindEl);
25613         this.currentRegion = Roo.lib.Region.getRegion(dom);
25614         this.currentTip.enter();
25615         
25616     },
25617     leave : function(ev)
25618     {
25619         var dom = ev.getTarget();
25620         //Roo.log(['leave',dom]);
25621         if (!this.currentEl) {
25622             return;
25623         }
25624         
25625         
25626         if (dom != this.currentEl.dom) {
25627             return;
25628         }
25629         var xy = ev.getXY();
25630         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
25631             return;
25632         }
25633         // only activate leave if mouse cursor is outside... bounding box..
25634         
25635         
25636         
25637         
25638         if (this.currentTip) {
25639             this.currentTip.leave();
25640         }
25641         //Roo.log('clear currentEl');
25642         this.currentEl = false;
25643         
25644         
25645     },
25646     alignment : {
25647         'left' : ['r-l', [-2,0], 'right'],
25648         'right' : ['l-r', [2,0], 'left'],
25649         'bottom' : ['t-b', [0,2], 'top'],
25650         'top' : [ 'b-t', [0,-2], 'bottom']
25651     }
25652     
25653 });
25654
25655
25656 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
25657     
25658     
25659     bindEl : false,
25660     
25661     delay : null, // can be { show : 300 , hide: 500}
25662     
25663     timeout : null,
25664     
25665     hoverState : null, //???
25666     
25667     placement : 'bottom', 
25668     
25669     alignment : false,
25670     
25671     getAutoCreate : function(){
25672     
25673         var cfg = {
25674            cls : 'tooltip',
25675            role : 'tooltip',
25676            cn : [
25677                 {
25678                     cls : 'tooltip-arrow'
25679                 },
25680                 {
25681                     cls : 'tooltip-inner'
25682                 }
25683            ]
25684         };
25685         
25686         return cfg;
25687     },
25688     bind : function(el)
25689     {
25690         this.bindEl = el;
25691     },
25692       
25693     
25694     enter : function () {
25695        
25696         if (this.timeout != null) {
25697             clearTimeout(this.timeout);
25698         }
25699         
25700         this.hoverState = 'in';
25701          //Roo.log("enter - show");
25702         if (!this.delay || !this.delay.show) {
25703             this.show();
25704             return;
25705         }
25706         var _t = this;
25707         this.timeout = setTimeout(function () {
25708             if (_t.hoverState == 'in') {
25709                 _t.show();
25710             }
25711         }, this.delay.show);
25712     },
25713     leave : function()
25714     {
25715         clearTimeout(this.timeout);
25716     
25717         this.hoverState = 'out';
25718          if (!this.delay || !this.delay.hide) {
25719             this.hide();
25720             return;
25721         }
25722        
25723         var _t = this;
25724         this.timeout = setTimeout(function () {
25725             //Roo.log("leave - timeout");
25726             
25727             if (_t.hoverState == 'out') {
25728                 _t.hide();
25729                 Roo.bootstrap.Tooltip.currentEl = false;
25730             }
25731         }, delay);
25732     },
25733     
25734     show : function (msg)
25735     {
25736         if (!this.el) {
25737             this.render(document.body);
25738         }
25739         // set content.
25740         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
25741         
25742         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
25743         
25744         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
25745         
25746         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
25747         
25748         var placement = typeof this.placement == 'function' ?
25749             this.placement.call(this, this.el, on_el) :
25750             this.placement;
25751             
25752         var autoToken = /\s?auto?\s?/i;
25753         var autoPlace = autoToken.test(placement);
25754         if (autoPlace) {
25755             placement = placement.replace(autoToken, '') || 'top';
25756         }
25757         
25758         //this.el.detach()
25759         //this.el.setXY([0,0]);
25760         this.el.show();
25761         //this.el.dom.style.display='block';
25762         
25763         //this.el.appendTo(on_el);
25764         
25765         var p = this.getPosition();
25766         var box = this.el.getBox();
25767         
25768         if (autoPlace) {
25769             // fixme..
25770         }
25771         
25772         var align = this.alignment[placement];
25773         
25774         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
25775         
25776         if(placement == 'top' || placement == 'bottom'){
25777             if(xy[0] < 0){
25778                 placement = 'right';
25779             }
25780             
25781             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
25782                 placement = 'left';
25783             }
25784             
25785             var scroll = Roo.select('body', true).first().getScroll();
25786             
25787             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
25788                 placement = 'top';
25789             }
25790             
25791         }
25792         
25793         this.el.alignTo(this.bindEl, align[0],align[1]);
25794         //var arrow = this.el.select('.arrow',true).first();
25795         //arrow.set(align[2], 
25796         
25797         this.el.addClass(placement);
25798         
25799         this.el.addClass('in fade');
25800         
25801         this.hoverState = null;
25802         
25803         if (this.el.hasClass('fade')) {
25804             // fade it?
25805         }
25806         
25807     },
25808     hide : function()
25809     {
25810          
25811         if (!this.el) {
25812             return;
25813         }
25814         //this.el.setXY([0,0]);
25815         this.el.removeClass('in');
25816         //this.el.hide();
25817         
25818     }
25819     
25820 });
25821  
25822
25823  /*
25824  * - LGPL
25825  *
25826  * Location Picker
25827  * 
25828  */
25829
25830 /**
25831  * @class Roo.bootstrap.LocationPicker
25832  * @extends Roo.bootstrap.Component
25833  * Bootstrap LocationPicker class
25834  * @cfg {Number} latitude Position when init default 0
25835  * @cfg {Number} longitude Position when init default 0
25836  * @cfg {Number} zoom default 15
25837  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
25838  * @cfg {Boolean} mapTypeControl default false
25839  * @cfg {Boolean} disableDoubleClickZoom default false
25840  * @cfg {Boolean} scrollwheel default true
25841  * @cfg {Boolean} streetViewControl default false
25842  * @cfg {Number} radius default 0
25843  * @cfg {String} locationName
25844  * @cfg {Boolean} draggable default true
25845  * @cfg {Boolean} enableAutocomplete default false
25846  * @cfg {Boolean} enableReverseGeocode default true
25847  * @cfg {String} markerTitle
25848  * 
25849  * @constructor
25850  * Create a new LocationPicker
25851  * @param {Object} config The config object
25852  */
25853
25854
25855 Roo.bootstrap.LocationPicker = function(config){
25856     
25857     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
25858     
25859     this.addEvents({
25860         /**
25861          * @event initial
25862          * Fires when the picker initialized.
25863          * @param {Roo.bootstrap.LocationPicker} this
25864          * @param {Google Location} location
25865          */
25866         initial : true,
25867         /**
25868          * @event positionchanged
25869          * Fires when the picker position changed.
25870          * @param {Roo.bootstrap.LocationPicker} this
25871          * @param {Google Location} location
25872          */
25873         positionchanged : true,
25874         /**
25875          * @event resize
25876          * Fires when the map resize.
25877          * @param {Roo.bootstrap.LocationPicker} this
25878          */
25879         resize : true,
25880         /**
25881          * @event show
25882          * Fires when the map show.
25883          * @param {Roo.bootstrap.LocationPicker} this
25884          */
25885         show : true,
25886         /**
25887          * @event hide
25888          * Fires when the map hide.
25889          * @param {Roo.bootstrap.LocationPicker} this
25890          */
25891         hide : true,
25892         /**
25893          * @event mapClick
25894          * Fires when click the map.
25895          * @param {Roo.bootstrap.LocationPicker} this
25896          * @param {Map event} e
25897          */
25898         mapClick : true,
25899         /**
25900          * @event mapRightClick
25901          * Fires when right click the map.
25902          * @param {Roo.bootstrap.LocationPicker} this
25903          * @param {Map event} e
25904          */
25905         mapRightClick : true,
25906         /**
25907          * @event markerClick
25908          * Fires when click the marker.
25909          * @param {Roo.bootstrap.LocationPicker} this
25910          * @param {Map event} e
25911          */
25912         markerClick : true,
25913         /**
25914          * @event markerRightClick
25915          * Fires when right click the marker.
25916          * @param {Roo.bootstrap.LocationPicker} this
25917          * @param {Map event} e
25918          */
25919         markerRightClick : true,
25920         /**
25921          * @event OverlayViewDraw
25922          * Fires when OverlayView Draw
25923          * @param {Roo.bootstrap.LocationPicker} this
25924          */
25925         OverlayViewDraw : true,
25926         /**
25927          * @event OverlayViewOnAdd
25928          * Fires when OverlayView Draw
25929          * @param {Roo.bootstrap.LocationPicker} this
25930          */
25931         OverlayViewOnAdd : true,
25932         /**
25933          * @event OverlayViewOnRemove
25934          * Fires when OverlayView Draw
25935          * @param {Roo.bootstrap.LocationPicker} this
25936          */
25937         OverlayViewOnRemove : true,
25938         /**
25939          * @event OverlayViewShow
25940          * Fires when OverlayView Draw
25941          * @param {Roo.bootstrap.LocationPicker} this
25942          * @param {Pixel} cpx
25943          */
25944         OverlayViewShow : true,
25945         /**
25946          * @event OverlayViewHide
25947          * Fires when OverlayView Draw
25948          * @param {Roo.bootstrap.LocationPicker} this
25949          */
25950         OverlayViewHide : true,
25951         /**
25952          * @event loadexception
25953          * Fires when load google lib failed.
25954          * @param {Roo.bootstrap.LocationPicker} this
25955          */
25956         loadexception : true
25957     });
25958         
25959 };
25960
25961 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
25962     
25963     gMapContext: false,
25964     
25965     latitude: 0,
25966     longitude: 0,
25967     zoom: 15,
25968     mapTypeId: false,
25969     mapTypeControl: false,
25970     disableDoubleClickZoom: false,
25971     scrollwheel: true,
25972     streetViewControl: false,
25973     radius: 0,
25974     locationName: '',
25975     draggable: true,
25976     enableAutocomplete: false,
25977     enableReverseGeocode: true,
25978     markerTitle: '',
25979     
25980     getAutoCreate: function()
25981     {
25982
25983         var cfg = {
25984             tag: 'div',
25985             cls: 'roo-location-picker'
25986         };
25987         
25988         return cfg
25989     },
25990     
25991     initEvents: function(ct, position)
25992     {       
25993         if(!this.el.getWidth() || this.isApplied()){
25994             return;
25995         }
25996         
25997         this.el.setVisibilityMode(Roo.Element.DISPLAY);
25998         
25999         this.initial();
26000     },
26001     
26002     initial: function()
26003     {
26004         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26005             this.fireEvent('loadexception', this);
26006             return;
26007         }
26008         
26009         if(!this.mapTypeId){
26010             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26011         }
26012         
26013         this.gMapContext = this.GMapContext();
26014         
26015         this.initOverlayView();
26016         
26017         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26018         
26019         var _this = this;
26020                 
26021         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26022             _this.setPosition(_this.gMapContext.marker.position);
26023         });
26024         
26025         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26026             _this.fireEvent('mapClick', this, event);
26027             
26028         });
26029
26030         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26031             _this.fireEvent('mapRightClick', this, event);
26032             
26033         });
26034         
26035         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26036             _this.fireEvent('markerClick', this, event);
26037             
26038         });
26039
26040         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26041             _this.fireEvent('markerRightClick', this, event);
26042             
26043         });
26044         
26045         this.setPosition(this.gMapContext.location);
26046         
26047         this.fireEvent('initial', this, this.gMapContext.location);
26048     },
26049     
26050     initOverlayView: function()
26051     {
26052         var _this = this;
26053         
26054         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26055             
26056             draw: function()
26057             {
26058                 _this.fireEvent('OverlayViewDraw', _this);
26059             },
26060             
26061             onAdd: function()
26062             {
26063                 _this.fireEvent('OverlayViewOnAdd', _this);
26064             },
26065             
26066             onRemove: function()
26067             {
26068                 _this.fireEvent('OverlayViewOnRemove', _this);
26069             },
26070             
26071             show: function(cpx)
26072             {
26073                 _this.fireEvent('OverlayViewShow', _this, cpx);
26074             },
26075             
26076             hide: function()
26077             {
26078                 _this.fireEvent('OverlayViewHide', _this);
26079             }
26080             
26081         });
26082     },
26083     
26084     fromLatLngToContainerPixel: function(event)
26085     {
26086         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26087     },
26088     
26089     isApplied: function() 
26090     {
26091         return this.getGmapContext() == false ? false : true;
26092     },
26093     
26094     getGmapContext: function() 
26095     {
26096         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26097     },
26098     
26099     GMapContext: function() 
26100     {
26101         var position = new google.maps.LatLng(this.latitude, this.longitude);
26102         
26103         var _map = new google.maps.Map(this.el.dom, {
26104             center: position,
26105             zoom: this.zoom,
26106             mapTypeId: this.mapTypeId,
26107             mapTypeControl: this.mapTypeControl,
26108             disableDoubleClickZoom: this.disableDoubleClickZoom,
26109             scrollwheel: this.scrollwheel,
26110             streetViewControl: this.streetViewControl,
26111             locationName: this.locationName,
26112             draggable: this.draggable,
26113             enableAutocomplete: this.enableAutocomplete,
26114             enableReverseGeocode: this.enableReverseGeocode
26115         });
26116         
26117         var _marker = new google.maps.Marker({
26118             position: position,
26119             map: _map,
26120             title: this.markerTitle,
26121             draggable: this.draggable
26122         });
26123         
26124         return {
26125             map: _map,
26126             marker: _marker,
26127             circle: null,
26128             location: position,
26129             radius: this.radius,
26130             locationName: this.locationName,
26131             addressComponents: {
26132                 formatted_address: null,
26133                 addressLine1: null,
26134                 addressLine2: null,
26135                 streetName: null,
26136                 streetNumber: null,
26137                 city: null,
26138                 district: null,
26139                 state: null,
26140                 stateOrProvince: null
26141             },
26142             settings: this,
26143             domContainer: this.el.dom,
26144             geodecoder: new google.maps.Geocoder()
26145         };
26146     },
26147     
26148     drawCircle: function(center, radius, options) 
26149     {
26150         if (this.gMapContext.circle != null) {
26151             this.gMapContext.circle.setMap(null);
26152         }
26153         if (radius > 0) {
26154             radius *= 1;
26155             options = Roo.apply({}, options, {
26156                 strokeColor: "#0000FF",
26157                 strokeOpacity: .35,
26158                 strokeWeight: 2,
26159                 fillColor: "#0000FF",
26160                 fillOpacity: .2
26161             });
26162             
26163             options.map = this.gMapContext.map;
26164             options.radius = radius;
26165             options.center = center;
26166             this.gMapContext.circle = new google.maps.Circle(options);
26167             return this.gMapContext.circle;
26168         }
26169         
26170         return null;
26171     },
26172     
26173     setPosition: function(location) 
26174     {
26175         this.gMapContext.location = location;
26176         this.gMapContext.marker.setPosition(location);
26177         this.gMapContext.map.panTo(location);
26178         this.drawCircle(location, this.gMapContext.radius, {});
26179         
26180         var _this = this;
26181         
26182         if (this.gMapContext.settings.enableReverseGeocode) {
26183             this.gMapContext.geodecoder.geocode({
26184                 latLng: this.gMapContext.location
26185             }, function(results, status) {
26186                 
26187                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26188                     _this.gMapContext.locationName = results[0].formatted_address;
26189                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26190                     
26191                     _this.fireEvent('positionchanged', this, location);
26192                 }
26193             });
26194             
26195             return;
26196         }
26197         
26198         this.fireEvent('positionchanged', this, location);
26199     },
26200     
26201     resize: function()
26202     {
26203         google.maps.event.trigger(this.gMapContext.map, "resize");
26204         
26205         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26206         
26207         this.fireEvent('resize', this);
26208     },
26209     
26210     setPositionByLatLng: function(latitude, longitude)
26211     {
26212         this.setPosition(new google.maps.LatLng(latitude, longitude));
26213     },
26214     
26215     getCurrentPosition: function() 
26216     {
26217         return {
26218             latitude: this.gMapContext.location.lat(),
26219             longitude: this.gMapContext.location.lng()
26220         };
26221     },
26222     
26223     getAddressName: function() 
26224     {
26225         return this.gMapContext.locationName;
26226     },
26227     
26228     getAddressComponents: function() 
26229     {
26230         return this.gMapContext.addressComponents;
26231     },
26232     
26233     address_component_from_google_geocode: function(address_components) 
26234     {
26235         var result = {};
26236         
26237         for (var i = 0; i < address_components.length; i++) {
26238             var component = address_components[i];
26239             if (component.types.indexOf("postal_code") >= 0) {
26240                 result.postalCode = component.short_name;
26241             } else if (component.types.indexOf("street_number") >= 0) {
26242                 result.streetNumber = component.short_name;
26243             } else if (component.types.indexOf("route") >= 0) {
26244                 result.streetName = component.short_name;
26245             } else if (component.types.indexOf("neighborhood") >= 0) {
26246                 result.city = component.short_name;
26247             } else if (component.types.indexOf("locality") >= 0) {
26248                 result.city = component.short_name;
26249             } else if (component.types.indexOf("sublocality") >= 0) {
26250                 result.district = component.short_name;
26251             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26252                 result.stateOrProvince = component.short_name;
26253             } else if (component.types.indexOf("country") >= 0) {
26254                 result.country = component.short_name;
26255             }
26256         }
26257         
26258         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26259         result.addressLine2 = "";
26260         return result;
26261     },
26262     
26263     setZoomLevel: function(zoom)
26264     {
26265         this.gMapContext.map.setZoom(zoom);
26266     },
26267     
26268     show: function()
26269     {
26270         if(!this.el){
26271             return;
26272         }
26273         
26274         this.el.show();
26275         
26276         this.resize();
26277         
26278         this.fireEvent('show', this);
26279     },
26280     
26281     hide: function()
26282     {
26283         if(!this.el){
26284             return;
26285         }
26286         
26287         this.el.hide();
26288         
26289         this.fireEvent('hide', this);
26290     }
26291     
26292 });
26293
26294 Roo.apply(Roo.bootstrap.LocationPicker, {
26295     
26296     OverlayView : function(map, options)
26297     {
26298         options = options || {};
26299         
26300         this.setMap(map);
26301     }
26302     
26303     
26304 });/*
26305  * - LGPL
26306  *
26307  * Alert
26308  * 
26309  */
26310
26311 /**
26312  * @class Roo.bootstrap.Alert
26313  * @extends Roo.bootstrap.Component
26314  * Bootstrap Alert class
26315  * @cfg {String} title The title of alert
26316  * @cfg {String} html The content of alert
26317  * @cfg {String} weight (  success | info | warning | danger )
26318  * @cfg {String} faicon font-awesomeicon
26319  * 
26320  * @constructor
26321  * Create a new alert
26322  * @param {Object} config The config object
26323  */
26324
26325
26326 Roo.bootstrap.Alert = function(config){
26327     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26328     
26329 };
26330
26331 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26332     
26333     title: '',
26334     html: '',
26335     weight: false,
26336     faicon: false,
26337     
26338     getAutoCreate : function()
26339     {
26340         
26341         var cfg = {
26342             tag : 'div',
26343             cls : 'alert',
26344             cn : [
26345                 {
26346                     tag : 'i',
26347                     cls : 'roo-alert-icon'
26348                     
26349                 },
26350                 {
26351                     tag : 'b',
26352                     cls : 'roo-alert-title',
26353                     html : this.title
26354                 },
26355                 {
26356                     tag : 'span',
26357                     cls : 'roo-alert-text',
26358                     html : this.html
26359                 }
26360             ]
26361         };
26362         
26363         if(this.faicon){
26364             cfg.cn[0].cls += ' fa ' + this.faicon;
26365         }
26366         
26367         if(this.weight){
26368             cfg.cls += ' alert-' + this.weight;
26369         }
26370         
26371         return cfg;
26372     },
26373     
26374     initEvents: function() 
26375     {
26376         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26377     },
26378     
26379     setTitle : function(str)
26380     {
26381         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26382     },
26383     
26384     setText : function(str)
26385     {
26386         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26387     },
26388     
26389     setWeight : function(weight)
26390     {
26391         if(this.weight){
26392             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26393         }
26394         
26395         this.weight = weight;
26396         
26397         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26398     },
26399     
26400     setIcon : function(icon)
26401     {
26402         if(this.faicon){
26403             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26404         }
26405         
26406         this.faicon = icon;
26407         
26408         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26409     },
26410     
26411     hide: function() 
26412     {
26413         this.el.hide();   
26414     },
26415     
26416     show: function() 
26417     {  
26418         this.el.show();   
26419     }
26420     
26421 });
26422
26423  
26424 /*
26425 * Licence: LGPL
26426 */
26427
26428 /**
26429  * @class Roo.bootstrap.UploadCropbox
26430  * @extends Roo.bootstrap.Component
26431  * Bootstrap UploadCropbox class
26432  * @cfg {String} emptyText show when image has been loaded
26433  * @cfg {String} rotateNotify show when image too small to rotate
26434  * @cfg {Number} errorTimeout default 3000
26435  * @cfg {Number} minWidth default 300
26436  * @cfg {Number} minHeight default 300
26437  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26438  * @cfg {Boolean} isDocument (true|false) default false
26439  * @cfg {String} url action url
26440  * @cfg {String} paramName default 'imageUpload'
26441  * @cfg {String} method default POST
26442  * @cfg {Boolean} loadMask (true|false) default true
26443  * @cfg {Boolean} loadingText default 'Loading...'
26444  * 
26445  * @constructor
26446  * Create a new UploadCropbox
26447  * @param {Object} config The config object
26448  */
26449
26450 Roo.bootstrap.UploadCropbox = function(config){
26451     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26452     
26453     this.addEvents({
26454         /**
26455          * @event beforeselectfile
26456          * Fire before select file
26457          * @param {Roo.bootstrap.UploadCropbox} this
26458          */
26459         "beforeselectfile" : true,
26460         /**
26461          * @event initial
26462          * Fire after initEvent
26463          * @param {Roo.bootstrap.UploadCropbox} this
26464          */
26465         "initial" : true,
26466         /**
26467          * @event crop
26468          * Fire after initEvent
26469          * @param {Roo.bootstrap.UploadCropbox} this
26470          * @param {String} data
26471          */
26472         "crop" : true,
26473         /**
26474          * @event prepare
26475          * Fire when preparing the file data
26476          * @param {Roo.bootstrap.UploadCropbox} this
26477          * @param {Object} file
26478          */
26479         "prepare" : true,
26480         /**
26481          * @event exception
26482          * Fire when get exception
26483          * @param {Roo.bootstrap.UploadCropbox} this
26484          * @param {XMLHttpRequest} xhr
26485          */
26486         "exception" : true,
26487         /**
26488          * @event beforeloadcanvas
26489          * Fire before load the canvas
26490          * @param {Roo.bootstrap.UploadCropbox} this
26491          * @param {String} src
26492          */
26493         "beforeloadcanvas" : true,
26494         /**
26495          * @event trash
26496          * Fire when trash image
26497          * @param {Roo.bootstrap.UploadCropbox} this
26498          */
26499         "trash" : true,
26500         /**
26501          * @event download
26502          * Fire when download the image
26503          * @param {Roo.bootstrap.UploadCropbox} this
26504          */
26505         "download" : true,
26506         /**
26507          * @event footerbuttonclick
26508          * Fire when footerbuttonclick
26509          * @param {Roo.bootstrap.UploadCropbox} this
26510          * @param {String} type
26511          */
26512         "footerbuttonclick" : true,
26513         /**
26514          * @event resize
26515          * Fire when resize
26516          * @param {Roo.bootstrap.UploadCropbox} this
26517          */
26518         "resize" : true,
26519         /**
26520          * @event rotate
26521          * Fire when rotate the image
26522          * @param {Roo.bootstrap.UploadCropbox} this
26523          * @param {String} pos
26524          */
26525         "rotate" : true,
26526         /**
26527          * @event inspect
26528          * Fire when inspect the file
26529          * @param {Roo.bootstrap.UploadCropbox} this
26530          * @param {Object} file
26531          */
26532         "inspect" : true,
26533         /**
26534          * @event upload
26535          * Fire when xhr upload the file
26536          * @param {Roo.bootstrap.UploadCropbox} this
26537          * @param {Object} data
26538          */
26539         "upload" : true,
26540         /**
26541          * @event arrange
26542          * Fire when arrange the file data
26543          * @param {Roo.bootstrap.UploadCropbox} this
26544          * @param {Object} formData
26545          */
26546         "arrange" : true
26547     });
26548     
26549     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26550 };
26551
26552 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
26553     
26554     emptyText : 'Click to upload image',
26555     rotateNotify : 'Image is too small to rotate',
26556     errorTimeout : 3000,
26557     scale : 0,
26558     baseScale : 1,
26559     rotate : 0,
26560     dragable : false,
26561     pinching : false,
26562     mouseX : 0,
26563     mouseY : 0,
26564     cropData : false,
26565     minWidth : 300,
26566     minHeight : 300,
26567     file : false,
26568     exif : {},
26569     baseRotate : 1,
26570     cropType : 'image/jpeg',
26571     buttons : false,
26572     canvasLoaded : false,
26573     isDocument : false,
26574     method : 'POST',
26575     paramName : 'imageUpload',
26576     loadMask : true,
26577     loadingText : 'Loading...',
26578     maskEl : false,
26579     
26580     getAutoCreate : function()
26581     {
26582         var cfg = {
26583             tag : 'div',
26584             cls : 'roo-upload-cropbox',
26585             cn : [
26586                 {
26587                     tag : 'input',
26588                     cls : 'roo-upload-cropbox-selector',
26589                     type : 'file'
26590                 },
26591                 {
26592                     tag : 'div',
26593                     cls : 'roo-upload-cropbox-body',
26594                     style : 'cursor:pointer',
26595                     cn : [
26596                         {
26597                             tag : 'div',
26598                             cls : 'roo-upload-cropbox-preview'
26599                         },
26600                         {
26601                             tag : 'div',
26602                             cls : 'roo-upload-cropbox-thumb'
26603                         },
26604                         {
26605                             tag : 'div',
26606                             cls : 'roo-upload-cropbox-empty-notify',
26607                             html : this.emptyText
26608                         },
26609                         {
26610                             tag : 'div',
26611                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
26612                             html : this.rotateNotify
26613                         }
26614                     ]
26615                 },
26616                 {
26617                     tag : 'div',
26618                     cls : 'roo-upload-cropbox-footer',
26619                     cn : {
26620                         tag : 'div',
26621                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
26622                         cn : []
26623                     }
26624                 }
26625             ]
26626         };
26627         
26628         return cfg;
26629     },
26630     
26631     onRender : function(ct, position)
26632     {
26633         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
26634         
26635         if (this.buttons.length) {
26636             
26637             Roo.each(this.buttons, function(bb) {
26638                 
26639                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
26640                 
26641                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
26642                 
26643             }, this);
26644         }
26645         
26646         if(this.loadMask){
26647             this.maskEl = this.el;
26648         }
26649     },
26650     
26651     initEvents : function()
26652     {
26653         this.urlAPI = (window.createObjectURL && window) || 
26654                                 (window.URL && URL.revokeObjectURL && URL) || 
26655                                 (window.webkitURL && webkitURL);
26656                         
26657         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
26658         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26659         
26660         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
26661         this.selectorEl.hide();
26662         
26663         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
26664         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26665         
26666         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
26667         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26668         this.thumbEl.hide();
26669         
26670         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
26671         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26672         
26673         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
26674         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26675         this.errorEl.hide();
26676         
26677         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
26678         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26679         this.footerEl.hide();
26680         
26681         this.setThumbBoxSize();
26682         
26683         this.bind();
26684         
26685         this.resize();
26686         
26687         this.fireEvent('initial', this);
26688     },
26689
26690     bind : function()
26691     {
26692         var _this = this;
26693         
26694         window.addEventListener("resize", function() { _this.resize(); } );
26695         
26696         this.bodyEl.on('click', this.beforeSelectFile, this);
26697         
26698         if(Roo.isTouch){
26699             this.bodyEl.on('touchstart', this.onTouchStart, this);
26700             this.bodyEl.on('touchmove', this.onTouchMove, this);
26701             this.bodyEl.on('touchend', this.onTouchEnd, this);
26702         }
26703         
26704         if(!Roo.isTouch){
26705             this.bodyEl.on('mousedown', this.onMouseDown, this);
26706             this.bodyEl.on('mousemove', this.onMouseMove, this);
26707             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
26708             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
26709             Roo.get(document).on('mouseup', this.onMouseUp, this);
26710         }
26711         
26712         this.selectorEl.on('change', this.onFileSelected, this);
26713     },
26714     
26715     reset : function()
26716     {    
26717         this.scale = 0;
26718         this.baseScale = 1;
26719         this.rotate = 0;
26720         this.baseRotate = 1;
26721         this.dragable = false;
26722         this.pinching = false;
26723         this.mouseX = 0;
26724         this.mouseY = 0;
26725         this.cropData = false;
26726         this.notifyEl.dom.innerHTML = this.emptyText;
26727         
26728         this.selectorEl.dom.value = '';
26729         
26730     },
26731     
26732     resize : function()
26733     {
26734         if(this.fireEvent('resize', this) != false){
26735             this.setThumbBoxPosition();
26736             this.setCanvasPosition();
26737         }
26738     },
26739     
26740     onFooterButtonClick : function(e, el, o, type)
26741     {
26742         switch (type) {
26743             case 'rotate-left' :
26744                 this.onRotateLeft(e);
26745                 break;
26746             case 'rotate-right' :
26747                 this.onRotateRight(e);
26748                 break;
26749             case 'picture' :
26750                 this.beforeSelectFile(e);
26751                 break;
26752             case 'trash' :
26753                 this.trash(e);
26754                 break;
26755             case 'crop' :
26756                 this.crop(e);
26757                 break;
26758             case 'download' :
26759                 this.download(e);
26760                 break;
26761             default :
26762                 break;
26763         }
26764         
26765         this.fireEvent('footerbuttonclick', this, type);
26766     },
26767     
26768     beforeSelectFile : function(e)
26769     {
26770         e.preventDefault();
26771         
26772         if(this.fireEvent('beforeselectfile', this) != false){
26773             this.selectorEl.dom.click();
26774         }
26775     },
26776     
26777     onFileSelected : function(e)
26778     {
26779         e.preventDefault();
26780         
26781         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26782             return;
26783         }
26784         
26785         var file = this.selectorEl.dom.files[0];
26786         
26787         if(this.fireEvent('inspect', this, file) != false){
26788             this.prepare(file);
26789         }
26790         
26791     },
26792     
26793     trash : function(e)
26794     {
26795         this.fireEvent('trash', this);
26796     },
26797     
26798     download : function(e)
26799     {
26800         this.fireEvent('download', this);
26801     },
26802     
26803     loadCanvas : function(src)
26804     {   
26805         if(this.fireEvent('beforeloadcanvas', this, src) != false){
26806             
26807             this.reset();
26808             
26809             this.imageEl = document.createElement('img');
26810             
26811             var _this = this;
26812             
26813             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
26814             
26815             this.imageEl.src = src;
26816         }
26817     },
26818     
26819     onLoadCanvas : function()
26820     {   
26821         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
26822         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
26823         
26824         this.bodyEl.un('click', this.beforeSelectFile, this);
26825         
26826         this.notifyEl.hide();
26827         this.thumbEl.show();
26828         this.footerEl.show();
26829         
26830         this.baseRotateLevel();
26831         
26832         if(this.isDocument){
26833             this.setThumbBoxSize();
26834         }
26835         
26836         this.setThumbBoxPosition();
26837         
26838         this.baseScaleLevel();
26839         
26840         this.draw();
26841         
26842         this.resize();
26843         
26844         this.canvasLoaded = true;
26845         
26846         if(this.loadMask){
26847             this.maskEl.unmask();
26848         }
26849         
26850     },
26851     
26852     setCanvasPosition : function()
26853     {   
26854         if(!this.canvasEl){
26855             return;
26856         }
26857         
26858         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
26859         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
26860         
26861         this.previewEl.setLeft(pw);
26862         this.previewEl.setTop(ph);
26863         
26864     },
26865     
26866     onMouseDown : function(e)
26867     {   
26868         e.stopEvent();
26869         
26870         this.dragable = true;
26871         this.pinching = false;
26872         
26873         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
26874             this.dragable = false;
26875             return;
26876         }
26877         
26878         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26879         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26880         
26881     },
26882     
26883     onMouseMove : function(e)
26884     {   
26885         e.stopEvent();
26886         
26887         if(!this.canvasLoaded){
26888             return;
26889         }
26890         
26891         if (!this.dragable){
26892             return;
26893         }
26894         
26895         var minX = Math.ceil(this.thumbEl.getLeft(true));
26896         var minY = Math.ceil(this.thumbEl.getTop(true));
26897         
26898         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
26899         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
26900         
26901         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26902         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26903         
26904         x = x - this.mouseX;
26905         y = y - this.mouseY;
26906         
26907         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
26908         var bgY = Math.ceil(y + this.previewEl.getTop(true));
26909         
26910         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
26911         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
26912         
26913         this.previewEl.setLeft(bgX);
26914         this.previewEl.setTop(bgY);
26915         
26916         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
26917         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
26918     },
26919     
26920     onMouseUp : function(e)
26921     {   
26922         e.stopEvent();
26923         
26924         this.dragable = false;
26925     },
26926     
26927     onMouseWheel : function(e)
26928     {   
26929         e.stopEvent();
26930         
26931         this.startScale = this.scale;
26932         
26933         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
26934         
26935         if(!this.zoomable()){
26936             this.scale = this.startScale;
26937             return;
26938         }
26939         
26940         this.draw();
26941         
26942         return;
26943     },
26944     
26945     zoomable : function()
26946     {
26947         var minScale = this.thumbEl.getWidth() / this.minWidth;
26948         
26949         if(this.minWidth < this.minHeight){
26950             minScale = this.thumbEl.getHeight() / this.minHeight;
26951         }
26952         
26953         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
26954         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
26955         
26956         if(
26957                 this.isDocument &&
26958                 (this.rotate == 0 || this.rotate == 180) && 
26959                 (
26960                     width > this.imageEl.OriginWidth || 
26961                     height > this.imageEl.OriginHeight ||
26962                     (width < this.minWidth && height < this.minHeight)
26963                 )
26964         ){
26965             return false;
26966         }
26967         
26968         if(
26969                 this.isDocument &&
26970                 (this.rotate == 90 || this.rotate == 270) && 
26971                 (
26972                     width > this.imageEl.OriginWidth || 
26973                     height > this.imageEl.OriginHeight ||
26974                     (width < this.minHeight && height < this.minWidth)
26975                 )
26976         ){
26977             return false;
26978         }
26979         
26980         if(
26981                 !this.isDocument &&
26982                 (this.rotate == 0 || this.rotate == 180) && 
26983                 (
26984                     width < this.minWidth || 
26985                     width > this.imageEl.OriginWidth || 
26986                     height < this.minHeight || 
26987                     height > this.imageEl.OriginHeight
26988                 )
26989         ){
26990             return false;
26991         }
26992         
26993         if(
26994                 !this.isDocument &&
26995                 (this.rotate == 90 || this.rotate == 270) && 
26996                 (
26997                     width < this.minHeight || 
26998                     width > this.imageEl.OriginWidth || 
26999                     height < this.minWidth || 
27000                     height > this.imageEl.OriginHeight
27001                 )
27002         ){
27003             return false;
27004         }
27005         
27006         return true;
27007         
27008     },
27009     
27010     onRotateLeft : function(e)
27011     {   
27012         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27013             
27014             var minScale = this.thumbEl.getWidth() / this.minWidth;
27015             
27016             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27017             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27018             
27019             this.startScale = this.scale;
27020             
27021             while (this.getScaleLevel() < minScale){
27022             
27023                 this.scale = this.scale + 1;
27024                 
27025                 if(!this.zoomable()){
27026                     break;
27027                 }
27028                 
27029                 if(
27030                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27031                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27032                 ){
27033                     continue;
27034                 }
27035                 
27036                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27037
27038                 this.draw();
27039                 
27040                 return;
27041             }
27042             
27043             this.scale = this.startScale;
27044             
27045             this.onRotateFail();
27046             
27047             return false;
27048         }
27049         
27050         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27051
27052         if(this.isDocument){
27053             this.setThumbBoxSize();
27054             this.setThumbBoxPosition();
27055             this.setCanvasPosition();
27056         }
27057         
27058         this.draw();
27059         
27060         this.fireEvent('rotate', this, 'left');
27061         
27062     },
27063     
27064     onRotateRight : function(e)
27065     {
27066         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27067             
27068             var minScale = this.thumbEl.getWidth() / this.minWidth;
27069         
27070             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27071             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27072             
27073             this.startScale = this.scale;
27074             
27075             while (this.getScaleLevel() < minScale){
27076             
27077                 this.scale = this.scale + 1;
27078                 
27079                 if(!this.zoomable()){
27080                     break;
27081                 }
27082                 
27083                 if(
27084                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27085                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27086                 ){
27087                     continue;
27088                 }
27089                 
27090                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27091
27092                 this.draw();
27093                 
27094                 return;
27095             }
27096             
27097             this.scale = this.startScale;
27098             
27099             this.onRotateFail();
27100             
27101             return false;
27102         }
27103         
27104         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27105
27106         if(this.isDocument){
27107             this.setThumbBoxSize();
27108             this.setThumbBoxPosition();
27109             this.setCanvasPosition();
27110         }
27111         
27112         this.draw();
27113         
27114         this.fireEvent('rotate', this, 'right');
27115     },
27116     
27117     onRotateFail : function()
27118     {
27119         this.errorEl.show(true);
27120         
27121         var _this = this;
27122         
27123         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27124     },
27125     
27126     draw : function()
27127     {
27128         this.previewEl.dom.innerHTML = '';
27129         
27130         var canvasEl = document.createElement("canvas");
27131         
27132         var contextEl = canvasEl.getContext("2d");
27133         
27134         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27135         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27136         var center = this.imageEl.OriginWidth / 2;
27137         
27138         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27139             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27140             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27141             center = this.imageEl.OriginHeight / 2;
27142         }
27143         
27144         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27145         
27146         contextEl.translate(center, center);
27147         contextEl.rotate(this.rotate * Math.PI / 180);
27148
27149         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27150         
27151         this.canvasEl = document.createElement("canvas");
27152         
27153         this.contextEl = this.canvasEl.getContext("2d");
27154         
27155         switch (this.rotate) {
27156             case 0 :
27157                 
27158                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27159                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27160                 
27161                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27162                 
27163                 break;
27164             case 90 : 
27165                 
27166                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27167                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27168                 
27169                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27170                     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);
27171                     break;
27172                 }
27173                 
27174                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27175                 
27176                 break;
27177             case 180 :
27178                 
27179                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27180                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27181                 
27182                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27183                     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);
27184                     break;
27185                 }
27186                 
27187                 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);
27188                 
27189                 break;
27190             case 270 :
27191                 
27192                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27193                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27194         
27195                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27196                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27197                     break;
27198                 }
27199                 
27200                 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);
27201                 
27202                 break;
27203             default : 
27204                 break;
27205         }
27206         
27207         this.previewEl.appendChild(this.canvasEl);
27208         
27209         this.setCanvasPosition();
27210     },
27211     
27212     crop : function()
27213     {
27214         if(!this.canvasLoaded){
27215             return;
27216         }
27217         
27218         var imageCanvas = document.createElement("canvas");
27219         
27220         var imageContext = imageCanvas.getContext("2d");
27221         
27222         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27223         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27224         
27225         var center = imageCanvas.width / 2;
27226         
27227         imageContext.translate(center, center);
27228         
27229         imageContext.rotate(this.rotate * Math.PI / 180);
27230         
27231         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27232         
27233         var canvas = document.createElement("canvas");
27234         
27235         var context = canvas.getContext("2d");
27236                 
27237         canvas.width = this.minWidth;
27238         canvas.height = this.minHeight;
27239
27240         switch (this.rotate) {
27241             case 0 :
27242                 
27243                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27244                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27245                 
27246                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27247                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27248                 
27249                 var targetWidth = this.minWidth - 2 * x;
27250                 var targetHeight = this.minHeight - 2 * y;
27251                 
27252                 var scale = 1;
27253                 
27254                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27255                     scale = targetWidth / width;
27256                 }
27257                 
27258                 if(x > 0 && y == 0){
27259                     scale = targetHeight / height;
27260                 }
27261                 
27262                 if(x > 0 && y > 0){
27263                     scale = targetWidth / width;
27264                     
27265                     if(width < height){
27266                         scale = targetHeight / height;
27267                     }
27268                 }
27269                 
27270                 context.scale(scale, scale);
27271                 
27272                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27273                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27274
27275                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27276                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27277
27278                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27279                 
27280                 break;
27281             case 90 : 
27282                 
27283                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27284                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27285                 
27286                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27287                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27288                 
27289                 var targetWidth = this.minWidth - 2 * x;
27290                 var targetHeight = this.minHeight - 2 * y;
27291                 
27292                 var scale = 1;
27293                 
27294                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27295                     scale = targetWidth / width;
27296                 }
27297                 
27298                 if(x > 0 && y == 0){
27299                     scale = targetHeight / height;
27300                 }
27301                 
27302                 if(x > 0 && y > 0){
27303                     scale = targetWidth / width;
27304                     
27305                     if(width < height){
27306                         scale = targetHeight / height;
27307                     }
27308                 }
27309                 
27310                 context.scale(scale, scale);
27311                 
27312                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27313                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27314
27315                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27316                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27317                 
27318                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27319                 
27320                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27321                 
27322                 break;
27323             case 180 :
27324                 
27325                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27326                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27327                 
27328                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27329                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27330                 
27331                 var targetWidth = this.minWidth - 2 * x;
27332                 var targetHeight = this.minHeight - 2 * y;
27333                 
27334                 var scale = 1;
27335                 
27336                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27337                     scale = targetWidth / width;
27338                 }
27339                 
27340                 if(x > 0 && y == 0){
27341                     scale = targetHeight / height;
27342                 }
27343                 
27344                 if(x > 0 && y > 0){
27345                     scale = targetWidth / width;
27346                     
27347                     if(width < height){
27348                         scale = targetHeight / height;
27349                     }
27350                 }
27351                 
27352                 context.scale(scale, scale);
27353                 
27354                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27355                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27356
27357                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27358                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27359
27360                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27361                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27362                 
27363                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27364                 
27365                 break;
27366             case 270 :
27367                 
27368                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27369                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27370                 
27371                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27372                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27373                 
27374                 var targetWidth = this.minWidth - 2 * x;
27375                 var targetHeight = this.minHeight - 2 * y;
27376                 
27377                 var scale = 1;
27378                 
27379                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27380                     scale = targetWidth / width;
27381                 }
27382                 
27383                 if(x > 0 && y == 0){
27384                     scale = targetHeight / height;
27385                 }
27386                 
27387                 if(x > 0 && y > 0){
27388                     scale = targetWidth / width;
27389                     
27390                     if(width < height){
27391                         scale = targetHeight / height;
27392                     }
27393                 }
27394                 
27395                 context.scale(scale, scale);
27396                 
27397                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27398                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27399
27400                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27401                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27402                 
27403                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27404                 
27405                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27406                 
27407                 break;
27408             default : 
27409                 break;
27410         }
27411         
27412         this.cropData = canvas.toDataURL(this.cropType);
27413         
27414         if(this.fireEvent('crop', this, this.cropData) !== false){
27415             this.process(this.file, this.cropData);
27416         }
27417         
27418         return;
27419         
27420     },
27421     
27422     setThumbBoxSize : function()
27423     {
27424         var width, height;
27425         
27426         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27427             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27428             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27429             
27430             this.minWidth = width;
27431             this.minHeight = height;
27432             
27433             if(this.rotate == 90 || this.rotate == 270){
27434                 this.minWidth = height;
27435                 this.minHeight = width;
27436             }
27437         }
27438         
27439         height = 300;
27440         width = Math.ceil(this.minWidth * height / this.minHeight);
27441         
27442         if(this.minWidth > this.minHeight){
27443             width = 300;
27444             height = Math.ceil(this.minHeight * width / this.minWidth);
27445         }
27446         
27447         this.thumbEl.setStyle({
27448             width : width + 'px',
27449             height : height + 'px'
27450         });
27451
27452         return;
27453             
27454     },
27455     
27456     setThumbBoxPosition : function()
27457     {
27458         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27459         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27460         
27461         this.thumbEl.setLeft(x);
27462         this.thumbEl.setTop(y);
27463         
27464     },
27465     
27466     baseRotateLevel : function()
27467     {
27468         this.baseRotate = 1;
27469         
27470         if(
27471                 typeof(this.exif) != 'undefined' &&
27472                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27473                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27474         ){
27475             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27476         }
27477         
27478         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27479         
27480     },
27481     
27482     baseScaleLevel : function()
27483     {
27484         var width, height;
27485         
27486         if(this.isDocument){
27487             
27488             if(this.baseRotate == 6 || this.baseRotate == 8){
27489             
27490                 height = this.thumbEl.getHeight();
27491                 this.baseScale = height / this.imageEl.OriginWidth;
27492
27493                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27494                     width = this.thumbEl.getWidth();
27495                     this.baseScale = width / this.imageEl.OriginHeight;
27496                 }
27497
27498                 return;
27499             }
27500
27501             height = this.thumbEl.getHeight();
27502             this.baseScale = height / this.imageEl.OriginHeight;
27503
27504             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27505                 width = this.thumbEl.getWidth();
27506                 this.baseScale = width / this.imageEl.OriginWidth;
27507             }
27508
27509             return;
27510         }
27511         
27512         if(this.baseRotate == 6 || this.baseRotate == 8){
27513             
27514             width = this.thumbEl.getHeight();
27515             this.baseScale = width / this.imageEl.OriginHeight;
27516             
27517             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27518                 height = this.thumbEl.getWidth();
27519                 this.baseScale = height / this.imageEl.OriginHeight;
27520             }
27521             
27522             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27523                 height = this.thumbEl.getWidth();
27524                 this.baseScale = height / this.imageEl.OriginHeight;
27525                 
27526                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27527                     width = this.thumbEl.getHeight();
27528                     this.baseScale = width / this.imageEl.OriginWidth;
27529                 }
27530             }
27531             
27532             return;
27533         }
27534         
27535         width = this.thumbEl.getWidth();
27536         this.baseScale = width / this.imageEl.OriginWidth;
27537         
27538         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27539             height = this.thumbEl.getHeight();
27540             this.baseScale = height / this.imageEl.OriginHeight;
27541         }
27542         
27543         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27544             
27545             height = this.thumbEl.getHeight();
27546             this.baseScale = height / this.imageEl.OriginHeight;
27547             
27548             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27549                 width = this.thumbEl.getWidth();
27550                 this.baseScale = width / this.imageEl.OriginWidth;
27551             }
27552             
27553         }
27554         
27555         return;
27556     },
27557     
27558     getScaleLevel : function()
27559     {
27560         return this.baseScale * Math.pow(1.1, this.scale);
27561     },
27562     
27563     onTouchStart : function(e)
27564     {
27565         if(!this.canvasLoaded){
27566             this.beforeSelectFile(e);
27567             return;
27568         }
27569         
27570         var touches = e.browserEvent.touches;
27571         
27572         if(!touches){
27573             return;
27574         }
27575         
27576         if(touches.length == 1){
27577             this.onMouseDown(e);
27578             return;
27579         }
27580         
27581         if(touches.length != 2){
27582             return;
27583         }
27584         
27585         var coords = [];
27586         
27587         for(var i = 0, finger; finger = touches[i]; i++){
27588             coords.push(finger.pageX, finger.pageY);
27589         }
27590         
27591         var x = Math.pow(coords[0] - coords[2], 2);
27592         var y = Math.pow(coords[1] - coords[3], 2);
27593         
27594         this.startDistance = Math.sqrt(x + y);
27595         
27596         this.startScale = this.scale;
27597         
27598         this.pinching = true;
27599         this.dragable = false;
27600         
27601     },
27602     
27603     onTouchMove : function(e)
27604     {
27605         if(!this.pinching && !this.dragable){
27606             return;
27607         }
27608         
27609         var touches = e.browserEvent.touches;
27610         
27611         if(!touches){
27612             return;
27613         }
27614         
27615         if(this.dragable){
27616             this.onMouseMove(e);
27617             return;
27618         }
27619         
27620         var coords = [];
27621         
27622         for(var i = 0, finger; finger = touches[i]; i++){
27623             coords.push(finger.pageX, finger.pageY);
27624         }
27625         
27626         var x = Math.pow(coords[0] - coords[2], 2);
27627         var y = Math.pow(coords[1] - coords[3], 2);
27628         
27629         this.endDistance = Math.sqrt(x + y);
27630         
27631         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
27632         
27633         if(!this.zoomable()){
27634             this.scale = this.startScale;
27635             return;
27636         }
27637         
27638         this.draw();
27639         
27640     },
27641     
27642     onTouchEnd : function(e)
27643     {
27644         this.pinching = false;
27645         this.dragable = false;
27646         
27647     },
27648     
27649     process : function(file, crop)
27650     {
27651         if(this.loadMask){
27652             this.maskEl.mask(this.loadingText);
27653         }
27654         
27655         this.xhr = new XMLHttpRequest();
27656         
27657         file.xhr = this.xhr;
27658
27659         this.xhr.open(this.method, this.url, true);
27660         
27661         var headers = {
27662             "Accept": "application/json",
27663             "Cache-Control": "no-cache",
27664             "X-Requested-With": "XMLHttpRequest"
27665         };
27666         
27667         for (var headerName in headers) {
27668             var headerValue = headers[headerName];
27669             if (headerValue) {
27670                 this.xhr.setRequestHeader(headerName, headerValue);
27671             }
27672         }
27673         
27674         var _this = this;
27675         
27676         this.xhr.onload = function()
27677         {
27678             _this.xhrOnLoad(_this.xhr);
27679         }
27680         
27681         this.xhr.onerror = function()
27682         {
27683             _this.xhrOnError(_this.xhr);
27684         }
27685         
27686         var formData = new FormData();
27687
27688         formData.append('returnHTML', 'NO');
27689         
27690         if(crop){
27691             formData.append('crop', crop);
27692         }
27693         
27694         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
27695             formData.append(this.paramName, file, file.name);
27696         }
27697         
27698         if(typeof(file.filename) != 'undefined'){
27699             formData.append('filename', file.filename);
27700         }
27701         
27702         if(typeof(file.mimetype) != 'undefined'){
27703             formData.append('mimetype', file.mimetype);
27704         }
27705         
27706         if(this.fireEvent('arrange', this, formData) != false){
27707             this.xhr.send(formData);
27708         };
27709     },
27710     
27711     xhrOnLoad : function(xhr)
27712     {
27713         if(this.loadMask){
27714             this.maskEl.unmask();
27715         }
27716         
27717         if (xhr.readyState !== 4) {
27718             this.fireEvent('exception', this, xhr);
27719             return;
27720         }
27721
27722         var response = Roo.decode(xhr.responseText);
27723         
27724         if(!response.success){
27725             this.fireEvent('exception', this, xhr);
27726             return;
27727         }
27728         
27729         var response = Roo.decode(xhr.responseText);
27730         
27731         this.fireEvent('upload', this, response);
27732         
27733     },
27734     
27735     xhrOnError : function()
27736     {
27737         if(this.loadMask){
27738             this.maskEl.unmask();
27739         }
27740         
27741         Roo.log('xhr on error');
27742         
27743         var response = Roo.decode(xhr.responseText);
27744           
27745         Roo.log(response);
27746         
27747     },
27748     
27749     prepare : function(file)
27750     {   
27751         if(this.loadMask){
27752             this.maskEl.mask(this.loadingText);
27753         }
27754         
27755         this.file = false;
27756         this.exif = {};
27757         
27758         if(typeof(file) === 'string'){
27759             this.loadCanvas(file);
27760             return;
27761         }
27762         
27763         if(!file || !this.urlAPI){
27764             return;
27765         }
27766         
27767         this.file = file;
27768         this.cropType = file.type;
27769         
27770         var _this = this;
27771         
27772         if(this.fireEvent('prepare', this, this.file) != false){
27773             
27774             var reader = new FileReader();
27775             
27776             reader.onload = function (e) {
27777                 if (e.target.error) {
27778                     Roo.log(e.target.error);
27779                     return;
27780                 }
27781                 
27782                 var buffer = e.target.result,
27783                     dataView = new DataView(buffer),
27784                     offset = 2,
27785                     maxOffset = dataView.byteLength - 4,
27786                     markerBytes,
27787                     markerLength;
27788                 
27789                 if (dataView.getUint16(0) === 0xffd8) {
27790                     while (offset < maxOffset) {
27791                         markerBytes = dataView.getUint16(offset);
27792                         
27793                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
27794                             markerLength = dataView.getUint16(offset + 2) + 2;
27795                             if (offset + markerLength > dataView.byteLength) {
27796                                 Roo.log('Invalid meta data: Invalid segment size.');
27797                                 break;
27798                             }
27799                             
27800                             if(markerBytes == 0xffe1){
27801                                 _this.parseExifData(
27802                                     dataView,
27803                                     offset,
27804                                     markerLength
27805                                 );
27806                             }
27807                             
27808                             offset += markerLength;
27809                             
27810                             continue;
27811                         }
27812                         
27813                         break;
27814                     }
27815                     
27816                 }
27817                 
27818                 var url = _this.urlAPI.createObjectURL(_this.file);
27819                 
27820                 _this.loadCanvas(url);
27821                 
27822                 return;
27823             }
27824             
27825             reader.readAsArrayBuffer(this.file);
27826             
27827         }
27828         
27829     },
27830     
27831     parseExifData : function(dataView, offset, length)
27832     {
27833         var tiffOffset = offset + 10,
27834             littleEndian,
27835             dirOffset;
27836     
27837         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27838             // No Exif data, might be XMP data instead
27839             return;
27840         }
27841         
27842         // Check for the ASCII code for "Exif" (0x45786966):
27843         if (dataView.getUint32(offset + 4) !== 0x45786966) {
27844             // No Exif data, might be XMP data instead
27845             return;
27846         }
27847         if (tiffOffset + 8 > dataView.byteLength) {
27848             Roo.log('Invalid Exif data: Invalid segment size.');
27849             return;
27850         }
27851         // Check for the two null bytes:
27852         if (dataView.getUint16(offset + 8) !== 0x0000) {
27853             Roo.log('Invalid Exif data: Missing byte alignment offset.');
27854             return;
27855         }
27856         // Check the byte alignment:
27857         switch (dataView.getUint16(tiffOffset)) {
27858         case 0x4949:
27859             littleEndian = true;
27860             break;
27861         case 0x4D4D:
27862             littleEndian = false;
27863             break;
27864         default:
27865             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
27866             return;
27867         }
27868         // Check for the TIFF tag marker (0x002A):
27869         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
27870             Roo.log('Invalid Exif data: Missing TIFF marker.');
27871             return;
27872         }
27873         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
27874         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
27875         
27876         this.parseExifTags(
27877             dataView,
27878             tiffOffset,
27879             tiffOffset + dirOffset,
27880             littleEndian
27881         );
27882     },
27883     
27884     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
27885     {
27886         var tagsNumber,
27887             dirEndOffset,
27888             i;
27889         if (dirOffset + 6 > dataView.byteLength) {
27890             Roo.log('Invalid Exif data: Invalid directory offset.');
27891             return;
27892         }
27893         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
27894         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
27895         if (dirEndOffset + 4 > dataView.byteLength) {
27896             Roo.log('Invalid Exif data: Invalid directory size.');
27897             return;
27898         }
27899         for (i = 0; i < tagsNumber; i += 1) {
27900             this.parseExifTag(
27901                 dataView,
27902                 tiffOffset,
27903                 dirOffset + 2 + 12 * i, // tag offset
27904                 littleEndian
27905             );
27906         }
27907         // Return the offset to the next directory:
27908         return dataView.getUint32(dirEndOffset, littleEndian);
27909     },
27910     
27911     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
27912     {
27913         var tag = dataView.getUint16(offset, littleEndian);
27914         
27915         this.exif[tag] = this.getExifValue(
27916             dataView,
27917             tiffOffset,
27918             offset,
27919             dataView.getUint16(offset + 2, littleEndian), // tag type
27920             dataView.getUint32(offset + 4, littleEndian), // tag length
27921             littleEndian
27922         );
27923     },
27924     
27925     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
27926     {
27927         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
27928             tagSize,
27929             dataOffset,
27930             values,
27931             i,
27932             str,
27933             c;
27934     
27935         if (!tagType) {
27936             Roo.log('Invalid Exif data: Invalid tag type.');
27937             return;
27938         }
27939         
27940         tagSize = tagType.size * length;
27941         // Determine if the value is contained in the dataOffset bytes,
27942         // or if the value at the dataOffset is a pointer to the actual data:
27943         dataOffset = tagSize > 4 ?
27944                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
27945         if (dataOffset + tagSize > dataView.byteLength) {
27946             Roo.log('Invalid Exif data: Invalid data offset.');
27947             return;
27948         }
27949         if (length === 1) {
27950             return tagType.getValue(dataView, dataOffset, littleEndian);
27951         }
27952         values = [];
27953         for (i = 0; i < length; i += 1) {
27954             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
27955         }
27956         
27957         if (tagType.ascii) {
27958             str = '';
27959             // Concatenate the chars:
27960             for (i = 0; i < values.length; i += 1) {
27961                 c = values[i];
27962                 // Ignore the terminating NULL byte(s):
27963                 if (c === '\u0000') {
27964                     break;
27965                 }
27966                 str += c;
27967             }
27968             return str;
27969         }
27970         return values;
27971     }
27972     
27973 });
27974
27975 Roo.apply(Roo.bootstrap.UploadCropbox, {
27976     tags : {
27977         'Orientation': 0x0112
27978     },
27979     
27980     Orientation: {
27981             1: 0, //'top-left',
27982 //            2: 'top-right',
27983             3: 180, //'bottom-right',
27984 //            4: 'bottom-left',
27985 //            5: 'left-top',
27986             6: 90, //'right-top',
27987 //            7: 'right-bottom',
27988             8: 270 //'left-bottom'
27989     },
27990     
27991     exifTagTypes : {
27992         // byte, 8-bit unsigned int:
27993         1: {
27994             getValue: function (dataView, dataOffset) {
27995                 return dataView.getUint8(dataOffset);
27996             },
27997             size: 1
27998         },
27999         // ascii, 8-bit byte:
28000         2: {
28001             getValue: function (dataView, dataOffset) {
28002                 return String.fromCharCode(dataView.getUint8(dataOffset));
28003             },
28004             size: 1,
28005             ascii: true
28006         },
28007         // short, 16 bit int:
28008         3: {
28009             getValue: function (dataView, dataOffset, littleEndian) {
28010                 return dataView.getUint16(dataOffset, littleEndian);
28011             },
28012             size: 2
28013         },
28014         // long, 32 bit int:
28015         4: {
28016             getValue: function (dataView, dataOffset, littleEndian) {
28017                 return dataView.getUint32(dataOffset, littleEndian);
28018             },
28019             size: 4
28020         },
28021         // rational = two long values, first is numerator, second is denominator:
28022         5: {
28023             getValue: function (dataView, dataOffset, littleEndian) {
28024                 return dataView.getUint32(dataOffset, littleEndian) /
28025                     dataView.getUint32(dataOffset + 4, littleEndian);
28026             },
28027             size: 8
28028         },
28029         // slong, 32 bit signed int:
28030         9: {
28031             getValue: function (dataView, dataOffset, littleEndian) {
28032                 return dataView.getInt32(dataOffset, littleEndian);
28033             },
28034             size: 4
28035         },
28036         // srational, two slongs, first is numerator, second is denominator:
28037         10: {
28038             getValue: function (dataView, dataOffset, littleEndian) {
28039                 return dataView.getInt32(dataOffset, littleEndian) /
28040                     dataView.getInt32(dataOffset + 4, littleEndian);
28041             },
28042             size: 8
28043         }
28044     },
28045     
28046     footer : {
28047         STANDARD : [
28048             {
28049                 tag : 'div',
28050                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28051                 action : 'rotate-left',
28052                 cn : [
28053                     {
28054                         tag : 'button',
28055                         cls : 'btn btn-default',
28056                         html : '<i class="fa fa-undo"></i>'
28057                     }
28058                 ]
28059             },
28060             {
28061                 tag : 'div',
28062                 cls : 'btn-group roo-upload-cropbox-picture',
28063                 action : 'picture',
28064                 cn : [
28065                     {
28066                         tag : 'button',
28067                         cls : 'btn btn-default',
28068                         html : '<i class="fa fa-picture-o"></i>'
28069                     }
28070                 ]
28071             },
28072             {
28073                 tag : 'div',
28074                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28075                 action : 'rotate-right',
28076                 cn : [
28077                     {
28078                         tag : 'button',
28079                         cls : 'btn btn-default',
28080                         html : '<i class="fa fa-repeat"></i>'
28081                     }
28082                 ]
28083             }
28084         ],
28085         DOCUMENT : [
28086             {
28087                 tag : 'div',
28088                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28089                 action : 'rotate-left',
28090                 cn : [
28091                     {
28092                         tag : 'button',
28093                         cls : 'btn btn-default',
28094                         html : '<i class="fa fa-undo"></i>'
28095                     }
28096                 ]
28097             },
28098             {
28099                 tag : 'div',
28100                 cls : 'btn-group roo-upload-cropbox-download',
28101                 action : 'download',
28102                 cn : [
28103                     {
28104                         tag : 'button',
28105                         cls : 'btn btn-default',
28106                         html : '<i class="fa fa-download"></i>'
28107                     }
28108                 ]
28109             },
28110             {
28111                 tag : 'div',
28112                 cls : 'btn-group roo-upload-cropbox-crop',
28113                 action : 'crop',
28114                 cn : [
28115                     {
28116                         tag : 'button',
28117                         cls : 'btn btn-default',
28118                         html : '<i class="fa fa-crop"></i>'
28119                     }
28120                 ]
28121             },
28122             {
28123                 tag : 'div',
28124                 cls : 'btn-group roo-upload-cropbox-trash',
28125                 action : 'trash',
28126                 cn : [
28127                     {
28128                         tag : 'button',
28129                         cls : 'btn btn-default',
28130                         html : '<i class="fa fa-trash"></i>'
28131                     }
28132                 ]
28133             },
28134             {
28135                 tag : 'div',
28136                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28137                 action : 'rotate-right',
28138                 cn : [
28139                     {
28140                         tag : 'button',
28141                         cls : 'btn btn-default',
28142                         html : '<i class="fa fa-repeat"></i>'
28143                     }
28144                 ]
28145             }
28146         ],
28147         ROTATOR : [
28148             {
28149                 tag : 'div',
28150                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28151                 action : 'rotate-left',
28152                 cn : [
28153                     {
28154                         tag : 'button',
28155                         cls : 'btn btn-default',
28156                         html : '<i class="fa fa-undo"></i>'
28157                     }
28158                 ]
28159             },
28160             {
28161                 tag : 'div',
28162                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28163                 action : 'rotate-right',
28164                 cn : [
28165                     {
28166                         tag : 'button',
28167                         cls : 'btn btn-default',
28168                         html : '<i class="fa fa-repeat"></i>'
28169                     }
28170                 ]
28171             }
28172         ]
28173     }
28174 });
28175
28176 /*
28177 * Licence: LGPL
28178 */
28179
28180 /**
28181  * @class Roo.bootstrap.DocumentManager
28182  * @extends Roo.bootstrap.Component
28183  * Bootstrap DocumentManager class
28184  * @cfg {String} paramName default 'imageUpload'
28185  * @cfg {String} toolTipName default 'filename'
28186  * @cfg {String} method default POST
28187  * @cfg {String} url action url
28188  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28189  * @cfg {Boolean} multiple multiple upload default true
28190  * @cfg {Number} thumbSize default 300
28191  * @cfg {String} fieldLabel
28192  * @cfg {Number} labelWidth default 4
28193  * @cfg {String} labelAlign (left|top) default left
28194  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28195 * @cfg {Number} labellg set the width of label (1-12)
28196  * @cfg {Number} labelmd set the width of label (1-12)
28197  * @cfg {Number} labelsm set the width of label (1-12)
28198  * @cfg {Number} labelxs set the width of label (1-12)
28199  * 
28200  * @constructor
28201  * Create a new DocumentManager
28202  * @param {Object} config The config object
28203  */
28204
28205 Roo.bootstrap.DocumentManager = function(config){
28206     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28207     
28208     this.files = [];
28209     this.delegates = [];
28210     
28211     this.addEvents({
28212         /**
28213          * @event initial
28214          * Fire when initial the DocumentManager
28215          * @param {Roo.bootstrap.DocumentManager} this
28216          */
28217         "initial" : true,
28218         /**
28219          * @event inspect
28220          * inspect selected file
28221          * @param {Roo.bootstrap.DocumentManager} this
28222          * @param {File} file
28223          */
28224         "inspect" : true,
28225         /**
28226          * @event exception
28227          * Fire when xhr load exception
28228          * @param {Roo.bootstrap.DocumentManager} this
28229          * @param {XMLHttpRequest} xhr
28230          */
28231         "exception" : true,
28232         /**
28233          * @event afterupload
28234          * Fire when xhr load exception
28235          * @param {Roo.bootstrap.DocumentManager} this
28236          * @param {XMLHttpRequest} xhr
28237          */
28238         "afterupload" : true,
28239         /**
28240          * @event prepare
28241          * prepare the form data
28242          * @param {Roo.bootstrap.DocumentManager} this
28243          * @param {Object} formData
28244          */
28245         "prepare" : true,
28246         /**
28247          * @event remove
28248          * Fire when remove the file
28249          * @param {Roo.bootstrap.DocumentManager} this
28250          * @param {Object} file
28251          */
28252         "remove" : true,
28253         /**
28254          * @event refresh
28255          * Fire after refresh the file
28256          * @param {Roo.bootstrap.DocumentManager} this
28257          */
28258         "refresh" : true,
28259         /**
28260          * @event click
28261          * Fire after click the image
28262          * @param {Roo.bootstrap.DocumentManager} this
28263          * @param {Object} file
28264          */
28265         "click" : true,
28266         /**
28267          * @event edit
28268          * Fire when upload a image and editable set to true
28269          * @param {Roo.bootstrap.DocumentManager} this
28270          * @param {Object} file
28271          */
28272         "edit" : true,
28273         /**
28274          * @event beforeselectfile
28275          * Fire before select file
28276          * @param {Roo.bootstrap.DocumentManager} this
28277          */
28278         "beforeselectfile" : true,
28279         /**
28280          * @event process
28281          * Fire before process file
28282          * @param {Roo.bootstrap.DocumentManager} this
28283          * @param {Object} file
28284          */
28285         "process" : true
28286         
28287     });
28288 };
28289
28290 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28291     
28292     boxes : 0,
28293     inputName : '',
28294     thumbSize : 300,
28295     multiple : true,
28296     files : false,
28297     method : 'POST',
28298     url : '',
28299     paramName : 'imageUpload',
28300     toolTipName : 'filename',
28301     fieldLabel : '',
28302     labelWidth : 4,
28303     labelAlign : 'left',
28304     editable : true,
28305     delegates : false,
28306     xhr : false, 
28307     
28308     labellg : 0,
28309     labelmd : 0,
28310     labelsm : 0,
28311     labelxs : 0,
28312     
28313     getAutoCreate : function()
28314     {   
28315         var managerWidget = {
28316             tag : 'div',
28317             cls : 'roo-document-manager',
28318             cn : [
28319                 {
28320                     tag : 'input',
28321                     cls : 'roo-document-manager-selector',
28322                     type : 'file'
28323                 },
28324                 {
28325                     tag : 'div',
28326                     cls : 'roo-document-manager-uploader',
28327                     cn : [
28328                         {
28329                             tag : 'div',
28330                             cls : 'roo-document-manager-upload-btn',
28331                             html : '<i class="fa fa-plus"></i>'
28332                         }
28333                     ]
28334                     
28335                 }
28336             ]
28337         };
28338         
28339         var content = [
28340             {
28341                 tag : 'div',
28342                 cls : 'column col-md-12',
28343                 cn : managerWidget
28344             }
28345         ];
28346         
28347         if(this.fieldLabel.length){
28348             
28349             content = [
28350                 {
28351                     tag : 'div',
28352                     cls : 'column col-md-12',
28353                     html : this.fieldLabel
28354                 },
28355                 {
28356                     tag : 'div',
28357                     cls : 'column col-md-12',
28358                     cn : managerWidget
28359                 }
28360             ];
28361
28362             if(this.labelAlign == 'left'){
28363                 content = [
28364                     {
28365                         tag : 'div',
28366                         cls : 'column',
28367                         html : this.fieldLabel
28368                     },
28369                     {
28370                         tag : 'div',
28371                         cls : 'column',
28372                         cn : managerWidget
28373                     }
28374                 ];
28375                 
28376                 if(this.labelWidth > 12){
28377                     content[0].style = "width: " + this.labelWidth + 'px';
28378                 }
28379
28380                 if(this.labelWidth < 13 && this.labelmd == 0){
28381                     this.labelmd = this.labelWidth;
28382                 }
28383
28384                 if(this.labellg > 0){
28385                     content[0].cls += ' col-lg-' + this.labellg;
28386                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28387                 }
28388
28389                 if(this.labelmd > 0){
28390                     content[0].cls += ' col-md-' + this.labelmd;
28391                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28392                 }
28393
28394                 if(this.labelsm > 0){
28395                     content[0].cls += ' col-sm-' + this.labelsm;
28396                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28397                 }
28398
28399                 if(this.labelxs > 0){
28400                     content[0].cls += ' col-xs-' + this.labelxs;
28401                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28402                 }
28403                 
28404             }
28405         }
28406         
28407         var cfg = {
28408             tag : 'div',
28409             cls : 'row clearfix',
28410             cn : content
28411         };
28412         
28413         return cfg;
28414         
28415     },
28416     
28417     initEvents : function()
28418     {
28419         this.managerEl = this.el.select('.roo-document-manager', true).first();
28420         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28421         
28422         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28423         this.selectorEl.hide();
28424         
28425         if(this.multiple){
28426             this.selectorEl.attr('multiple', 'multiple');
28427         }
28428         
28429         this.selectorEl.on('change', this.onFileSelected, this);
28430         
28431         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28432         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28433         
28434         this.uploader.on('click', this.onUploaderClick, this);
28435         
28436         this.renderProgressDialog();
28437         
28438         var _this = this;
28439         
28440         window.addEventListener("resize", function() { _this.refresh(); } );
28441         
28442         this.fireEvent('initial', this);
28443     },
28444     
28445     renderProgressDialog : function()
28446     {
28447         var _this = this;
28448         
28449         this.progressDialog = new Roo.bootstrap.Modal({
28450             cls : 'roo-document-manager-progress-dialog',
28451             allow_close : false,
28452             title : '',
28453             buttons : [
28454                 {
28455                     name  :'cancel',
28456                     weight : 'danger',
28457                     html : 'Cancel'
28458                 }
28459             ], 
28460             listeners : { 
28461                 btnclick : function() {
28462                     _this.uploadCancel();
28463                     this.hide();
28464                 }
28465             }
28466         });
28467          
28468         this.progressDialog.render(Roo.get(document.body));
28469          
28470         this.progress = new Roo.bootstrap.Progress({
28471             cls : 'roo-document-manager-progress',
28472             active : true,
28473             striped : true
28474         });
28475         
28476         this.progress.render(this.progressDialog.getChildContainer());
28477         
28478         this.progressBar = new Roo.bootstrap.ProgressBar({
28479             cls : 'roo-document-manager-progress-bar',
28480             aria_valuenow : 0,
28481             aria_valuemin : 0,
28482             aria_valuemax : 12,
28483             panel : 'success'
28484         });
28485         
28486         this.progressBar.render(this.progress.getChildContainer());
28487     },
28488     
28489     onUploaderClick : function(e)
28490     {
28491         e.preventDefault();
28492      
28493         if(this.fireEvent('beforeselectfile', this) != false){
28494             this.selectorEl.dom.click();
28495         }
28496         
28497     },
28498     
28499     onFileSelected : function(e)
28500     {
28501         e.preventDefault();
28502         
28503         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28504             return;
28505         }
28506         
28507         Roo.each(this.selectorEl.dom.files, function(file){
28508             if(this.fireEvent('inspect', this, file) != false){
28509                 this.files.push(file);
28510             }
28511         }, this);
28512         
28513         this.queue();
28514         
28515     },
28516     
28517     queue : function()
28518     {
28519         this.selectorEl.dom.value = '';
28520         
28521         if(!this.files.length){
28522             return;
28523         }
28524         
28525         if(this.boxes > 0 && this.files.length > this.boxes){
28526             this.files = this.files.slice(0, this.boxes);
28527         }
28528         
28529         this.uploader.show();
28530         
28531         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28532             this.uploader.hide();
28533         }
28534         
28535         var _this = this;
28536         
28537         var files = [];
28538         
28539         var docs = [];
28540         
28541         Roo.each(this.files, function(file){
28542             
28543             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28544                 var f = this.renderPreview(file);
28545                 files.push(f);
28546                 return;
28547             }
28548             
28549             if(file.type.indexOf('image') != -1){
28550                 this.delegates.push(
28551                     (function(){
28552                         _this.process(file);
28553                     }).createDelegate(this)
28554                 );
28555         
28556                 return;
28557             }
28558             
28559             docs.push(
28560                 (function(){
28561                     _this.process(file);
28562                 }).createDelegate(this)
28563             );
28564             
28565         }, this);
28566         
28567         this.files = files;
28568         
28569         this.delegates = this.delegates.concat(docs);
28570         
28571         if(!this.delegates.length){
28572             this.refresh();
28573             return;
28574         }
28575         
28576         this.progressBar.aria_valuemax = this.delegates.length;
28577         
28578         this.arrange();
28579         
28580         return;
28581     },
28582     
28583     arrange : function()
28584     {
28585         if(!this.delegates.length){
28586             this.progressDialog.hide();
28587             this.refresh();
28588             return;
28589         }
28590         
28591         var delegate = this.delegates.shift();
28592         
28593         this.progressDialog.show();
28594         
28595         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
28596         
28597         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
28598         
28599         delegate();
28600     },
28601     
28602     refresh : function()
28603     {
28604         this.uploader.show();
28605         
28606         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28607             this.uploader.hide();
28608         }
28609         
28610         Roo.isTouch ? this.closable(false) : this.closable(true);
28611         
28612         this.fireEvent('refresh', this);
28613     },
28614     
28615     onRemove : function(e, el, o)
28616     {
28617         e.preventDefault();
28618         
28619         this.fireEvent('remove', this, o);
28620         
28621     },
28622     
28623     remove : function(o)
28624     {
28625         var files = [];
28626         
28627         Roo.each(this.files, function(file){
28628             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
28629                 files.push(file);
28630                 return;
28631             }
28632
28633             o.target.remove();
28634
28635         }, this);
28636         
28637         this.files = files;
28638         
28639         this.refresh();
28640     },
28641     
28642     clear : function()
28643     {
28644         Roo.each(this.files, function(file){
28645             if(!file.target){
28646                 return;
28647             }
28648             
28649             file.target.remove();
28650
28651         }, this);
28652         
28653         this.files = [];
28654         
28655         this.refresh();
28656     },
28657     
28658     onClick : function(e, el, o)
28659     {
28660         e.preventDefault();
28661         
28662         this.fireEvent('click', this, o);
28663         
28664     },
28665     
28666     closable : function(closable)
28667     {
28668         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
28669             
28670             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28671             
28672             if(closable){
28673                 el.show();
28674                 return;
28675             }
28676             
28677             el.hide();
28678             
28679         }, this);
28680     },
28681     
28682     xhrOnLoad : function(xhr)
28683     {
28684         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28685             el.remove();
28686         }, this);
28687         
28688         if (xhr.readyState !== 4) {
28689             this.arrange();
28690             this.fireEvent('exception', this, xhr);
28691             return;
28692         }
28693
28694         var response = Roo.decode(xhr.responseText);
28695         
28696         if(!response.success){
28697             this.arrange();
28698             this.fireEvent('exception', this, xhr);
28699             return;
28700         }
28701         
28702         var file = this.renderPreview(response.data);
28703         
28704         this.files.push(file);
28705         
28706         this.arrange();
28707         
28708         this.fireEvent('afterupload', this, xhr);
28709         
28710     },
28711     
28712     xhrOnError : function(xhr)
28713     {
28714         Roo.log('xhr on error');
28715         
28716         var response = Roo.decode(xhr.responseText);
28717           
28718         Roo.log(response);
28719         
28720         this.arrange();
28721     },
28722     
28723     process : function(file)
28724     {
28725         if(this.fireEvent('process', this, file) !== false){
28726             if(this.editable && file.type.indexOf('image') != -1){
28727                 this.fireEvent('edit', this, file);
28728                 return;
28729             }
28730
28731             this.uploadStart(file, false);
28732
28733             return;
28734         }
28735         
28736     },
28737     
28738     uploadStart : function(file, crop)
28739     {
28740         this.xhr = new XMLHttpRequest();
28741         
28742         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28743             this.arrange();
28744             return;
28745         }
28746         
28747         file.xhr = this.xhr;
28748             
28749         this.managerEl.createChild({
28750             tag : 'div',
28751             cls : 'roo-document-manager-loading',
28752             cn : [
28753                 {
28754                     tag : 'div',
28755                     tooltip : file.name,
28756                     cls : 'roo-document-manager-thumb',
28757                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28758                 }
28759             ]
28760
28761         });
28762
28763         this.xhr.open(this.method, this.url, true);
28764         
28765         var headers = {
28766             "Accept": "application/json",
28767             "Cache-Control": "no-cache",
28768             "X-Requested-With": "XMLHttpRequest"
28769         };
28770         
28771         for (var headerName in headers) {
28772             var headerValue = headers[headerName];
28773             if (headerValue) {
28774                 this.xhr.setRequestHeader(headerName, headerValue);
28775             }
28776         }
28777         
28778         var _this = this;
28779         
28780         this.xhr.onload = function()
28781         {
28782             _this.xhrOnLoad(_this.xhr);
28783         }
28784         
28785         this.xhr.onerror = function()
28786         {
28787             _this.xhrOnError(_this.xhr);
28788         }
28789         
28790         var formData = new FormData();
28791
28792         formData.append('returnHTML', 'NO');
28793         
28794         if(crop){
28795             formData.append('crop', crop);
28796         }
28797         
28798         formData.append(this.paramName, file, file.name);
28799         
28800         var options = {
28801             file : file, 
28802             manually : false
28803         };
28804         
28805         if(this.fireEvent('prepare', this, formData, options) != false){
28806             
28807             if(options.manually){
28808                 return;
28809             }
28810             
28811             this.xhr.send(formData);
28812             return;
28813         };
28814         
28815         this.uploadCancel();
28816     },
28817     
28818     uploadCancel : function()
28819     {
28820         if (this.xhr) {
28821             this.xhr.abort();
28822         }
28823         
28824         this.delegates = [];
28825         
28826         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28827             el.remove();
28828         }, this);
28829         
28830         this.arrange();
28831     },
28832     
28833     renderPreview : function(file)
28834     {
28835         if(typeof(file.target) != 'undefined' && file.target){
28836             return file;
28837         }
28838         
28839         var previewEl = this.managerEl.createChild({
28840             tag : 'div',
28841             cls : 'roo-document-manager-preview',
28842             cn : [
28843                 {
28844                     tag : 'div',
28845                     tooltip : file[this.toolTipName],
28846                     cls : 'roo-document-manager-thumb',
28847                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename + '">'
28848                 },
28849                 {
28850                     tag : 'button',
28851                     cls : 'close',
28852                     html : '<i class="fa fa-times-circle"></i>'
28853                 }
28854             ]
28855         });
28856
28857         var close = previewEl.select('button.close', true).first();
28858
28859         close.on('click', this.onRemove, this, file);
28860
28861         file.target = previewEl;
28862
28863         var image = previewEl.select('img', true).first();
28864         
28865         var _this = this;
28866         
28867         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
28868         
28869         image.on('click', this.onClick, this, file);
28870         
28871         return file;
28872         
28873     },
28874     
28875     onPreviewLoad : function(file, image)
28876     {
28877         if(typeof(file.target) == 'undefined' || !file.target){
28878             return;
28879         }
28880         
28881         var width = image.dom.naturalWidth || image.dom.width;
28882         var height = image.dom.naturalHeight || image.dom.height;
28883         
28884         if(width > height){
28885             file.target.addClass('wide');
28886             return;
28887         }
28888         
28889         file.target.addClass('tall');
28890         return;
28891         
28892     },
28893     
28894     uploadFromSource : function(file, crop)
28895     {
28896         this.xhr = new XMLHttpRequest();
28897         
28898         this.managerEl.createChild({
28899             tag : 'div',
28900             cls : 'roo-document-manager-loading',
28901             cn : [
28902                 {
28903                     tag : 'div',
28904                     tooltip : file.name,
28905                     cls : 'roo-document-manager-thumb',
28906                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28907                 }
28908             ]
28909
28910         });
28911
28912         this.xhr.open(this.method, this.url, true);
28913         
28914         var headers = {
28915             "Accept": "application/json",
28916             "Cache-Control": "no-cache",
28917             "X-Requested-With": "XMLHttpRequest"
28918         };
28919         
28920         for (var headerName in headers) {
28921             var headerValue = headers[headerName];
28922             if (headerValue) {
28923                 this.xhr.setRequestHeader(headerName, headerValue);
28924             }
28925         }
28926         
28927         var _this = this;
28928         
28929         this.xhr.onload = function()
28930         {
28931             _this.xhrOnLoad(_this.xhr);
28932         }
28933         
28934         this.xhr.onerror = function()
28935         {
28936             _this.xhrOnError(_this.xhr);
28937         }
28938         
28939         var formData = new FormData();
28940
28941         formData.append('returnHTML', 'NO');
28942         
28943         formData.append('crop', crop);
28944         
28945         if(typeof(file.filename) != 'undefined'){
28946             formData.append('filename', file.filename);
28947         }
28948         
28949         if(typeof(file.mimetype) != 'undefined'){
28950             formData.append('mimetype', file.mimetype);
28951         }
28952         
28953         Roo.log(formData);
28954         
28955         if(this.fireEvent('prepare', this, formData) != false){
28956             this.xhr.send(formData);
28957         };
28958     }
28959 });
28960
28961 /*
28962 * Licence: LGPL
28963 */
28964
28965 /**
28966  * @class Roo.bootstrap.DocumentViewer
28967  * @extends Roo.bootstrap.Component
28968  * Bootstrap DocumentViewer class
28969  * @cfg {Boolean} showDownload (true|false) show download button (default true)
28970  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
28971  * 
28972  * @constructor
28973  * Create a new DocumentViewer
28974  * @param {Object} config The config object
28975  */
28976
28977 Roo.bootstrap.DocumentViewer = function(config){
28978     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
28979     
28980     this.addEvents({
28981         /**
28982          * @event initial
28983          * Fire after initEvent
28984          * @param {Roo.bootstrap.DocumentViewer} this
28985          */
28986         "initial" : true,
28987         /**
28988          * @event click
28989          * Fire after click
28990          * @param {Roo.bootstrap.DocumentViewer} this
28991          */
28992         "click" : true,
28993         /**
28994          * @event download
28995          * Fire after download button
28996          * @param {Roo.bootstrap.DocumentViewer} this
28997          */
28998         "download" : true,
28999         /**
29000          * @event trash
29001          * Fire after trash button
29002          * @param {Roo.bootstrap.DocumentViewer} this
29003          */
29004         "trash" : true
29005         
29006     });
29007 };
29008
29009 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29010     
29011     showDownload : true,
29012     
29013     showTrash : true,
29014     
29015     getAutoCreate : function()
29016     {
29017         var cfg = {
29018             tag : 'div',
29019             cls : 'roo-document-viewer',
29020             cn : [
29021                 {
29022                     tag : 'div',
29023                     cls : 'roo-document-viewer-body',
29024                     cn : [
29025                         {
29026                             tag : 'div',
29027                             cls : 'roo-document-viewer-thumb',
29028                             cn : [
29029                                 {
29030                                     tag : 'img',
29031                                     cls : 'roo-document-viewer-image'
29032                                 }
29033                             ]
29034                         }
29035                     ]
29036                 },
29037                 {
29038                     tag : 'div',
29039                     cls : 'roo-document-viewer-footer',
29040                     cn : {
29041                         tag : 'div',
29042                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29043                         cn : [
29044                             {
29045                                 tag : 'div',
29046                                 cls : 'btn-group roo-document-viewer-download',
29047                                 cn : [
29048                                     {
29049                                         tag : 'button',
29050                                         cls : 'btn btn-default',
29051                                         html : '<i class="fa fa-download"></i>'
29052                                     }
29053                                 ]
29054                             },
29055                             {
29056                                 tag : 'div',
29057                                 cls : 'btn-group roo-document-viewer-trash',
29058                                 cn : [
29059                                     {
29060                                         tag : 'button',
29061                                         cls : 'btn btn-default',
29062                                         html : '<i class="fa fa-trash"></i>'
29063                                     }
29064                                 ]
29065                             }
29066                         ]
29067                     }
29068                 }
29069             ]
29070         };
29071         
29072         return cfg;
29073     },
29074     
29075     initEvents : function()
29076     {
29077         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29078         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29079         
29080         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29081         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29082         
29083         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29084         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29085         
29086         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29087         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29088         
29089         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29090         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29091         
29092         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29093         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29094         
29095         this.bodyEl.on('click', this.onClick, this);
29096         this.downloadBtn.on('click', this.onDownload, this);
29097         this.trashBtn.on('click', this.onTrash, this);
29098         
29099         this.downloadBtn.hide();
29100         this.trashBtn.hide();
29101         
29102         if(this.showDownload){
29103             this.downloadBtn.show();
29104         }
29105         
29106         if(this.showTrash){
29107             this.trashBtn.show();
29108         }
29109         
29110         if(!this.showDownload && !this.showTrash) {
29111             this.footerEl.hide();
29112         }
29113         
29114     },
29115     
29116     initial : function()
29117     {
29118         this.fireEvent('initial', this);
29119         
29120     },
29121     
29122     onClick : function(e)
29123     {
29124         e.preventDefault();
29125         
29126         this.fireEvent('click', this);
29127     },
29128     
29129     onDownload : function(e)
29130     {
29131         e.preventDefault();
29132         
29133         this.fireEvent('download', this);
29134     },
29135     
29136     onTrash : function(e)
29137     {
29138         e.preventDefault();
29139         
29140         this.fireEvent('trash', this);
29141     }
29142     
29143 });
29144 /*
29145  * - LGPL
29146  *
29147  * nav progress bar
29148  * 
29149  */
29150
29151 /**
29152  * @class Roo.bootstrap.NavProgressBar
29153  * @extends Roo.bootstrap.Component
29154  * Bootstrap NavProgressBar class
29155  * 
29156  * @constructor
29157  * Create a new nav progress bar
29158  * @param {Object} config The config object
29159  */
29160
29161 Roo.bootstrap.NavProgressBar = function(config){
29162     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29163
29164     this.bullets = this.bullets || [];
29165    
29166 //    Roo.bootstrap.NavProgressBar.register(this);
29167      this.addEvents({
29168         /**
29169              * @event changed
29170              * Fires when the active item changes
29171              * @param {Roo.bootstrap.NavProgressBar} this
29172              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29173              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29174          */
29175         'changed': true
29176      });
29177     
29178 };
29179
29180 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29181     
29182     bullets : [],
29183     barItems : [],
29184     
29185     getAutoCreate : function()
29186     {
29187         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29188         
29189         cfg = {
29190             tag : 'div',
29191             cls : 'roo-navigation-bar-group',
29192             cn : [
29193                 {
29194                     tag : 'div',
29195                     cls : 'roo-navigation-top-bar'
29196                 },
29197                 {
29198                     tag : 'div',
29199                     cls : 'roo-navigation-bullets-bar',
29200                     cn : [
29201                         {
29202                             tag : 'ul',
29203                             cls : 'roo-navigation-bar'
29204                         }
29205                     ]
29206                 },
29207                 
29208                 {
29209                     tag : 'div',
29210                     cls : 'roo-navigation-bottom-bar'
29211                 }
29212             ]
29213             
29214         };
29215         
29216         return cfg;
29217         
29218     },
29219     
29220     initEvents: function() 
29221     {
29222         
29223     },
29224     
29225     onRender : function(ct, position) 
29226     {
29227         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29228         
29229         if(this.bullets.length){
29230             Roo.each(this.bullets, function(b){
29231                this.addItem(b);
29232             }, this);
29233         }
29234         
29235         this.format();
29236         
29237     },
29238     
29239     addItem : function(cfg)
29240     {
29241         var item = new Roo.bootstrap.NavProgressItem(cfg);
29242         
29243         item.parentId = this.id;
29244         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29245         
29246         if(cfg.html){
29247             var top = new Roo.bootstrap.Element({
29248                 tag : 'div',
29249                 cls : 'roo-navigation-bar-text'
29250             });
29251             
29252             var bottom = new Roo.bootstrap.Element({
29253                 tag : 'div',
29254                 cls : 'roo-navigation-bar-text'
29255             });
29256             
29257             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29258             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29259             
29260             var topText = new Roo.bootstrap.Element({
29261                 tag : 'span',
29262                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29263             });
29264             
29265             var bottomText = new Roo.bootstrap.Element({
29266                 tag : 'span',
29267                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29268             });
29269             
29270             topText.onRender(top.el, null);
29271             bottomText.onRender(bottom.el, null);
29272             
29273             item.topEl = top;
29274             item.bottomEl = bottom;
29275         }
29276         
29277         this.barItems.push(item);
29278         
29279         return item;
29280     },
29281     
29282     getActive : function()
29283     {
29284         var active = false;
29285         
29286         Roo.each(this.barItems, function(v){
29287             
29288             if (!v.isActive()) {
29289                 return;
29290             }
29291             
29292             active = v;
29293             return false;
29294             
29295         });
29296         
29297         return active;
29298     },
29299     
29300     setActiveItem : function(item)
29301     {
29302         var prev = false;
29303         
29304         Roo.each(this.barItems, function(v){
29305             if (v.rid == item.rid) {
29306                 return ;
29307             }
29308             
29309             if (v.isActive()) {
29310                 v.setActive(false);
29311                 prev = v;
29312             }
29313         });
29314
29315         item.setActive(true);
29316         
29317         this.fireEvent('changed', this, item, prev);
29318     },
29319     
29320     getBarItem: function(rid)
29321     {
29322         var ret = false;
29323         
29324         Roo.each(this.barItems, function(e) {
29325             if (e.rid != rid) {
29326                 return;
29327             }
29328             
29329             ret =  e;
29330             return false;
29331         });
29332         
29333         return ret;
29334     },
29335     
29336     indexOfItem : function(item)
29337     {
29338         var index = false;
29339         
29340         Roo.each(this.barItems, function(v, i){
29341             
29342             if (v.rid != item.rid) {
29343                 return;
29344             }
29345             
29346             index = i;
29347             return false
29348         });
29349         
29350         return index;
29351     },
29352     
29353     setActiveNext : function()
29354     {
29355         var i = this.indexOfItem(this.getActive());
29356         
29357         if (i > this.barItems.length) {
29358             return;
29359         }
29360         
29361         this.setActiveItem(this.barItems[i+1]);
29362     },
29363     
29364     setActivePrev : function()
29365     {
29366         var i = this.indexOfItem(this.getActive());
29367         
29368         if (i  < 1) {
29369             return;
29370         }
29371         
29372         this.setActiveItem(this.barItems[i-1]);
29373     },
29374     
29375     format : function()
29376     {
29377         if(!this.barItems.length){
29378             return;
29379         }
29380      
29381         var width = 100 / this.barItems.length;
29382         
29383         Roo.each(this.barItems, function(i){
29384             i.el.setStyle('width', width + '%');
29385             i.topEl.el.setStyle('width', width + '%');
29386             i.bottomEl.el.setStyle('width', width + '%');
29387         }, this);
29388         
29389     }
29390     
29391 });
29392 /*
29393  * - LGPL
29394  *
29395  * Nav Progress Item
29396  * 
29397  */
29398
29399 /**
29400  * @class Roo.bootstrap.NavProgressItem
29401  * @extends Roo.bootstrap.Component
29402  * Bootstrap NavProgressItem class
29403  * @cfg {String} rid the reference id
29404  * @cfg {Boolean} active (true|false) Is item active default false
29405  * @cfg {Boolean} disabled (true|false) Is item active default false
29406  * @cfg {String} html
29407  * @cfg {String} position (top|bottom) text position default bottom
29408  * @cfg {String} icon show icon instead of number
29409  * 
29410  * @constructor
29411  * Create a new NavProgressItem
29412  * @param {Object} config The config object
29413  */
29414 Roo.bootstrap.NavProgressItem = function(config){
29415     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29416     this.addEvents({
29417         // raw events
29418         /**
29419          * @event click
29420          * The raw click event for the entire grid.
29421          * @param {Roo.bootstrap.NavProgressItem} this
29422          * @param {Roo.EventObject} e
29423          */
29424         "click" : true
29425     });
29426    
29427 };
29428
29429 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29430     
29431     rid : '',
29432     active : false,
29433     disabled : false,
29434     html : '',
29435     position : 'bottom',
29436     icon : false,
29437     
29438     getAutoCreate : function()
29439     {
29440         var iconCls = 'roo-navigation-bar-item-icon';
29441         
29442         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29443         
29444         var cfg = {
29445             tag: 'li',
29446             cls: 'roo-navigation-bar-item',
29447             cn : [
29448                 {
29449                     tag : 'i',
29450                     cls : iconCls
29451                 }
29452             ]
29453         };
29454         
29455         if(this.active){
29456             cfg.cls += ' active';
29457         }
29458         if(this.disabled){
29459             cfg.cls += ' disabled';
29460         }
29461         
29462         return cfg;
29463     },
29464     
29465     disable : function()
29466     {
29467         this.setDisabled(true);
29468     },
29469     
29470     enable : function()
29471     {
29472         this.setDisabled(false);
29473     },
29474     
29475     initEvents: function() 
29476     {
29477         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29478         
29479         this.iconEl.on('click', this.onClick, this);
29480     },
29481     
29482     onClick : function(e)
29483     {
29484         e.preventDefault();
29485         
29486         if(this.disabled){
29487             return;
29488         }
29489         
29490         if(this.fireEvent('click', this, e) === false){
29491             return;
29492         };
29493         
29494         this.parent().setActiveItem(this);
29495     },
29496     
29497     isActive: function () 
29498     {
29499         return this.active;
29500     },
29501     
29502     setActive : function(state)
29503     {
29504         if(this.active == state){
29505             return;
29506         }
29507         
29508         this.active = state;
29509         
29510         if (state) {
29511             this.el.addClass('active');
29512             return;
29513         }
29514         
29515         this.el.removeClass('active');
29516         
29517         return;
29518     },
29519     
29520     setDisabled : function(state)
29521     {
29522         if(this.disabled == state){
29523             return;
29524         }
29525         
29526         this.disabled = state;
29527         
29528         if (state) {
29529             this.el.addClass('disabled');
29530             return;
29531         }
29532         
29533         this.el.removeClass('disabled');
29534     },
29535     
29536     tooltipEl : function()
29537     {
29538         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29539     }
29540 });
29541  
29542
29543  /*
29544  * - LGPL
29545  *
29546  * FieldLabel
29547  * 
29548  */
29549
29550 /**
29551  * @class Roo.bootstrap.FieldLabel
29552  * @extends Roo.bootstrap.Component
29553  * Bootstrap FieldLabel class
29554  * @cfg {String} html contents of the element
29555  * @cfg {String} tag tag of the element default label
29556  * @cfg {String} cls class of the element
29557  * @cfg {String} target label target 
29558  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29559  * @cfg {String} invalidClass default "text-danger fa fa-lg fa-exclamation-triangle"
29560  * @cfg {String} validClass default "text-success fa fa-lg fa-check"
29561  * @cfg {String} iconTooltip default "This field is required"
29562  * 
29563  * @constructor
29564  * Create a new FieldLabel
29565  * @param {Object} config The config object
29566  */
29567
29568 Roo.bootstrap.FieldLabel = function(config){
29569     Roo.bootstrap.Element.superclass.constructor.call(this, config);
29570     
29571     this.addEvents({
29572             /**
29573              * @event invalid
29574              * Fires after the field has been marked as invalid.
29575              * @param {Roo.form.FieldLabel} this
29576              * @param {String} msg The validation message
29577              */
29578             invalid : true,
29579             /**
29580              * @event valid
29581              * Fires after the field has been validated with no errors.
29582              * @param {Roo.form.FieldLabel} this
29583              */
29584             valid : true
29585         });
29586 };
29587
29588 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
29589     
29590     tag: 'label',
29591     cls: '',
29592     html: '',
29593     target: '',
29594     allowBlank : true,
29595     invalidClass : 'text-danger fa fa-lg fa-exclamation-triangle',
29596     validClass : 'text-success fa fa-lg fa-check',
29597     iconTooltip : 'This field is required',
29598     
29599     getAutoCreate : function(){
29600         
29601         var cfg = {
29602             tag : this.tag,
29603             cls : 'roo-bootstrap-field-label ' + this.cls,
29604             for : this.target,
29605             cn : [
29606                 {
29607                     tag : 'i',
29608                     cls : '',
29609                     tooltip : this.iconTooltip
29610                 },
29611                 {
29612                     tag : 'span',
29613                     html : this.html
29614                 }
29615             ] 
29616         };
29617         
29618         return cfg;
29619     },
29620     
29621     initEvents: function() 
29622     {
29623         Roo.bootstrap.Element.superclass.initEvents.call(this);
29624         
29625         this.iconEl = this.el.select('i', true).first();
29626         
29627         this.iconEl.setVisibilityMode(Roo.Element.DISPLAY).hide();
29628         
29629         Roo.bootstrap.FieldLabel.register(this);
29630     },
29631     
29632     /**
29633      * Mark this field as valid
29634      */
29635     markValid : function()
29636     {
29637         this.iconEl.show();
29638         
29639         this.iconEl.removeClass(this.invalidClass);
29640         
29641         this.iconEl.addClass(this.validClass);
29642         
29643         this.fireEvent('valid', this);
29644     },
29645     
29646     /**
29647      * Mark this field as invalid
29648      * @param {String} msg The validation message
29649      */
29650     markInvalid : function(msg)
29651     {
29652         this.iconEl.show();
29653         
29654         this.iconEl.removeClass(this.validClass);
29655         
29656         this.iconEl.addClass(this.invalidClass);
29657         
29658         this.fireEvent('invalid', this, msg);
29659     }
29660     
29661    
29662 });
29663
29664 Roo.apply(Roo.bootstrap.FieldLabel, {
29665     
29666     groups: {},
29667     
29668      /**
29669     * register a FieldLabel Group
29670     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
29671     */
29672     register : function(label)
29673     {
29674         if(this.groups.hasOwnProperty(label.target)){
29675             return;
29676         }
29677      
29678         this.groups[label.target] = label;
29679         
29680     },
29681     /**
29682     * fetch a FieldLabel Group based on the target
29683     * @param {string} target
29684     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
29685     */
29686     get: function(target) {
29687         if (typeof(this.groups[target]) == 'undefined') {
29688             return false;
29689         }
29690         
29691         return this.groups[target] ;
29692     }
29693 });
29694
29695  
29696
29697  /*
29698  * - LGPL
29699  *
29700  * page DateSplitField.
29701  * 
29702  */
29703
29704
29705 /**
29706  * @class Roo.bootstrap.DateSplitField
29707  * @extends Roo.bootstrap.Component
29708  * Bootstrap DateSplitField class
29709  * @cfg {string} fieldLabel - the label associated
29710  * @cfg {Number} labelWidth set the width of label (0-12)
29711  * @cfg {String} labelAlign (top|left)
29712  * @cfg {Boolean} dayAllowBlank (true|false) default false
29713  * @cfg {Boolean} monthAllowBlank (true|false) default false
29714  * @cfg {Boolean} yearAllowBlank (true|false) default false
29715  * @cfg {string} dayPlaceholder 
29716  * @cfg {string} monthPlaceholder
29717  * @cfg {string} yearPlaceholder
29718  * @cfg {string} dayFormat default 'd'
29719  * @cfg {string} monthFormat default 'm'
29720  * @cfg {string} yearFormat default 'Y'
29721  * @cfg {Number} labellg set the width of label (1-12)
29722  * @cfg {Number} labelmd set the width of label (1-12)
29723  * @cfg {Number} labelsm set the width of label (1-12)
29724  * @cfg {Number} labelxs set the width of label (1-12)
29725
29726  *     
29727  * @constructor
29728  * Create a new DateSplitField
29729  * @param {Object} config The config object
29730  */
29731
29732 Roo.bootstrap.DateSplitField = function(config){
29733     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
29734     
29735     this.addEvents({
29736         // raw events
29737          /**
29738          * @event years
29739          * getting the data of years
29740          * @param {Roo.bootstrap.DateSplitField} this
29741          * @param {Object} years
29742          */
29743         "years" : true,
29744         /**
29745          * @event days
29746          * getting the data of days
29747          * @param {Roo.bootstrap.DateSplitField} this
29748          * @param {Object} days
29749          */
29750         "days" : true,
29751         /**
29752          * @event invalid
29753          * Fires after the field has been marked as invalid.
29754          * @param {Roo.form.Field} this
29755          * @param {String} msg The validation message
29756          */
29757         invalid : true,
29758        /**
29759          * @event valid
29760          * Fires after the field has been validated with no errors.
29761          * @param {Roo.form.Field} this
29762          */
29763         valid : true
29764     });
29765 };
29766
29767 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
29768     
29769     fieldLabel : '',
29770     labelAlign : 'top',
29771     labelWidth : 3,
29772     dayAllowBlank : false,
29773     monthAllowBlank : false,
29774     yearAllowBlank : false,
29775     dayPlaceholder : '',
29776     monthPlaceholder : '',
29777     yearPlaceholder : '',
29778     dayFormat : 'd',
29779     monthFormat : 'm',
29780     yearFormat : 'Y',
29781     isFormField : true,
29782     labellg : 0,
29783     labelmd : 0,
29784     labelsm : 0,
29785     labelxs : 0,
29786     
29787     getAutoCreate : function()
29788     {
29789         var cfg = {
29790             tag : 'div',
29791             cls : 'row roo-date-split-field-group',
29792             cn : [
29793                 {
29794                     tag : 'input',
29795                     type : 'hidden',
29796                     cls : 'form-hidden-field roo-date-split-field-group-value',
29797                     name : this.name
29798                 }
29799             ]
29800         };
29801         
29802         var labelCls = 'col-md-12';
29803         var contentCls = 'col-md-4';
29804         
29805         if(this.fieldLabel){
29806             
29807             var label = {
29808                 tag : 'div',
29809                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
29810                 cn : [
29811                     {
29812                         tag : 'label',
29813                         html : this.fieldLabel
29814                     }
29815                 ]
29816             };
29817             
29818             if(this.labelAlign == 'left'){
29819             
29820                 if(this.labelWidth > 12){
29821                     label.style = "width: " + this.labelWidth + 'px';
29822                 }
29823
29824                 if(this.labelWidth < 13 && this.labelmd == 0){
29825                     this.labelmd = this.labelWidth;
29826                 }
29827
29828                 if(this.labellg > 0){
29829                     labelCls = ' col-lg-' + this.labellg;
29830                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
29831                 }
29832
29833                 if(this.labelmd > 0){
29834                     labelCls = ' col-md-' + this.labelmd;
29835                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
29836                 }
29837
29838                 if(this.labelsm > 0){
29839                     labelCls = ' col-sm-' + this.labelsm;
29840                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
29841                 }
29842
29843                 if(this.labelxs > 0){
29844                     labelCls = ' col-xs-' + this.labelxs;
29845                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
29846                 }
29847             }
29848             
29849             label.cls += ' ' + labelCls;
29850             
29851             cfg.cn.push(label);
29852         }
29853         
29854         Roo.each(['day', 'month', 'year'], function(t){
29855             cfg.cn.push({
29856                 tag : 'div',
29857                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
29858             });
29859         }, this);
29860         
29861         return cfg;
29862     },
29863     
29864     inputEl: function ()
29865     {
29866         return this.el.select('.roo-date-split-field-group-value', true).first();
29867     },
29868     
29869     onRender : function(ct, position) 
29870     {
29871         var _this = this;
29872         
29873         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29874         
29875         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
29876         
29877         this.dayField = new Roo.bootstrap.ComboBox({
29878             allowBlank : this.dayAllowBlank,
29879             alwaysQuery : true,
29880             displayField : 'value',
29881             editable : false,
29882             fieldLabel : '',
29883             forceSelection : true,
29884             mode : 'local',
29885             placeholder : this.dayPlaceholder,
29886             selectOnFocus : true,
29887             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29888             triggerAction : 'all',
29889             typeAhead : true,
29890             valueField : 'value',
29891             store : new Roo.data.SimpleStore({
29892                 data : (function() {    
29893                     var days = [];
29894                     _this.fireEvent('days', _this, days);
29895                     return days;
29896                 })(),
29897                 fields : [ 'value' ]
29898             }),
29899             listeners : {
29900                 select : function (_self, record, index)
29901                 {
29902                     _this.setValue(_this.getValue());
29903                 }
29904             }
29905         });
29906
29907         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
29908         
29909         this.monthField = new Roo.bootstrap.MonthField({
29910             after : '<i class=\"fa fa-calendar\"></i>',
29911             allowBlank : this.monthAllowBlank,
29912             placeholder : this.monthPlaceholder,
29913             readOnly : true,
29914             listeners : {
29915                 render : function (_self)
29916                 {
29917                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
29918                         e.preventDefault();
29919                         _self.focus();
29920                     });
29921                 },
29922                 select : function (_self, oldvalue, newvalue)
29923                 {
29924                     _this.setValue(_this.getValue());
29925                 }
29926             }
29927         });
29928         
29929         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
29930         
29931         this.yearField = new Roo.bootstrap.ComboBox({
29932             allowBlank : this.yearAllowBlank,
29933             alwaysQuery : true,
29934             displayField : 'value',
29935             editable : false,
29936             fieldLabel : '',
29937             forceSelection : true,
29938             mode : 'local',
29939             placeholder : this.yearPlaceholder,
29940             selectOnFocus : true,
29941             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
29942             triggerAction : 'all',
29943             typeAhead : true,
29944             valueField : 'value',
29945             store : new Roo.data.SimpleStore({
29946                 data : (function() {
29947                     var years = [];
29948                     _this.fireEvent('years', _this, years);
29949                     return years;
29950                 })(),
29951                 fields : [ 'value' ]
29952             }),
29953             listeners : {
29954                 select : function (_self, record, index)
29955                 {
29956                     _this.setValue(_this.getValue());
29957                 }
29958             }
29959         });
29960
29961         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
29962     },
29963     
29964     setValue : function(v, format)
29965     {
29966         this.inputEl.dom.value = v;
29967         
29968         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
29969         
29970         var d = Date.parseDate(v, f);
29971         
29972         if(!d){
29973             this.validate();
29974             return;
29975         }
29976         
29977         this.setDay(d.format(this.dayFormat));
29978         this.setMonth(d.format(this.monthFormat));
29979         this.setYear(d.format(this.yearFormat));
29980         
29981         this.validate();
29982         
29983         return;
29984     },
29985     
29986     setDay : function(v)
29987     {
29988         this.dayField.setValue(v);
29989         this.inputEl.dom.value = this.getValue();
29990         this.validate();
29991         return;
29992     },
29993     
29994     setMonth : function(v)
29995     {
29996         this.monthField.setValue(v, true);
29997         this.inputEl.dom.value = this.getValue();
29998         this.validate();
29999         return;
30000     },
30001     
30002     setYear : function(v)
30003     {
30004         this.yearField.setValue(v);
30005         this.inputEl.dom.value = this.getValue();
30006         this.validate();
30007         return;
30008     },
30009     
30010     getDay : function()
30011     {
30012         return this.dayField.getValue();
30013     },
30014     
30015     getMonth : function()
30016     {
30017         return this.monthField.getValue();
30018     },
30019     
30020     getYear : function()
30021     {
30022         return this.yearField.getValue();
30023     },
30024     
30025     getValue : function()
30026     {
30027         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30028         
30029         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30030         
30031         return date;
30032     },
30033     
30034     reset : function()
30035     {
30036         this.setDay('');
30037         this.setMonth('');
30038         this.setYear('');
30039         this.inputEl.dom.value = '';
30040         this.validate();
30041         return;
30042     },
30043     
30044     validate : function()
30045     {
30046         var d = this.dayField.validate();
30047         var m = this.monthField.validate();
30048         var y = this.yearField.validate();
30049         
30050         var valid = true;
30051         
30052         if(
30053                 (!this.dayAllowBlank && !d) ||
30054                 (!this.monthAllowBlank && !m) ||
30055                 (!this.yearAllowBlank && !y)
30056         ){
30057             valid = false;
30058         }
30059         
30060         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30061             return valid;
30062         }
30063         
30064         if(valid){
30065             this.markValid();
30066             return valid;
30067         }
30068         
30069         this.markInvalid();
30070         
30071         return valid;
30072     },
30073     
30074     markValid : function()
30075     {
30076         
30077         var label = this.el.select('label', true).first();
30078         var icon = this.el.select('i.fa-star', true).first();
30079
30080         if(label && icon){
30081             icon.remove();
30082         }
30083         
30084         this.fireEvent('valid', this);
30085     },
30086     
30087      /**
30088      * Mark this field as invalid
30089      * @param {String} msg The validation message
30090      */
30091     markInvalid : function(msg)
30092     {
30093         
30094         var label = this.el.select('label', true).first();
30095         var icon = this.el.select('i.fa-star', true).first();
30096
30097         if(label && !icon){
30098             this.el.select('.roo-date-split-field-label', true).createChild({
30099                 tag : 'i',
30100                 cls : 'text-danger fa fa-lg fa-star',
30101                 tooltip : 'This field is required',
30102                 style : 'margin-right:5px;'
30103             }, label, true);
30104         }
30105         
30106         this.fireEvent('invalid', this, msg);
30107     },
30108     
30109     clearInvalid : function()
30110     {
30111         var label = this.el.select('label', true).first();
30112         var icon = this.el.select('i.fa-star', true).first();
30113
30114         if(label && icon){
30115             icon.remove();
30116         }
30117         
30118         this.fireEvent('valid', this);
30119     },
30120     
30121     getName: function()
30122     {
30123         return this.name;
30124     }
30125     
30126 });
30127
30128  /**
30129  *
30130  * This is based on 
30131  * http://masonry.desandro.com
30132  *
30133  * The idea is to render all the bricks based on vertical width...
30134  *
30135  * The original code extends 'outlayer' - we might need to use that....
30136  * 
30137  */
30138
30139
30140 /**
30141  * @class Roo.bootstrap.LayoutMasonry
30142  * @extends Roo.bootstrap.Component
30143  * Bootstrap Layout Masonry class
30144  * 
30145  * @constructor
30146  * Create a new Element
30147  * @param {Object} config The config object
30148  */
30149
30150 Roo.bootstrap.LayoutMasonry = function(config){
30151     
30152     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30153     
30154     this.bricks = [];
30155     
30156     Roo.bootstrap.LayoutMasonry.register(this);
30157     
30158     this.addEvents({
30159         // raw events
30160         /**
30161          * @event layout
30162          * Fire after layout the items
30163          * @param {Roo.bootstrap.LayoutMasonry} this
30164          * @param {Roo.EventObject} e
30165          */
30166         "layout" : true
30167     });
30168     
30169 };
30170
30171 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30172     
30173     /**
30174      * @cfg {Boolean} isLayoutInstant = no animation?
30175      */   
30176     isLayoutInstant : false, // needed?
30177    
30178     /**
30179      * @cfg {Number} boxWidth  width of the columns
30180      */   
30181     boxWidth : 450,
30182     
30183       /**
30184      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30185      */   
30186     boxHeight : 0,
30187     
30188     /**
30189      * @cfg {Number} padWidth padding below box..
30190      */   
30191     padWidth : 10, 
30192     
30193     /**
30194      * @cfg {Number} gutter gutter width..
30195      */   
30196     gutter : 10,
30197     
30198      /**
30199      * @cfg {Number} maxCols maximum number of columns
30200      */   
30201     
30202     maxCols: 0,
30203     
30204     /**
30205      * @cfg {Boolean} isAutoInitial defalut true
30206      */   
30207     isAutoInitial : true, 
30208     
30209     containerWidth: 0,
30210     
30211     /**
30212      * @cfg {Boolean} isHorizontal defalut false
30213      */   
30214     isHorizontal : false, 
30215
30216     currentSize : null,
30217     
30218     tag: 'div',
30219     
30220     cls: '',
30221     
30222     bricks: null, //CompositeElement
30223     
30224     cols : 1,
30225     
30226     _isLayoutInited : false,
30227     
30228 //    isAlternative : false, // only use for vertical layout...
30229     
30230     /**
30231      * @cfg {Number} alternativePadWidth padding below box..
30232      */   
30233     alternativePadWidth : 50,
30234     
30235     selectedBrick : [],
30236     
30237     getAutoCreate : function(){
30238         
30239         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30240         
30241         var cfg = {
30242             tag: this.tag,
30243             cls: 'blog-masonary-wrapper ' + this.cls,
30244             cn : {
30245                 cls : 'mas-boxes masonary'
30246             }
30247         };
30248         
30249         return cfg;
30250     },
30251     
30252     getChildContainer: function( )
30253     {
30254         if (this.boxesEl) {
30255             return this.boxesEl;
30256         }
30257         
30258         this.boxesEl = this.el.select('.mas-boxes').first();
30259         
30260         return this.boxesEl;
30261     },
30262     
30263     
30264     initEvents : function()
30265     {
30266         var _this = this;
30267         
30268         if(this.isAutoInitial){
30269             Roo.log('hook children rendered');
30270             this.on('childrenrendered', function() {
30271                 Roo.log('children rendered');
30272                 _this.initial();
30273             } ,this);
30274         }
30275     },
30276     
30277     initial : function()
30278     {
30279         this.selectedBrick = [];
30280         
30281         this.currentSize = this.el.getBox(true);
30282         
30283         Roo.EventManager.onWindowResize(this.resize, this); 
30284
30285         if(!this.isAutoInitial){
30286             this.layout();
30287             return;
30288         }
30289         
30290         this.layout();
30291         
30292         return;
30293         //this.layout.defer(500,this);
30294         
30295     },
30296     
30297     resize : function()
30298     {
30299         var cs = this.el.getBox(true);
30300         
30301         if (
30302                 this.currentSize.width == cs.width && 
30303                 this.currentSize.x == cs.x && 
30304                 this.currentSize.height == cs.height && 
30305                 this.currentSize.y == cs.y 
30306         ) {
30307             Roo.log("no change in with or X or Y");
30308             return;
30309         }
30310         
30311         this.currentSize = cs;
30312         
30313         this.layout();
30314         
30315     },
30316     
30317     layout : function()
30318     {   
30319         this._resetLayout();
30320         
30321         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30322         
30323         this.layoutItems( isInstant );
30324       
30325         this._isLayoutInited = true;
30326         
30327         this.fireEvent('layout', this);
30328         
30329     },
30330     
30331     _resetLayout : function()
30332     {
30333         if(this.isHorizontal){
30334             this.horizontalMeasureColumns();
30335             return;
30336         }
30337         
30338         this.verticalMeasureColumns();
30339         
30340     },
30341     
30342     verticalMeasureColumns : function()
30343     {
30344         this.getContainerWidth();
30345         
30346 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30347 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30348 //            return;
30349 //        }
30350         
30351         var boxWidth = this.boxWidth + this.padWidth;
30352         
30353         if(this.containerWidth < this.boxWidth){
30354             boxWidth = this.containerWidth
30355         }
30356         
30357         var containerWidth = this.containerWidth;
30358         
30359         var cols = Math.floor(containerWidth / boxWidth);
30360         
30361         this.cols = Math.max( cols, 1 );
30362         
30363         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30364         
30365         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30366         
30367         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30368         
30369         this.colWidth = boxWidth + avail - this.padWidth;
30370         
30371         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30372         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30373     },
30374     
30375     horizontalMeasureColumns : function()
30376     {
30377         this.getContainerWidth();
30378         
30379         var boxWidth = this.boxWidth;
30380         
30381         if(this.containerWidth < boxWidth){
30382             boxWidth = this.containerWidth;
30383         }
30384         
30385         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30386         
30387         this.el.setHeight(boxWidth);
30388         
30389     },
30390     
30391     getContainerWidth : function()
30392     {
30393         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30394     },
30395     
30396     layoutItems : function( isInstant )
30397     {
30398         Roo.log(this.bricks);
30399         
30400         var items = Roo.apply([], this.bricks);
30401         
30402         if(this.isHorizontal){
30403             this._horizontalLayoutItems( items , isInstant );
30404             return;
30405         }
30406         
30407 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30408 //            this._verticalAlternativeLayoutItems( items , isInstant );
30409 //            return;
30410 //        }
30411         
30412         this._verticalLayoutItems( items , isInstant );
30413         
30414     },
30415     
30416     _verticalLayoutItems : function ( items , isInstant)
30417     {
30418         if ( !items || !items.length ) {
30419             return;
30420         }
30421         
30422         var standard = [
30423             ['xs', 'xs', 'xs', 'tall'],
30424             ['xs', 'xs', 'tall'],
30425             ['xs', 'xs', 'sm'],
30426             ['xs', 'xs', 'xs'],
30427             ['xs', 'tall'],
30428             ['xs', 'sm'],
30429             ['xs', 'xs'],
30430             ['xs'],
30431             
30432             ['sm', 'xs', 'xs'],
30433             ['sm', 'xs'],
30434             ['sm'],
30435             
30436             ['tall', 'xs', 'xs', 'xs'],
30437             ['tall', 'xs', 'xs'],
30438             ['tall', 'xs'],
30439             ['tall']
30440             
30441         ];
30442         
30443         var queue = [];
30444         
30445         var boxes = [];
30446         
30447         var box = [];
30448         
30449         Roo.each(items, function(item, k){
30450             
30451             switch (item.size) {
30452                 // these layouts take up a full box,
30453                 case 'md' :
30454                 case 'md-left' :
30455                 case 'md-right' :
30456                 case 'wide' :
30457                     
30458                     if(box.length){
30459                         boxes.push(box);
30460                         box = [];
30461                     }
30462                     
30463                     boxes.push([item]);
30464                     
30465                     break;
30466                     
30467                 case 'xs' :
30468                 case 'sm' :
30469                 case 'tall' :
30470                     
30471                     box.push(item);
30472                     
30473                     break;
30474                 default :
30475                     break;
30476                     
30477             }
30478             
30479         }, this);
30480         
30481         if(box.length){
30482             boxes.push(box);
30483             box = [];
30484         }
30485         
30486         var filterPattern = function(box, length)
30487         {
30488             if(!box.length){
30489                 return;
30490             }
30491             
30492             var match = false;
30493             
30494             var pattern = box.slice(0, length);
30495             
30496             var format = [];
30497             
30498             Roo.each(pattern, function(i){
30499                 format.push(i.size);
30500             }, this);
30501             
30502             Roo.each(standard, function(s){
30503                 
30504                 if(String(s) != String(format)){
30505                     return;
30506                 }
30507                 
30508                 match = true;
30509                 return false;
30510                 
30511             }, this);
30512             
30513             if(!match && length == 1){
30514                 return;
30515             }
30516             
30517             if(!match){
30518                 filterPattern(box, length - 1);
30519                 return;
30520             }
30521                 
30522             queue.push(pattern);
30523
30524             box = box.slice(length, box.length);
30525
30526             filterPattern(box, 4);
30527
30528             return;
30529             
30530         }
30531         
30532         Roo.each(boxes, function(box, k){
30533             
30534             if(!box.length){
30535                 return;
30536             }
30537             
30538             if(box.length == 1){
30539                 queue.push(box);
30540                 return;
30541             }
30542             
30543             filterPattern(box, 4);
30544             
30545         }, this);
30546         
30547         this._processVerticalLayoutQueue( queue, isInstant );
30548         
30549     },
30550     
30551 //    _verticalAlternativeLayoutItems : function( items , isInstant )
30552 //    {
30553 //        if ( !items || !items.length ) {
30554 //            return;
30555 //        }
30556 //
30557 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
30558 //        
30559 //    },
30560     
30561     _horizontalLayoutItems : function ( items , isInstant)
30562     {
30563         if ( !items || !items.length || items.length < 3) {
30564             return;
30565         }
30566         
30567         items.reverse();
30568         
30569         var eItems = items.slice(0, 3);
30570         
30571         items = items.slice(3, items.length);
30572         
30573         var standard = [
30574             ['xs', 'xs', 'xs', 'wide'],
30575             ['xs', 'xs', 'wide'],
30576             ['xs', 'xs', 'sm'],
30577             ['xs', 'xs', 'xs'],
30578             ['xs', 'wide'],
30579             ['xs', 'sm'],
30580             ['xs', 'xs'],
30581             ['xs'],
30582             
30583             ['sm', 'xs', 'xs'],
30584             ['sm', 'xs'],
30585             ['sm'],
30586             
30587             ['wide', 'xs', 'xs', 'xs'],
30588             ['wide', 'xs', 'xs'],
30589             ['wide', 'xs'],
30590             ['wide'],
30591             
30592             ['wide-thin']
30593         ];
30594         
30595         var queue = [];
30596         
30597         var boxes = [];
30598         
30599         var box = [];
30600         
30601         Roo.each(items, function(item, k){
30602             
30603             switch (item.size) {
30604                 case 'md' :
30605                 case 'md-left' :
30606                 case 'md-right' :
30607                 case 'tall' :
30608                     
30609                     if(box.length){
30610                         boxes.push(box);
30611                         box = [];
30612                     }
30613                     
30614                     boxes.push([item]);
30615                     
30616                     break;
30617                     
30618                 case 'xs' :
30619                 case 'sm' :
30620                 case 'wide' :
30621                 case 'wide-thin' :
30622                     
30623                     box.push(item);
30624                     
30625                     break;
30626                 default :
30627                     break;
30628                     
30629             }
30630             
30631         }, this);
30632         
30633         if(box.length){
30634             boxes.push(box);
30635             box = [];
30636         }
30637         
30638         var filterPattern = function(box, length)
30639         {
30640             if(!box.length){
30641                 return;
30642             }
30643             
30644             var match = false;
30645             
30646             var pattern = box.slice(0, length);
30647             
30648             var format = [];
30649             
30650             Roo.each(pattern, function(i){
30651                 format.push(i.size);
30652             }, this);
30653             
30654             Roo.each(standard, function(s){
30655                 
30656                 if(String(s) != String(format)){
30657                     return;
30658                 }
30659                 
30660                 match = true;
30661                 return false;
30662                 
30663             }, this);
30664             
30665             if(!match && length == 1){
30666                 return;
30667             }
30668             
30669             if(!match){
30670                 filterPattern(box, length - 1);
30671                 return;
30672             }
30673                 
30674             queue.push(pattern);
30675
30676             box = box.slice(length, box.length);
30677
30678             filterPattern(box, 4);
30679
30680             return;
30681             
30682         }
30683         
30684         Roo.each(boxes, function(box, k){
30685             
30686             if(!box.length){
30687                 return;
30688             }
30689             
30690             if(box.length == 1){
30691                 queue.push(box);
30692                 return;
30693             }
30694             
30695             filterPattern(box, 4);
30696             
30697         }, this);
30698         
30699         
30700         var prune = [];
30701         
30702         var pos = this.el.getBox(true);
30703         
30704         var minX = pos.x;
30705         
30706         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30707         
30708         var hit_end = false;
30709         
30710         Roo.each(queue, function(box){
30711             
30712             if(hit_end){
30713                 
30714                 Roo.each(box, function(b){
30715                 
30716                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30717                     b.el.hide();
30718
30719                 }, this);
30720
30721                 return;
30722             }
30723             
30724             var mx = 0;
30725             
30726             Roo.each(box, function(b){
30727                 
30728                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
30729                 b.el.show();
30730
30731                 mx = Math.max(mx, b.x);
30732                 
30733             }, this);
30734             
30735             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
30736             
30737             if(maxX < minX){
30738                 
30739                 Roo.each(box, function(b){
30740                 
30741                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30742                     b.el.hide();
30743                     
30744                 }, this);
30745                 
30746                 hit_end = true;
30747                 
30748                 return;
30749             }
30750             
30751             prune.push(box);
30752             
30753         }, this);
30754         
30755         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
30756     },
30757     
30758     /** Sets position of item in DOM
30759     * @param {Element} item
30760     * @param {Number} x - horizontal position
30761     * @param {Number} y - vertical position
30762     * @param {Boolean} isInstant - disables transitions
30763     */
30764     _processVerticalLayoutQueue : function( queue, isInstant )
30765     {
30766         var pos = this.el.getBox(true);
30767         var x = pos.x;
30768         var y = pos.y;
30769         var maxY = [];
30770         
30771         for (var i = 0; i < this.cols; i++){
30772             maxY[i] = pos.y;
30773         }
30774         
30775         Roo.each(queue, function(box, k){
30776             
30777             var col = k % this.cols;
30778             
30779             Roo.each(box, function(b,kk){
30780                 
30781                 b.el.position('absolute');
30782                 
30783                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30784                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30785                 
30786                 if(b.size == 'md-left' || b.size == 'md-right'){
30787                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30788                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30789                 }
30790                 
30791                 b.el.setWidth(width);
30792                 b.el.setHeight(height);
30793                 // iframe?
30794                 b.el.select('iframe',true).setSize(width,height);
30795                 
30796             }, this);
30797             
30798             for (var i = 0; i < this.cols; i++){
30799                 
30800                 if(maxY[i] < maxY[col]){
30801                     col = i;
30802                     continue;
30803                 }
30804                 
30805                 col = Math.min(col, i);
30806                 
30807             }
30808             
30809             x = pos.x + col * (this.colWidth + this.padWidth);
30810             
30811             y = maxY[col];
30812             
30813             var positions = [];
30814             
30815             switch (box.length){
30816                 case 1 :
30817                     positions = this.getVerticalOneBoxColPositions(x, y, box);
30818                     break;
30819                 case 2 :
30820                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
30821                     break;
30822                 case 3 :
30823                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
30824                     break;
30825                 case 4 :
30826                     positions = this.getVerticalFourBoxColPositions(x, y, box);
30827                     break;
30828                 default :
30829                     break;
30830             }
30831             
30832             Roo.each(box, function(b,kk){
30833                 
30834                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30835                 
30836                 var sz = b.el.getSize();
30837                 
30838                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
30839                 
30840             }, this);
30841             
30842         }, this);
30843         
30844         var mY = 0;
30845         
30846         for (var i = 0; i < this.cols; i++){
30847             mY = Math.max(mY, maxY[i]);
30848         }
30849         
30850         this.el.setHeight(mY - pos.y);
30851         
30852     },
30853     
30854 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
30855 //    {
30856 //        var pos = this.el.getBox(true);
30857 //        var x = pos.x;
30858 //        var y = pos.y;
30859 //        var maxX = pos.right;
30860 //        
30861 //        var maxHeight = 0;
30862 //        
30863 //        Roo.each(items, function(item, k){
30864 //            
30865 //            var c = k % 2;
30866 //            
30867 //            item.el.position('absolute');
30868 //                
30869 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
30870 //
30871 //            item.el.setWidth(width);
30872 //
30873 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
30874 //
30875 //            item.el.setHeight(height);
30876 //            
30877 //            if(c == 0){
30878 //                item.el.setXY([x, y], isInstant ? false : true);
30879 //            } else {
30880 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
30881 //            }
30882 //            
30883 //            y = y + height + this.alternativePadWidth;
30884 //            
30885 //            maxHeight = maxHeight + height + this.alternativePadWidth;
30886 //            
30887 //        }, this);
30888 //        
30889 //        this.el.setHeight(maxHeight);
30890 //        
30891 //    },
30892     
30893     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
30894     {
30895         var pos = this.el.getBox(true);
30896         
30897         var minX = pos.x;
30898         var minY = pos.y;
30899         
30900         var maxX = pos.right;
30901         
30902         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
30903         
30904         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30905         
30906         Roo.each(queue, function(box, k){
30907             
30908             Roo.each(box, function(b, kk){
30909                 
30910                 b.el.position('absolute');
30911                 
30912                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30913                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30914                 
30915                 if(b.size == 'md-left' || b.size == 'md-right'){
30916                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
30917                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
30918                 }
30919                 
30920                 b.el.setWidth(width);
30921                 b.el.setHeight(height);
30922                 
30923             }, this);
30924             
30925             if(!box.length){
30926                 return;
30927             }
30928             
30929             var positions = [];
30930             
30931             switch (box.length){
30932                 case 1 :
30933                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
30934                     break;
30935                 case 2 :
30936                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
30937                     break;
30938                 case 3 :
30939                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
30940                     break;
30941                 case 4 :
30942                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
30943                     break;
30944                 default :
30945                     break;
30946             }
30947             
30948             Roo.each(box, function(b,kk){
30949                 
30950                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
30951                 
30952                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
30953                 
30954             }, this);
30955             
30956         }, this);
30957         
30958     },
30959     
30960     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
30961     {
30962         Roo.each(eItems, function(b,k){
30963             
30964             b.size = (k == 0) ? 'sm' : 'xs';
30965             b.x = (k == 0) ? 2 : 1;
30966             b.y = (k == 0) ? 2 : 1;
30967             
30968             b.el.position('absolute');
30969             
30970             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
30971                 
30972             b.el.setWidth(width);
30973             
30974             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
30975             
30976             b.el.setHeight(height);
30977             
30978         }, this);
30979
30980         var positions = [];
30981         
30982         positions.push({
30983             x : maxX - this.unitWidth * 2 - this.gutter,
30984             y : minY
30985         });
30986         
30987         positions.push({
30988             x : maxX - this.unitWidth,
30989             y : minY + (this.unitWidth + this.gutter) * 2
30990         });
30991         
30992         positions.push({
30993             x : maxX - this.unitWidth * 3 - this.gutter * 2,
30994             y : minY
30995         });
30996         
30997         Roo.each(eItems, function(b,k){
30998             
30999             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31000
31001         }, this);
31002         
31003     },
31004     
31005     getVerticalOneBoxColPositions : function(x, y, box)
31006     {
31007         var pos = [];
31008         
31009         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31010         
31011         if(box[0].size == 'md-left'){
31012             rand = 0;
31013         }
31014         
31015         if(box[0].size == 'md-right'){
31016             rand = 1;
31017         }
31018         
31019         pos.push({
31020             x : x + (this.unitWidth + this.gutter) * rand,
31021             y : y
31022         });
31023         
31024         return pos;
31025     },
31026     
31027     getVerticalTwoBoxColPositions : function(x, y, box)
31028     {
31029         var pos = [];
31030         
31031         if(box[0].size == 'xs'){
31032             
31033             pos.push({
31034                 x : x,
31035                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31036             });
31037
31038             pos.push({
31039                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31040                 y : y
31041             });
31042             
31043             return pos;
31044             
31045         }
31046         
31047         pos.push({
31048             x : x,
31049             y : y
31050         });
31051
31052         pos.push({
31053             x : x + (this.unitWidth + this.gutter) * 2,
31054             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31055         });
31056         
31057         return pos;
31058         
31059     },
31060     
31061     getVerticalThreeBoxColPositions : function(x, y, box)
31062     {
31063         var pos = [];
31064         
31065         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31066             
31067             pos.push({
31068                 x : x,
31069                 y : y
31070             });
31071
31072             pos.push({
31073                 x : x + (this.unitWidth + this.gutter) * 1,
31074                 y : y
31075             });
31076             
31077             pos.push({
31078                 x : x + (this.unitWidth + this.gutter) * 2,
31079                 y : y
31080             });
31081             
31082             return pos;
31083             
31084         }
31085         
31086         if(box[0].size == 'xs' && box[1].size == 'xs'){
31087             
31088             pos.push({
31089                 x : x,
31090                 y : y
31091             });
31092
31093             pos.push({
31094                 x : x,
31095                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31096             });
31097             
31098             pos.push({
31099                 x : x + (this.unitWidth + this.gutter) * 1,
31100                 y : y
31101             });
31102             
31103             return pos;
31104             
31105         }
31106         
31107         pos.push({
31108             x : x,
31109             y : y
31110         });
31111
31112         pos.push({
31113             x : x + (this.unitWidth + this.gutter) * 2,
31114             y : y
31115         });
31116
31117         pos.push({
31118             x : x + (this.unitWidth + this.gutter) * 2,
31119             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31120         });
31121             
31122         return pos;
31123         
31124     },
31125     
31126     getVerticalFourBoxColPositions : function(x, y, box)
31127     {
31128         var pos = [];
31129         
31130         if(box[0].size == 'xs'){
31131             
31132             pos.push({
31133                 x : x,
31134                 y : y
31135             });
31136
31137             pos.push({
31138                 x : x,
31139                 y : y + (this.unitHeight + this.gutter) * 1
31140             });
31141             
31142             pos.push({
31143                 x : x,
31144                 y : y + (this.unitHeight + this.gutter) * 2
31145             });
31146             
31147             pos.push({
31148                 x : x + (this.unitWidth + this.gutter) * 1,
31149                 y : y
31150             });
31151             
31152             return pos;
31153             
31154         }
31155         
31156         pos.push({
31157             x : x,
31158             y : y
31159         });
31160
31161         pos.push({
31162             x : x + (this.unitWidth + this.gutter) * 2,
31163             y : y
31164         });
31165
31166         pos.push({
31167             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31168             y : y + (this.unitHeight + this.gutter) * 1
31169         });
31170
31171         pos.push({
31172             x : x + (this.unitWidth + this.gutter) * 2,
31173             y : y + (this.unitWidth + this.gutter) * 2
31174         });
31175
31176         return pos;
31177         
31178     },
31179     
31180     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31181     {
31182         var pos = [];
31183         
31184         if(box[0].size == 'md-left'){
31185             pos.push({
31186                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31187                 y : minY
31188             });
31189             
31190             return pos;
31191         }
31192         
31193         if(box[0].size == 'md-right'){
31194             pos.push({
31195                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31196                 y : minY + (this.unitWidth + this.gutter) * 1
31197             });
31198             
31199             return pos;
31200         }
31201         
31202         var rand = Math.floor(Math.random() * (4 - box[0].y));
31203         
31204         pos.push({
31205             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31206             y : minY + (this.unitWidth + this.gutter) * rand
31207         });
31208         
31209         return pos;
31210         
31211     },
31212     
31213     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31214     {
31215         var pos = [];
31216         
31217         if(box[0].size == 'xs'){
31218             
31219             pos.push({
31220                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31221                 y : minY
31222             });
31223
31224             pos.push({
31225                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31226                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31227             });
31228             
31229             return pos;
31230             
31231         }
31232         
31233         pos.push({
31234             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31235             y : minY
31236         });
31237
31238         pos.push({
31239             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31240             y : minY + (this.unitWidth + this.gutter) * 2
31241         });
31242         
31243         return pos;
31244         
31245     },
31246     
31247     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31248     {
31249         var pos = [];
31250         
31251         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31252             
31253             pos.push({
31254                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31255                 y : minY
31256             });
31257
31258             pos.push({
31259                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31260                 y : minY + (this.unitWidth + this.gutter) * 1
31261             });
31262             
31263             pos.push({
31264                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31265                 y : minY + (this.unitWidth + this.gutter) * 2
31266             });
31267             
31268             return pos;
31269             
31270         }
31271         
31272         if(box[0].size == 'xs' && box[1].size == 'xs'){
31273             
31274             pos.push({
31275                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31276                 y : minY
31277             });
31278
31279             pos.push({
31280                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31281                 y : minY
31282             });
31283             
31284             pos.push({
31285                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31286                 y : minY + (this.unitWidth + this.gutter) * 1
31287             });
31288             
31289             return pos;
31290             
31291         }
31292         
31293         pos.push({
31294             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31295             y : minY
31296         });
31297
31298         pos.push({
31299             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31300             y : minY + (this.unitWidth + this.gutter) * 2
31301         });
31302
31303         pos.push({
31304             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31305             y : minY + (this.unitWidth + this.gutter) * 2
31306         });
31307             
31308         return pos;
31309         
31310     },
31311     
31312     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31313     {
31314         var pos = [];
31315         
31316         if(box[0].size == 'xs'){
31317             
31318             pos.push({
31319                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31320                 y : minY
31321             });
31322
31323             pos.push({
31324                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31325                 y : minY
31326             });
31327             
31328             pos.push({
31329                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31330                 y : minY
31331             });
31332             
31333             pos.push({
31334                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31335                 y : minY + (this.unitWidth + this.gutter) * 1
31336             });
31337             
31338             return pos;
31339             
31340         }
31341         
31342         pos.push({
31343             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31344             y : minY
31345         });
31346         
31347         pos.push({
31348             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31349             y : minY + (this.unitWidth + this.gutter) * 2
31350         });
31351         
31352         pos.push({
31353             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31354             y : minY + (this.unitWidth + this.gutter) * 2
31355         });
31356         
31357         pos.push({
31358             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1) - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31359             y : minY + (this.unitWidth + this.gutter) * 2
31360         });
31361
31362         return pos;
31363         
31364     },
31365     
31366     /**
31367     * remove a Masonry Brick
31368     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31369     */
31370     removeBrick : function(brick_id)
31371     {
31372         if (!brick_id) {
31373             return;
31374         }
31375         
31376         for (var i = 0; i<this.bricks.length; i++) {
31377             if (this.bricks[i].id == brick_id) {
31378                 this.bricks.splice(i,1);
31379                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31380                 this.initial();
31381             }
31382         }
31383     },
31384     
31385     /**
31386     * adds a Masonry Brick
31387     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31388     */
31389     addBrick : function(cfg)
31390     {
31391         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31392         //this.register(cn);
31393         cn.parentId = this.id;
31394         cn.onRender(this.el, null);
31395         return cn;
31396     },
31397     
31398     /**
31399     * register a Masonry Brick
31400     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31401     */
31402     
31403     register : function(brick)
31404     {
31405         this.bricks.push(brick);
31406         brick.masonryId = this.id;
31407     },
31408     
31409     /**
31410     * clear all the Masonry Brick
31411     */
31412     clearAll : function()
31413     {
31414         this.bricks = [];
31415         //this.getChildContainer().dom.innerHTML = "";
31416         this.el.dom.innerHTML = '';
31417     },
31418     
31419     getSelected : function()
31420     {
31421         if (!this.selectedBrick) {
31422             return false;
31423         }
31424         
31425         return this.selectedBrick;
31426     }
31427 });
31428
31429 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31430     
31431     groups: {},
31432      /**
31433     * register a Masonry Layout
31434     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31435     */
31436     
31437     register : function(layout)
31438     {
31439         this.groups[layout.id] = layout;
31440     },
31441     /**
31442     * fetch a  Masonry Layout based on the masonry layout ID
31443     * @param {string} the masonry layout to add
31444     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31445     */
31446     
31447     get: function(layout_id) {
31448         if (typeof(this.groups[layout_id]) == 'undefined') {
31449             return false;
31450         }
31451         return this.groups[layout_id] ;
31452     }
31453     
31454     
31455     
31456 });
31457
31458  
31459
31460  /**
31461  *
31462  * This is based on 
31463  * http://masonry.desandro.com
31464  *
31465  * The idea is to render all the bricks based on vertical width...
31466  *
31467  * The original code extends 'outlayer' - we might need to use that....
31468  * 
31469  */
31470
31471
31472 /**
31473  * @class Roo.bootstrap.LayoutMasonryAuto
31474  * @extends Roo.bootstrap.Component
31475  * Bootstrap Layout Masonry class
31476  * 
31477  * @constructor
31478  * Create a new Element
31479  * @param {Object} config The config object
31480  */
31481
31482 Roo.bootstrap.LayoutMasonryAuto = function(config){
31483     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31484 };
31485
31486 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
31487     
31488       /**
31489      * @cfg {Boolean} isFitWidth  - resize the width..
31490      */   
31491     isFitWidth : false,  // options..
31492     /**
31493      * @cfg {Boolean} isOriginLeft = left align?
31494      */   
31495     isOriginLeft : true,
31496     /**
31497      * @cfg {Boolean} isOriginTop = top align?
31498      */   
31499     isOriginTop : false,
31500     /**
31501      * @cfg {Boolean} isLayoutInstant = no animation?
31502      */   
31503     isLayoutInstant : false, // needed?
31504     /**
31505      * @cfg {Boolean} isResizingContainer = not sure if this is used..
31506      */   
31507     isResizingContainer : true,
31508     /**
31509      * @cfg {Number} columnWidth  width of the columns 
31510      */   
31511     
31512     columnWidth : 0,
31513     
31514     /**
31515      * @cfg {Number} maxCols maximum number of columns
31516      */   
31517     
31518     maxCols: 0,
31519     /**
31520      * @cfg {Number} padHeight padding below box..
31521      */   
31522     
31523     padHeight : 10, 
31524     
31525     /**
31526      * @cfg {Boolean} isAutoInitial defalut true
31527      */   
31528     
31529     isAutoInitial : true, 
31530     
31531     // private?
31532     gutter : 0,
31533     
31534     containerWidth: 0,
31535     initialColumnWidth : 0,
31536     currentSize : null,
31537     
31538     colYs : null, // array.
31539     maxY : 0,
31540     padWidth: 10,
31541     
31542     
31543     tag: 'div',
31544     cls: '',
31545     bricks: null, //CompositeElement
31546     cols : 0, // array?
31547     // element : null, // wrapped now this.el
31548     _isLayoutInited : null, 
31549     
31550     
31551     getAutoCreate : function(){
31552         
31553         var cfg = {
31554             tag: this.tag,
31555             cls: 'blog-masonary-wrapper ' + this.cls,
31556             cn : {
31557                 cls : 'mas-boxes masonary'
31558             }
31559         };
31560         
31561         return cfg;
31562     },
31563     
31564     getChildContainer: function( )
31565     {
31566         if (this.boxesEl) {
31567             return this.boxesEl;
31568         }
31569         
31570         this.boxesEl = this.el.select('.mas-boxes').first();
31571         
31572         return this.boxesEl;
31573     },
31574     
31575     
31576     initEvents : function()
31577     {
31578         var _this = this;
31579         
31580         if(this.isAutoInitial){
31581             Roo.log('hook children rendered');
31582             this.on('childrenrendered', function() {
31583                 Roo.log('children rendered');
31584                 _this.initial();
31585             } ,this);
31586         }
31587         
31588     },
31589     
31590     initial : function()
31591     {
31592         this.reloadItems();
31593
31594         this.currentSize = this.el.getBox(true);
31595
31596         /// was window resize... - let's see if this works..
31597         Roo.EventManager.onWindowResize(this.resize, this); 
31598
31599         if(!this.isAutoInitial){
31600             this.layout();
31601             return;
31602         }
31603         
31604         this.layout.defer(500,this);
31605     },
31606     
31607     reloadItems: function()
31608     {
31609         this.bricks = this.el.select('.masonry-brick', true);
31610         
31611         this.bricks.each(function(b) {
31612             //Roo.log(b.getSize());
31613             if (!b.attr('originalwidth')) {
31614                 b.attr('originalwidth',  b.getSize().width);
31615             }
31616             
31617         });
31618         
31619         Roo.log(this.bricks.elements.length);
31620     },
31621     
31622     resize : function()
31623     {
31624         Roo.log('resize');
31625         var cs = this.el.getBox(true);
31626         
31627         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
31628             Roo.log("no change in with or X");
31629             return;
31630         }
31631         this.currentSize = cs;
31632         this.layout();
31633     },
31634     
31635     layout : function()
31636     {
31637          Roo.log('layout');
31638         this._resetLayout();
31639         //this._manageStamps();
31640       
31641         // don't animate first layout
31642         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31643         this.layoutItems( isInstant );
31644       
31645         // flag for initalized
31646         this._isLayoutInited = true;
31647     },
31648     
31649     layoutItems : function( isInstant )
31650     {
31651         //var items = this._getItemsForLayout( this.items );
31652         // original code supports filtering layout items.. we just ignore it..
31653         
31654         this._layoutItems( this.bricks , isInstant );
31655       
31656         this._postLayout();
31657     },
31658     _layoutItems : function ( items , isInstant)
31659     {
31660        //this.fireEvent( 'layout', this, items );
31661     
31662
31663         if ( !items || !items.elements.length ) {
31664           // no items, emit event with empty array
31665             return;
31666         }
31667
31668         var queue = [];
31669         items.each(function(item) {
31670             Roo.log("layout item");
31671             Roo.log(item);
31672             // get x/y object from method
31673             var position = this._getItemLayoutPosition( item );
31674             // enqueue
31675             position.item = item;
31676             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
31677             queue.push( position );
31678         }, this);
31679       
31680         this._processLayoutQueue( queue );
31681     },
31682     /** Sets position of item in DOM
31683     * @param {Element} item
31684     * @param {Number} x - horizontal position
31685     * @param {Number} y - vertical position
31686     * @param {Boolean} isInstant - disables transitions
31687     */
31688     _processLayoutQueue : function( queue )
31689     {
31690         for ( var i=0, len = queue.length; i < len; i++ ) {
31691             var obj = queue[i];
31692             obj.item.position('absolute');
31693             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
31694         }
31695     },
31696       
31697     
31698     /**
31699     * Any logic you want to do after each layout,
31700     * i.e. size the container
31701     */
31702     _postLayout : function()
31703     {
31704         this.resizeContainer();
31705     },
31706     
31707     resizeContainer : function()
31708     {
31709         if ( !this.isResizingContainer ) {
31710             return;
31711         }
31712         var size = this._getContainerSize();
31713         if ( size ) {
31714             this.el.setSize(size.width,size.height);
31715             this.boxesEl.setSize(size.width,size.height);
31716         }
31717     },
31718     
31719     
31720     
31721     _resetLayout : function()
31722     {
31723         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
31724         this.colWidth = this.el.getWidth();
31725         //this.gutter = this.el.getWidth(); 
31726         
31727         this.measureColumns();
31728
31729         // reset column Y
31730         var i = this.cols;
31731         this.colYs = [];
31732         while (i--) {
31733             this.colYs.push( 0 );
31734         }
31735     
31736         this.maxY = 0;
31737     },
31738
31739     measureColumns : function()
31740     {
31741         this.getContainerWidth();
31742       // if columnWidth is 0, default to outerWidth of first item
31743         if ( !this.columnWidth ) {
31744             var firstItem = this.bricks.first();
31745             Roo.log(firstItem);
31746             this.columnWidth  = this.containerWidth;
31747             if (firstItem && firstItem.attr('originalwidth') ) {
31748                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
31749             }
31750             // columnWidth fall back to item of first element
31751             Roo.log("set column width?");
31752                         this.initialColumnWidth = this.columnWidth  ;
31753
31754             // if first elem has no width, default to size of container
31755             
31756         }
31757         
31758         
31759         if (this.initialColumnWidth) {
31760             this.columnWidth = this.initialColumnWidth;
31761         }
31762         
31763         
31764             
31765         // column width is fixed at the top - however if container width get's smaller we should
31766         // reduce it...
31767         
31768         // this bit calcs how man columns..
31769             
31770         var columnWidth = this.columnWidth += this.gutter;
31771       
31772         // calculate columns
31773         var containerWidth = this.containerWidth + this.gutter;
31774         
31775         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
31776         // fix rounding errors, typically with gutters
31777         var excess = columnWidth - containerWidth % columnWidth;
31778         
31779         
31780         // if overshoot is less than a pixel, round up, otherwise floor it
31781         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
31782         cols = Math[ mathMethod ]( cols );
31783         this.cols = Math.max( cols, 1 );
31784         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
31785         
31786          // padding positioning..
31787         var totalColWidth = this.cols * this.columnWidth;
31788         var padavail = this.containerWidth - totalColWidth;
31789         // so for 2 columns - we need 3 'pads'
31790         
31791         var padNeeded = (1+this.cols) * this.padWidth;
31792         
31793         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
31794         
31795         this.columnWidth += padExtra
31796         //this.padWidth = Math.floor(padavail /  ( this.cols));
31797         
31798         // adjust colum width so that padding is fixed??
31799         
31800         // we have 3 columns ... total = width * 3
31801         // we have X left over... that should be used by 
31802         
31803         //if (this.expandC) {
31804             
31805         //}
31806         
31807         
31808         
31809     },
31810     
31811     getContainerWidth : function()
31812     {
31813        /* // container is parent if fit width
31814         var container = this.isFitWidth ? this.element.parentNode : this.element;
31815         // check that this.size and size are there
31816         // IE8 triggers resize on body size change, so they might not be
31817         
31818         var size = getSize( container );  //FIXME
31819         this.containerWidth = size && size.innerWidth; //FIXME
31820         */
31821          
31822         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
31823         
31824     },
31825     
31826     _getItemLayoutPosition : function( item )  // what is item?
31827     {
31828         // we resize the item to our columnWidth..
31829       
31830         item.setWidth(this.columnWidth);
31831         item.autoBoxAdjust  = false;
31832         
31833         var sz = item.getSize();
31834  
31835         // how many columns does this brick span
31836         var remainder = this.containerWidth % this.columnWidth;
31837         
31838         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
31839         // round if off by 1 pixel, otherwise use ceil
31840         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
31841         colSpan = Math.min( colSpan, this.cols );
31842         
31843         // normally this should be '1' as we dont' currently allow multi width columns..
31844         
31845         var colGroup = this._getColGroup( colSpan );
31846         // get the minimum Y value from the columns
31847         var minimumY = Math.min.apply( Math, colGroup );
31848         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
31849         
31850         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
31851          
31852         // position the brick
31853         var position = {
31854             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
31855             y: this.currentSize.y + minimumY + this.padHeight
31856         };
31857         
31858         Roo.log(position);
31859         // apply setHeight to necessary columns
31860         var setHeight = minimumY + sz.height + this.padHeight;
31861         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
31862         
31863         var setSpan = this.cols + 1 - colGroup.length;
31864         for ( var i = 0; i < setSpan; i++ ) {
31865           this.colYs[ shortColIndex + i ] = setHeight ;
31866         }
31867       
31868         return position;
31869     },
31870     
31871     /**
31872      * @param {Number} colSpan - number of columns the element spans
31873      * @returns {Array} colGroup
31874      */
31875     _getColGroup : function( colSpan )
31876     {
31877         if ( colSpan < 2 ) {
31878           // if brick spans only one column, use all the column Ys
31879           return this.colYs;
31880         }
31881       
31882         var colGroup = [];
31883         // how many different places could this brick fit horizontally
31884         var groupCount = this.cols + 1 - colSpan;
31885         // for each group potential horizontal position
31886         for ( var i = 0; i < groupCount; i++ ) {
31887           // make an array of colY values for that one group
31888           var groupColYs = this.colYs.slice( i, i + colSpan );
31889           // and get the max value of the array
31890           colGroup[i] = Math.max.apply( Math, groupColYs );
31891         }
31892         return colGroup;
31893     },
31894     /*
31895     _manageStamp : function( stamp )
31896     {
31897         var stampSize =  stamp.getSize();
31898         var offset = stamp.getBox();
31899         // get the columns that this stamp affects
31900         var firstX = this.isOriginLeft ? offset.x : offset.right;
31901         var lastX = firstX + stampSize.width;
31902         var firstCol = Math.floor( firstX / this.columnWidth );
31903         firstCol = Math.max( 0, firstCol );
31904         
31905         var lastCol = Math.floor( lastX / this.columnWidth );
31906         // lastCol should not go over if multiple of columnWidth #425
31907         lastCol -= lastX % this.columnWidth ? 0 : 1;
31908         lastCol = Math.min( this.cols - 1, lastCol );
31909         
31910         // set colYs to bottom of the stamp
31911         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
31912             stampSize.height;
31913             
31914         for ( var i = firstCol; i <= lastCol; i++ ) {
31915           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
31916         }
31917     },
31918     */
31919     
31920     _getContainerSize : function()
31921     {
31922         this.maxY = Math.max.apply( Math, this.colYs );
31923         var size = {
31924             height: this.maxY
31925         };
31926       
31927         if ( this.isFitWidth ) {
31928             size.width = this._getContainerFitWidth();
31929         }
31930       
31931         return size;
31932     },
31933     
31934     _getContainerFitWidth : function()
31935     {
31936         var unusedCols = 0;
31937         // count unused columns
31938         var i = this.cols;
31939         while ( --i ) {
31940           if ( this.colYs[i] !== 0 ) {
31941             break;
31942           }
31943           unusedCols++;
31944         }
31945         // fit container to columns that have been used
31946         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
31947     },
31948     
31949     needsResizeLayout : function()
31950     {
31951         var previousWidth = this.containerWidth;
31952         this.getContainerWidth();
31953         return previousWidth !== this.containerWidth;
31954     }
31955  
31956 });
31957
31958  
31959
31960  /*
31961  * - LGPL
31962  *
31963  * element
31964  * 
31965  */
31966
31967 /**
31968  * @class Roo.bootstrap.MasonryBrick
31969  * @extends Roo.bootstrap.Component
31970  * Bootstrap MasonryBrick class
31971  * 
31972  * @constructor
31973  * Create a new MasonryBrick
31974  * @param {Object} config The config object
31975  */
31976
31977 Roo.bootstrap.MasonryBrick = function(config){
31978     
31979     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
31980     
31981     Roo.bootstrap.MasonryBrick.register(this);
31982     
31983     this.addEvents({
31984         // raw events
31985         /**
31986          * @event click
31987          * When a MasonryBrick is clcik
31988          * @param {Roo.bootstrap.MasonryBrick} this
31989          * @param {Roo.EventObject} e
31990          */
31991         "click" : true
31992     });
31993 };
31994
31995 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
31996     
31997     /**
31998      * @cfg {String} title
31999      */   
32000     title : '',
32001     /**
32002      * @cfg {String} html
32003      */   
32004     html : '',
32005     /**
32006      * @cfg {String} bgimage
32007      */   
32008     bgimage : '',
32009     /**
32010      * @cfg {String} videourl
32011      */   
32012     videourl : '',
32013     /**
32014      * @cfg {String} cls
32015      */   
32016     cls : '',
32017     /**
32018      * @cfg {String} href
32019      */   
32020     href : '',
32021     /**
32022      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32023      */   
32024     size : 'xs',
32025     
32026     /**
32027      * @cfg {String} placetitle (center|bottom)
32028      */   
32029     placetitle : '',
32030     
32031     /**
32032      * @cfg {Boolean} isFitContainer defalut true
32033      */   
32034     isFitContainer : true, 
32035     
32036     /**
32037      * @cfg {Boolean} preventDefault defalut false
32038      */   
32039     preventDefault : false, 
32040     
32041     /**
32042      * @cfg {Boolean} inverse defalut false
32043      */   
32044     maskInverse : false, 
32045     
32046     getAutoCreate : function()
32047     {
32048         if(!this.isFitContainer){
32049             return this.getSplitAutoCreate();
32050         }
32051         
32052         var cls = 'masonry-brick masonry-brick-full';
32053         
32054         if(this.href.length){
32055             cls += ' masonry-brick-link';
32056         }
32057         
32058         if(this.bgimage.length){
32059             cls += ' masonry-brick-image';
32060         }
32061         
32062         if(this.maskInverse){
32063             cls += ' mask-inverse';
32064         }
32065         
32066         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32067             cls += ' enable-mask';
32068         }
32069         
32070         if(this.size){
32071             cls += ' masonry-' + this.size + '-brick';
32072         }
32073         
32074         if(this.placetitle.length){
32075             
32076             switch (this.placetitle) {
32077                 case 'center' :
32078                     cls += ' masonry-center-title';
32079                     break;
32080                 case 'bottom' :
32081                     cls += ' masonry-bottom-title';
32082                     break;
32083                 default:
32084                     break;
32085             }
32086             
32087         } else {
32088             if(!this.html.length && !this.bgimage.length){
32089                 cls += ' masonry-center-title';
32090             }
32091
32092             if(!this.html.length && this.bgimage.length){
32093                 cls += ' masonry-bottom-title';
32094             }
32095         }
32096         
32097         if(this.cls){
32098             cls += ' ' + this.cls;
32099         }
32100         
32101         var cfg = {
32102             tag: (this.href.length) ? 'a' : 'div',
32103             cls: cls,
32104             cn: [
32105                 {
32106                     tag: 'div',
32107                     cls: 'masonry-brick-mask'
32108                 },
32109                 {
32110                     tag: 'div',
32111                     cls: 'masonry-brick-paragraph',
32112                     cn: []
32113                 }
32114             ]
32115         };
32116         
32117         if(this.href.length){
32118             cfg.href = this.href;
32119         }
32120         
32121         var cn = cfg.cn[1].cn;
32122         
32123         if(this.title.length){
32124             cn.push({
32125                 tag: 'h4',
32126                 cls: 'masonry-brick-title',
32127                 html: this.title
32128             });
32129         }
32130         
32131         if(this.html.length){
32132             cn.push({
32133                 tag: 'p',
32134                 cls: 'masonry-brick-text',
32135                 html: this.html
32136             });
32137         }
32138         
32139         if (!this.title.length && !this.html.length) {
32140             cfg.cn[1].cls += ' hide';
32141         }
32142         
32143         if(this.bgimage.length){
32144             cfg.cn.push({
32145                 tag: 'img',
32146                 cls: 'masonry-brick-image-view',
32147                 src: this.bgimage
32148             });
32149         }
32150         
32151         if(this.videourl.length){
32152             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32153             // youtube support only?
32154             cfg.cn.push({
32155                 tag: 'iframe',
32156                 cls: 'masonry-brick-image-view',
32157                 src: vurl,
32158                 frameborder : 0,
32159                 allowfullscreen : true
32160             });
32161         }
32162         
32163         return cfg;
32164         
32165     },
32166     
32167     getSplitAutoCreate : function()
32168     {
32169         var cls = 'masonry-brick masonry-brick-split';
32170         
32171         if(this.href.length){
32172             cls += ' masonry-brick-link';
32173         }
32174         
32175         if(this.bgimage.length){
32176             cls += ' masonry-brick-image';
32177         }
32178         
32179         if(this.size){
32180             cls += ' masonry-' + this.size + '-brick';
32181         }
32182         
32183         switch (this.placetitle) {
32184             case 'center' :
32185                 cls += ' masonry-center-title';
32186                 break;
32187             case 'bottom' :
32188                 cls += ' masonry-bottom-title';
32189                 break;
32190             default:
32191                 if(!this.bgimage.length){
32192                     cls += ' masonry-center-title';
32193                 }
32194
32195                 if(this.bgimage.length){
32196                     cls += ' masonry-bottom-title';
32197                 }
32198                 break;
32199         }
32200         
32201         if(this.cls){
32202             cls += ' ' + this.cls;
32203         }
32204         
32205         var cfg = {
32206             tag: (this.href.length) ? 'a' : 'div',
32207             cls: cls,
32208             cn: [
32209                 {
32210                     tag: 'div',
32211                     cls: 'masonry-brick-split-head',
32212                     cn: [
32213                         {
32214                             tag: 'div',
32215                             cls: 'masonry-brick-paragraph',
32216                             cn: []
32217                         }
32218                     ]
32219                 },
32220                 {
32221                     tag: 'div',
32222                     cls: 'masonry-brick-split-body',
32223                     cn: []
32224                 }
32225             ]
32226         };
32227         
32228         if(this.href.length){
32229             cfg.href = this.href;
32230         }
32231         
32232         if(this.title.length){
32233             cfg.cn[0].cn[0].cn.push({
32234                 tag: 'h4',
32235                 cls: 'masonry-brick-title',
32236                 html: this.title
32237             });
32238         }
32239         
32240         if(this.html.length){
32241             cfg.cn[1].cn.push({
32242                 tag: 'p',
32243                 cls: 'masonry-brick-text',
32244                 html: this.html
32245             });
32246         }
32247
32248         if(this.bgimage.length){
32249             cfg.cn[0].cn.push({
32250                 tag: 'img',
32251                 cls: 'masonry-brick-image-view',
32252                 src: this.bgimage
32253             });
32254         }
32255         
32256         if(this.videourl.length){
32257             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32258             // youtube support only?
32259             cfg.cn[0].cn.cn.push({
32260                 tag: 'iframe',
32261                 cls: 'masonry-brick-image-view',
32262                 src: vurl,
32263                 frameborder : 0,
32264                 allowfullscreen : true
32265             });
32266         }
32267         
32268         return cfg;
32269     },
32270     
32271     initEvents: function() 
32272     {
32273         switch (this.size) {
32274             case 'xs' :
32275                 this.x = 1;
32276                 this.y = 1;
32277                 break;
32278             case 'sm' :
32279                 this.x = 2;
32280                 this.y = 2;
32281                 break;
32282             case 'md' :
32283             case 'md-left' :
32284             case 'md-right' :
32285                 this.x = 3;
32286                 this.y = 3;
32287                 break;
32288             case 'tall' :
32289                 this.x = 2;
32290                 this.y = 3;
32291                 break;
32292             case 'wide' :
32293                 this.x = 3;
32294                 this.y = 2;
32295                 break;
32296             case 'wide-thin' :
32297                 this.x = 3;
32298                 this.y = 1;
32299                 break;
32300                         
32301             default :
32302                 break;
32303         }
32304         
32305         if(Roo.isTouch){
32306             this.el.on('touchstart', this.onTouchStart, this);
32307             this.el.on('touchmove', this.onTouchMove, this);
32308             this.el.on('touchend', this.onTouchEnd, this);
32309             this.el.on('contextmenu', this.onContextMenu, this);
32310         } else {
32311             this.el.on('mouseenter'  ,this.enter, this);
32312             this.el.on('mouseleave', this.leave, this);
32313             this.el.on('click', this.onClick, this);
32314         }
32315         
32316         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32317             this.parent().bricks.push(this);   
32318         }
32319         
32320     },
32321     
32322     onClick: function(e, el)
32323     {
32324         var time = this.endTimer - this.startTimer;
32325         // Roo.log(e.preventDefault());
32326         if(Roo.isTouch){
32327             if(time > 1000){
32328                 e.preventDefault();
32329                 return;
32330             }
32331         }
32332         
32333         if(!this.preventDefault){
32334             return;
32335         }
32336         
32337         e.preventDefault();
32338         
32339         if (this.activcClass != '') {
32340             this.selectBrick();
32341         }
32342         
32343         this.fireEvent('click', this);
32344     },
32345     
32346     enter: function(e, el)
32347     {
32348         e.preventDefault();
32349         
32350         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32351             return;
32352         }
32353         
32354         if(this.bgimage.length && this.html.length){
32355             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32356         }
32357     },
32358     
32359     leave: function(e, el)
32360     {
32361         e.preventDefault();
32362         
32363         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32364             return;
32365         }
32366         
32367         if(this.bgimage.length && this.html.length){
32368             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32369         }
32370     },
32371     
32372     onTouchStart: function(e, el)
32373     {
32374 //        e.preventDefault();
32375         
32376         this.touchmoved = false;
32377         
32378         if(!this.isFitContainer){
32379             return;
32380         }
32381         
32382         if(!this.bgimage.length || !this.html.length){
32383             return;
32384         }
32385         
32386         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32387         
32388         this.timer = new Date().getTime();
32389         
32390     },
32391     
32392     onTouchMove: function(e, el)
32393     {
32394         this.touchmoved = true;
32395     },
32396     
32397     onContextMenu : function(e,el)
32398     {
32399         e.preventDefault();
32400         e.stopPropagation();
32401         return false;
32402     },
32403     
32404     onTouchEnd: function(e, el)
32405     {
32406 //        e.preventDefault();
32407         
32408         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32409         
32410             this.leave(e,el);
32411             
32412             return;
32413         }
32414         
32415         if(!this.bgimage.length || !this.html.length){
32416             
32417             if(this.href.length){
32418                 window.location.href = this.href;
32419             }
32420             
32421             return;
32422         }
32423         
32424         if(!this.isFitContainer){
32425             return;
32426         }
32427         
32428         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32429         
32430         window.location.href = this.href;
32431     },
32432     
32433     //selection on single brick only
32434     selectBrick : function() {
32435         
32436         if (!this.parentId) {
32437             return;
32438         }
32439         
32440         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32441         var index = m.selectedBrick.indexOf(this.id);
32442         
32443         if ( index > -1) {
32444             m.selectedBrick.splice(index,1);
32445             this.el.removeClass(this.activeClass);
32446             return;
32447         }
32448         
32449         for(var i = 0; i < m.selectedBrick.length; i++) {
32450             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32451             b.el.removeClass(b.activeClass);
32452         }
32453         
32454         m.selectedBrick = [];
32455         
32456         m.selectedBrick.push(this.id);
32457         this.el.addClass(this.activeClass);
32458         return;
32459     }
32460     
32461 });
32462
32463 Roo.apply(Roo.bootstrap.MasonryBrick, {
32464     
32465     //groups: {},
32466     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32467      /**
32468     * register a Masonry Brick
32469     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32470     */
32471     
32472     register : function(brick)
32473     {
32474         //this.groups[brick.id] = brick;
32475         this.groups.add(brick.id, brick);
32476     },
32477     /**
32478     * fetch a  masonry brick based on the masonry brick ID
32479     * @param {string} the masonry brick to add
32480     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32481     */
32482     
32483     get: function(brick_id) 
32484     {
32485         // if (typeof(this.groups[brick_id]) == 'undefined') {
32486         //     return false;
32487         // }
32488         // return this.groups[brick_id] ;
32489         
32490         if(this.groups.key(brick_id)) {
32491             return this.groups.key(brick_id);
32492         }
32493         
32494         return false;
32495     }
32496     
32497     
32498     
32499 });
32500
32501  /*
32502  * - LGPL
32503  *
32504  * element
32505  * 
32506  */
32507
32508 /**
32509  * @class Roo.bootstrap.Brick
32510  * @extends Roo.bootstrap.Component
32511  * Bootstrap Brick class
32512  * 
32513  * @constructor
32514  * Create a new Brick
32515  * @param {Object} config The config object
32516  */
32517
32518 Roo.bootstrap.Brick = function(config){
32519     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
32520     
32521     this.addEvents({
32522         // raw events
32523         /**
32524          * @event click
32525          * When a Brick is click
32526          * @param {Roo.bootstrap.Brick} this
32527          * @param {Roo.EventObject} e
32528          */
32529         "click" : true
32530     });
32531 };
32532
32533 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
32534     
32535     /**
32536      * @cfg {String} title
32537      */   
32538     title : '',
32539     /**
32540      * @cfg {String} html
32541      */   
32542     html : '',
32543     /**
32544      * @cfg {String} bgimage
32545      */   
32546     bgimage : '',
32547     /**
32548      * @cfg {String} cls
32549      */   
32550     cls : '',
32551     /**
32552      * @cfg {String} href
32553      */   
32554     href : '',
32555     /**
32556      * @cfg {String} video
32557      */   
32558     video : '',
32559     /**
32560      * @cfg {Boolean} square
32561      */   
32562     square : true,
32563     
32564     getAutoCreate : function()
32565     {
32566         var cls = 'roo-brick';
32567         
32568         if(this.href.length){
32569             cls += ' roo-brick-link';
32570         }
32571         
32572         if(this.bgimage.length){
32573             cls += ' roo-brick-image';
32574         }
32575         
32576         if(!this.html.length && !this.bgimage.length){
32577             cls += ' roo-brick-center-title';
32578         }
32579         
32580         if(!this.html.length && this.bgimage.length){
32581             cls += ' roo-brick-bottom-title';
32582         }
32583         
32584         if(this.cls){
32585             cls += ' ' + this.cls;
32586         }
32587         
32588         var cfg = {
32589             tag: (this.href.length) ? 'a' : 'div',
32590             cls: cls,
32591             cn: [
32592                 {
32593                     tag: 'div',
32594                     cls: 'roo-brick-paragraph',
32595                     cn: []
32596                 }
32597             ]
32598         };
32599         
32600         if(this.href.length){
32601             cfg.href = this.href;
32602         }
32603         
32604         var cn = cfg.cn[0].cn;
32605         
32606         if(this.title.length){
32607             cn.push({
32608                 tag: 'h4',
32609                 cls: 'roo-brick-title',
32610                 html: this.title
32611             });
32612         }
32613         
32614         if(this.html.length){
32615             cn.push({
32616                 tag: 'p',
32617                 cls: 'roo-brick-text',
32618                 html: this.html
32619             });
32620         } else {
32621             cn.cls += ' hide';
32622         }
32623         
32624         if(this.bgimage.length){
32625             cfg.cn.push({
32626                 tag: 'img',
32627                 cls: 'roo-brick-image-view',
32628                 src: this.bgimage
32629             });
32630         }
32631         
32632         return cfg;
32633     },
32634     
32635     initEvents: function() 
32636     {
32637         if(this.title.length || this.html.length){
32638             this.el.on('mouseenter'  ,this.enter, this);
32639             this.el.on('mouseleave', this.leave, this);
32640         }
32641         
32642         Roo.EventManager.onWindowResize(this.resize, this); 
32643         
32644         if(this.bgimage.length){
32645             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
32646             this.imageEl.on('load', this.onImageLoad, this);
32647             return;
32648         }
32649         
32650         this.resize();
32651     },
32652     
32653     onImageLoad : function()
32654     {
32655         this.resize();
32656     },
32657     
32658     resize : function()
32659     {
32660         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
32661         
32662         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
32663         
32664         if(this.bgimage.length){
32665             var image = this.el.select('.roo-brick-image-view', true).first();
32666             
32667             image.setWidth(paragraph.getWidth());
32668             
32669             if(this.square){
32670                 image.setHeight(paragraph.getWidth());
32671             }
32672             
32673             this.el.setHeight(image.getHeight());
32674             paragraph.setHeight(image.getHeight());
32675             
32676         }
32677         
32678     },
32679     
32680     enter: function(e, el)
32681     {
32682         e.preventDefault();
32683         
32684         if(this.bgimage.length){
32685             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
32686             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
32687         }
32688     },
32689     
32690     leave: function(e, el)
32691     {
32692         e.preventDefault();
32693         
32694         if(this.bgimage.length){
32695             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
32696             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
32697         }
32698     }
32699     
32700 });
32701
32702  
32703
32704  /*
32705  * - LGPL
32706  *
32707  * Input
32708  * 
32709  */
32710
32711 /**
32712  * @class Roo.bootstrap.NumberField
32713  * @extends Roo.bootstrap.Input
32714  * Bootstrap NumberField class
32715  * 
32716  * 
32717  * 
32718  * 
32719  * @constructor
32720  * Create a new NumberField
32721  * @param {Object} config The config object
32722  */
32723
32724 Roo.bootstrap.NumberField = function(config){
32725     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
32726 };
32727
32728 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
32729     
32730     /**
32731      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
32732      */
32733     allowDecimals : true,
32734     /**
32735      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
32736      */
32737     decimalSeparator : ".",
32738     /**
32739      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
32740      */
32741     decimalPrecision : 2,
32742     /**
32743      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
32744      */
32745     allowNegative : true,
32746     /**
32747      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
32748      */
32749     minValue : Number.NEGATIVE_INFINITY,
32750     /**
32751      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
32752      */
32753     maxValue : Number.MAX_VALUE,
32754     /**
32755      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
32756      */
32757     minText : "The minimum value for this field is {0}",
32758     /**
32759      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
32760      */
32761     maxText : "The maximum value for this field is {0}",
32762     /**
32763      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
32764      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
32765      */
32766     nanText : "{0} is not a valid number",
32767     /**
32768      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
32769      */
32770     castInt : true,
32771
32772     // private
32773     initEvents : function()
32774     {   
32775         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
32776         
32777         var allowed = "0123456789";
32778         
32779         if(this.allowDecimals){
32780             allowed += this.decimalSeparator;
32781         }
32782         
32783         if(this.allowNegative){
32784             allowed += "-";
32785         }
32786         
32787         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
32788         
32789         var keyPress = function(e){
32790             
32791             var k = e.getKey();
32792             
32793             var c = e.getCharCode();
32794             
32795             if(
32796                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
32797                     allowed.indexOf(String.fromCharCode(c)) === -1
32798             ){
32799                 e.stopEvent();
32800                 return;
32801             }
32802             
32803             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
32804                 return;
32805             }
32806             
32807             if(allowed.indexOf(String.fromCharCode(c)) === -1){
32808                 e.stopEvent();
32809             }
32810         };
32811         
32812         this.el.on("keypress", keyPress, this);
32813     },
32814     
32815     validateValue : function(value)
32816     {
32817         
32818         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
32819             return false;
32820         }
32821         
32822         var num = this.parseValue(value);
32823         
32824         if(isNaN(num)){
32825             this.markInvalid(String.format(this.nanText, value));
32826             return false;
32827         }
32828         
32829         if(num < this.minValue){
32830             this.markInvalid(String.format(this.minText, this.minValue));
32831             return false;
32832         }
32833         
32834         if(num > this.maxValue){
32835             this.markInvalid(String.format(this.maxText, this.maxValue));
32836             return false;
32837         }
32838         
32839         return true;
32840     },
32841
32842     getValue : function()
32843     {
32844         return this.fixPrecision(this.parseValue(Roo.bootstrap.NumberField.superclass.getValue.call(this)));
32845     },
32846
32847     parseValue : function(value)
32848     {
32849         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
32850         return isNaN(value) ? '' : value;
32851     },
32852
32853     fixPrecision : function(value)
32854     {
32855         var nan = isNaN(value);
32856         
32857         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
32858             return nan ? '' : value;
32859         }
32860         return parseFloat(value).toFixed(this.decimalPrecision);
32861     },
32862
32863     setValue : function(v)
32864     {
32865         v = this.fixPrecision(v);
32866         Roo.bootstrap.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
32867     },
32868
32869     decimalPrecisionFcn : function(v)
32870     {
32871         return Math.floor(v);
32872     },
32873
32874     beforeBlur : function()
32875     {
32876         if(!this.castInt){
32877             return;
32878         }
32879         
32880         var v = this.parseValue(this.getRawValue());
32881         if(v){
32882             this.setValue(v);
32883         }
32884     }
32885     
32886 });
32887
32888  
32889
32890 /*
32891 * Licence: LGPL
32892 */
32893
32894 /**
32895  * @class Roo.bootstrap.DocumentSlider
32896  * @extends Roo.bootstrap.Component
32897  * Bootstrap DocumentSlider class
32898  * 
32899  * @constructor
32900  * Create a new DocumentViewer
32901  * @param {Object} config The config object
32902  */
32903
32904 Roo.bootstrap.DocumentSlider = function(config){
32905     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
32906     
32907     this.files = [];
32908     
32909     this.addEvents({
32910         /**
32911          * @event initial
32912          * Fire after initEvent
32913          * @param {Roo.bootstrap.DocumentSlider} this
32914          */
32915         "initial" : true,
32916         /**
32917          * @event update
32918          * Fire after update
32919          * @param {Roo.bootstrap.DocumentSlider} this
32920          */
32921         "update" : true,
32922         /**
32923          * @event click
32924          * Fire after click
32925          * @param {Roo.bootstrap.DocumentSlider} this
32926          */
32927         "click" : true
32928     });
32929 };
32930
32931 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
32932     
32933     files : false,
32934     
32935     indicator : 0,
32936     
32937     getAutoCreate : function()
32938     {
32939         var cfg = {
32940             tag : 'div',
32941             cls : 'roo-document-slider',
32942             cn : [
32943                 {
32944                     tag : 'div',
32945                     cls : 'roo-document-slider-header',
32946                     cn : [
32947                         {
32948                             tag : 'div',
32949                             cls : 'roo-document-slider-header-title'
32950                         }
32951                     ]
32952                 },
32953                 {
32954                     tag : 'div',
32955                     cls : 'roo-document-slider-body',
32956                     cn : [
32957                         {
32958                             tag : 'div',
32959                             cls : 'roo-document-slider-prev',
32960                             cn : [
32961                                 {
32962                                     tag : 'i',
32963                                     cls : 'fa fa-chevron-left'
32964                                 }
32965                             ]
32966                         },
32967                         {
32968                             tag : 'div',
32969                             cls : 'roo-document-slider-thumb',
32970                             cn : [
32971                                 {
32972                                     tag : 'img',
32973                                     cls : 'roo-document-slider-image'
32974                                 }
32975                             ]
32976                         },
32977                         {
32978                             tag : 'div',
32979                             cls : 'roo-document-slider-next',
32980                             cn : [
32981                                 {
32982                                     tag : 'i',
32983                                     cls : 'fa fa-chevron-right'
32984                                 }
32985                             ]
32986                         }
32987                     ]
32988                 }
32989             ]
32990         };
32991         
32992         return cfg;
32993     },
32994     
32995     initEvents : function()
32996     {
32997         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
32998         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
32999         
33000         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33001         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33002         
33003         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33004         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33005         
33006         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33007         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33008         
33009         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33010         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33011         
33012         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33013         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33014         
33015         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33016         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33017         
33018         this.thumbEl.on('click', this.onClick, this);
33019         
33020         this.prevIndicator.on('click', this.prev, this);
33021         
33022         this.nextIndicator.on('click', this.next, this);
33023         
33024     },
33025     
33026     initial : function()
33027     {
33028         if(this.files.length){
33029             this.indicator = 1;
33030             this.update()
33031         }
33032         
33033         this.fireEvent('initial', this);
33034     },
33035     
33036     update : function()
33037     {
33038         this.imageEl.attr('src', this.files[this.indicator - 1]);
33039         
33040         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33041         
33042         this.prevIndicator.show();
33043         
33044         if(this.indicator == 1){
33045             this.prevIndicator.hide();
33046         }
33047         
33048         this.nextIndicator.show();
33049         
33050         if(this.indicator == this.files.length){
33051             this.nextIndicator.hide();
33052         }
33053         
33054         this.thumbEl.scrollTo('top');
33055         
33056         this.fireEvent('update', this);
33057     },
33058     
33059     onClick : function(e)
33060     {
33061         e.preventDefault();
33062         
33063         this.fireEvent('click', this);
33064     },
33065     
33066     prev : function(e)
33067     {
33068         e.preventDefault();
33069         
33070         this.indicator = Math.max(1, this.indicator - 1);
33071         
33072         this.update();
33073     },
33074     
33075     next : function(e)
33076     {
33077         e.preventDefault();
33078         
33079         this.indicator = Math.min(this.files.length, this.indicator + 1);
33080         
33081         this.update();
33082     }
33083 });
33084 /*
33085  * - LGPL
33086  *
33087  * RadioSet
33088  *
33089  *
33090  */
33091
33092 /**
33093  * @class Roo.bootstrap.RadioSet
33094  * @extends Roo.bootstrap.Input
33095  * Bootstrap RadioSet class
33096  * @cfg {String} indicatorpos (left|right) default left
33097  * @cfg {Boolean} inline (true|false) inline the element (default true)
33098  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33099  * @constructor
33100  * Create a new RadioSet
33101  * @param {Object} config The config object
33102  */
33103
33104 Roo.bootstrap.RadioSet = function(config){
33105     
33106     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33107     
33108     this.radioes = [];
33109     
33110     Roo.bootstrap.RadioSet.register(this);
33111     
33112     this.addEvents({
33113         /**
33114         * @event check
33115         * Fires when the element is checked or unchecked.
33116         * @param {Roo.bootstrap.RadioSet} this This radio
33117         * @param {Roo.bootstrap.Radio} item The checked item
33118         */
33119        check : true
33120     });
33121     
33122 };
33123
33124 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33125
33126     radioes : false,
33127     
33128     inline : true,
33129     
33130     weight : '',
33131     
33132     indicatorpos : 'left',
33133     
33134     getAutoCreate : function()
33135     {
33136         var label = {
33137             tag : 'label',
33138             cls : 'roo-radio-set-label',
33139             cn : [
33140                 {
33141                     tag : 'span',
33142                     html : this.fieldLabel
33143                 }
33144             ]
33145         };
33146         
33147         if(this.indicatorpos == 'left'){
33148             label.cn.unshift({
33149                 tag : 'i',
33150                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33151                 tooltip : 'This field is required'
33152             });
33153         } else {
33154             label.cn.push({
33155                 tag : 'i',
33156                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33157                 tooltip : 'This field is required'
33158             });
33159         }
33160         
33161         var items = {
33162             tag : 'div',
33163             cls : 'roo-radio-set-items'
33164         };
33165         
33166         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33167         
33168         if (align === 'left' && this.fieldLabel.length) {
33169             
33170             items = {
33171                 cls : "roo-radio-set-right", 
33172                 cn: [
33173                     items
33174                 ]
33175             };
33176             
33177             if(this.labelWidth > 12){
33178                 label.style = "width: " + this.labelWidth + 'px';
33179             }
33180             
33181             if(this.labelWidth < 13 && this.labelmd == 0){
33182                 this.labelmd = this.labelWidth;
33183             }
33184             
33185             if(this.labellg > 0){
33186                 label.cls += ' col-lg-' + this.labellg;
33187                 items.cls += ' col-lg-' + (12 - this.labellg);
33188             }
33189             
33190             if(this.labelmd > 0){
33191                 label.cls += ' col-md-' + this.labelmd;
33192                 items.cls += ' col-md-' + (12 - this.labelmd);
33193             }
33194             
33195             if(this.labelsm > 0){
33196                 label.cls += ' col-sm-' + this.labelsm;
33197                 items.cls += ' col-sm-' + (12 - this.labelsm);
33198             }
33199             
33200             if(this.labelxs > 0){
33201                 label.cls += ' col-xs-' + this.labelxs;
33202                 items.cls += ' col-xs-' + (12 - this.labelxs);
33203             }
33204         }
33205         
33206         var cfg = {
33207             tag : 'div',
33208             cls : 'roo-radio-set',
33209             cn : [
33210                 {
33211                     tag : 'input',
33212                     cls : 'roo-radio-set-input',
33213                     type : 'hidden',
33214                     name : this.name,
33215                     value : this.value ? this.value :  ''
33216                 },
33217                 label,
33218                 items
33219             ]
33220         };
33221         
33222         if(this.weight.length){
33223             cfg.cls += ' roo-radio-' + this.weight;
33224         }
33225         
33226         if(this.inline) {
33227             cfg.cls += ' roo-radio-set-inline';
33228         }
33229         
33230         return cfg;
33231         
33232     },
33233
33234     initEvents : function()
33235     {
33236         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33237         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33238         
33239         if(!this.fieldLabel.length){
33240             this.labelEl.hide();
33241         }
33242         
33243         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33244         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33245         
33246         this.indicatorEl().setVisibilityMode(Roo.Element.DISPLAY);
33247         this.indicatorEl().hide();
33248         
33249         this.originalValue = this.getValue();
33250         
33251     },
33252     
33253     inputEl: function ()
33254     {
33255         return this.el.select('.roo-radio-set-input', true).first();
33256     },
33257     
33258     getChildContainer : function()
33259     {
33260         return this.itemsEl;
33261     },
33262     
33263     register : function(item)
33264     {
33265         this.radioes.push(item);
33266         
33267     },
33268     
33269     validate : function()
33270     {   
33271         var valid = false;
33272         
33273         Roo.each(this.radioes, function(i){
33274             if(!i.checked){
33275                 return;
33276             }
33277             
33278             valid = true;
33279             return false;
33280         });
33281         
33282         if(this.allowBlank) {
33283             return true;
33284         }
33285         
33286         if(this.disabled || valid){
33287             this.markValid();
33288             return true;
33289         }
33290         
33291         this.markInvalid();
33292         return false;
33293         
33294     },
33295     
33296     markValid : function()
33297     {
33298         if(this.labelEl.isVisible(true)){
33299             this.indicatorEl().hide();
33300         }
33301         
33302         this.el.removeClass([this.invalidClass, this.validClass]);
33303         this.el.addClass(this.validClass);
33304         
33305         this.fireEvent('valid', this);
33306     },
33307     
33308     markInvalid : function(msg)
33309     {
33310         if(this.allowBlank || this.disabled){
33311             return;
33312         }
33313         
33314         if(this.labelEl.isVisible(true)){
33315             this.indicatorEl().show();
33316         }
33317         
33318         this.el.removeClass([this.invalidClass, this.validClass]);
33319         this.el.addClass(this.invalidClass);
33320         
33321         this.fireEvent('invalid', this, msg);
33322         
33323     },
33324     
33325     setValue : function(v, suppressEvent)
33326     {   
33327         this.value = v;
33328         if(this.rendered){
33329             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33330         }
33331         
33332         Roo.each(this.radioes, function(i){
33333             
33334             i.checked = false;
33335             i.el.removeClass('checked');
33336             
33337             if(i.value === v || i.value.toString() === v.toString()){
33338                 i.checked = true;
33339                 i.el.addClass('checked');
33340                 
33341                 if(suppressEvent !== true){
33342                     this.fireEvent('check', this, i);
33343                 }
33344             }
33345             
33346         }, this);
33347         
33348         this.validate();
33349     },
33350     
33351     clearInvalid : function(){
33352         
33353         if(!this.el || this.preventMark){
33354             return;
33355         }
33356         
33357         this.el.removeClass([this.invalidClass]);
33358         
33359         this.fireEvent('valid', this);
33360     }
33361     
33362 });
33363
33364 Roo.apply(Roo.bootstrap.RadioSet, {
33365     
33366     groups: {},
33367     
33368     register : function(set)
33369     {
33370         this.groups[set.name] = set;
33371     },
33372     
33373     get: function(name) 
33374     {
33375         if (typeof(this.groups[name]) == 'undefined') {
33376             return false;
33377         }
33378         
33379         return this.groups[name] ;
33380     }
33381     
33382 });
33383 /*
33384  * Based on:
33385  * Ext JS Library 1.1.1
33386  * Copyright(c) 2006-2007, Ext JS, LLC.
33387  *
33388  * Originally Released Under LGPL - original licence link has changed is not relivant.
33389  *
33390  * Fork - LGPL
33391  * <script type="text/javascript">
33392  */
33393
33394
33395 /**
33396  * @class Roo.bootstrap.SplitBar
33397  * @extends Roo.util.Observable
33398  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33399  * <br><br>
33400  * Usage:
33401  * <pre><code>
33402 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33403                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33404 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
33405 split.minSize = 100;
33406 split.maxSize = 600;
33407 split.animate = true;
33408 split.on('moved', splitterMoved);
33409 </code></pre>
33410  * @constructor
33411  * Create a new SplitBar
33412  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
33413  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
33414  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33415  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
33416                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
33417                         position of the SplitBar).
33418  */
33419 Roo.bootstrap.SplitBar = function(cfg){
33420     
33421     /** @private */
33422     
33423     //{
33424     //  dragElement : elm
33425     //  resizingElement: el,
33426         // optional..
33427     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
33428     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
33429         // existingProxy ???
33430     //}
33431     
33432     this.el = Roo.get(cfg.dragElement, true);
33433     this.el.dom.unselectable = "on";
33434     /** @private */
33435     this.resizingEl = Roo.get(cfg.resizingElement, true);
33436
33437     /**
33438      * @private
33439      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33440      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
33441      * @type Number
33442      */
33443     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
33444     
33445     /**
33446      * The minimum size of the resizing element. (Defaults to 0)
33447      * @type Number
33448      */
33449     this.minSize = 0;
33450     
33451     /**
33452      * The maximum size of the resizing element. (Defaults to 2000)
33453      * @type Number
33454      */
33455     this.maxSize = 2000;
33456     
33457     /**
33458      * Whether to animate the transition to the new size
33459      * @type Boolean
33460      */
33461     this.animate = false;
33462     
33463     /**
33464      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
33465      * @type Boolean
33466      */
33467     this.useShim = false;
33468     
33469     /** @private */
33470     this.shim = null;
33471     
33472     if(!cfg.existingProxy){
33473         /** @private */
33474         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
33475     }else{
33476         this.proxy = Roo.get(cfg.existingProxy).dom;
33477     }
33478     /** @private */
33479     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
33480     
33481     /** @private */
33482     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
33483     
33484     /** @private */
33485     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
33486     
33487     /** @private */
33488     this.dragSpecs = {};
33489     
33490     /**
33491      * @private The adapter to use to positon and resize elements
33492      */
33493     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33494     this.adapter.init(this);
33495     
33496     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33497         /** @private */
33498         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
33499         this.el.addClass("roo-splitbar-h");
33500     }else{
33501         /** @private */
33502         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
33503         this.el.addClass("roo-splitbar-v");
33504     }
33505     
33506     this.addEvents({
33507         /**
33508          * @event resize
33509          * Fires when the splitter is moved (alias for {@link #event-moved})
33510          * @param {Roo.bootstrap.SplitBar} this
33511          * @param {Number} newSize the new width or height
33512          */
33513         "resize" : true,
33514         /**
33515          * @event moved
33516          * Fires when the splitter is moved
33517          * @param {Roo.bootstrap.SplitBar} this
33518          * @param {Number} newSize the new width or height
33519          */
33520         "moved" : true,
33521         /**
33522          * @event beforeresize
33523          * Fires before the splitter is dragged
33524          * @param {Roo.bootstrap.SplitBar} this
33525          */
33526         "beforeresize" : true,
33527
33528         "beforeapply" : true
33529     });
33530
33531     Roo.util.Observable.call(this);
33532 };
33533
33534 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
33535     onStartProxyDrag : function(x, y){
33536         this.fireEvent("beforeresize", this);
33537         if(!this.overlay){
33538             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
33539             o.unselectable();
33540             o.enableDisplayMode("block");
33541             // all splitbars share the same overlay
33542             Roo.bootstrap.SplitBar.prototype.overlay = o;
33543         }
33544         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33545         this.overlay.show();
33546         Roo.get(this.proxy).setDisplayed("block");
33547         var size = this.adapter.getElementSize(this);
33548         this.activeMinSize = this.getMinimumSize();;
33549         this.activeMaxSize = this.getMaximumSize();;
33550         var c1 = size - this.activeMinSize;
33551         var c2 = Math.max(this.activeMaxSize - size, 0);
33552         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33553             this.dd.resetConstraints();
33554             this.dd.setXConstraint(
33555                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
33556                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
33557             );
33558             this.dd.setYConstraint(0, 0);
33559         }else{
33560             this.dd.resetConstraints();
33561             this.dd.setXConstraint(0, 0);
33562             this.dd.setYConstraint(
33563                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
33564                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
33565             );
33566          }
33567         this.dragSpecs.startSize = size;
33568         this.dragSpecs.startPoint = [x, y];
33569         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
33570     },
33571     
33572     /** 
33573      * @private Called after the drag operation by the DDProxy
33574      */
33575     onEndProxyDrag : function(e){
33576         Roo.get(this.proxy).setDisplayed(false);
33577         var endPoint = Roo.lib.Event.getXY(e);
33578         if(this.overlay){
33579             this.overlay.hide();
33580         }
33581         var newSize;
33582         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33583             newSize = this.dragSpecs.startSize + 
33584                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
33585                     endPoint[0] - this.dragSpecs.startPoint[0] :
33586                     this.dragSpecs.startPoint[0] - endPoint[0]
33587                 );
33588         }else{
33589             newSize = this.dragSpecs.startSize + 
33590                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
33591                     endPoint[1] - this.dragSpecs.startPoint[1] :
33592                     this.dragSpecs.startPoint[1] - endPoint[1]
33593                 );
33594         }
33595         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
33596         if(newSize != this.dragSpecs.startSize){
33597             if(this.fireEvent('beforeapply', this, newSize) !== false){
33598                 this.adapter.setElementSize(this, newSize);
33599                 this.fireEvent("moved", this, newSize);
33600                 this.fireEvent("resize", this, newSize);
33601             }
33602         }
33603     },
33604     
33605     /**
33606      * Get the adapter this SplitBar uses
33607      * @return The adapter object
33608      */
33609     getAdapter : function(){
33610         return this.adapter;
33611     },
33612     
33613     /**
33614      * Set the adapter this SplitBar uses
33615      * @param {Object} adapter A SplitBar adapter object
33616      */
33617     setAdapter : function(adapter){
33618         this.adapter = adapter;
33619         this.adapter.init(this);
33620     },
33621     
33622     /**
33623      * Gets the minimum size for the resizing element
33624      * @return {Number} The minimum size
33625      */
33626     getMinimumSize : function(){
33627         return this.minSize;
33628     },
33629     
33630     /**
33631      * Sets the minimum size for the resizing element
33632      * @param {Number} minSize The minimum size
33633      */
33634     setMinimumSize : function(minSize){
33635         this.minSize = minSize;
33636     },
33637     
33638     /**
33639      * Gets the maximum size for the resizing element
33640      * @return {Number} The maximum size
33641      */
33642     getMaximumSize : function(){
33643         return this.maxSize;
33644     },
33645     
33646     /**
33647      * Sets the maximum size for the resizing element
33648      * @param {Number} maxSize The maximum size
33649      */
33650     setMaximumSize : function(maxSize){
33651         this.maxSize = maxSize;
33652     },
33653     
33654     /**
33655      * Sets the initialize size for the resizing element
33656      * @param {Number} size The initial size
33657      */
33658     setCurrentSize : function(size){
33659         var oldAnimate = this.animate;
33660         this.animate = false;
33661         this.adapter.setElementSize(this, size);
33662         this.animate = oldAnimate;
33663     },
33664     
33665     /**
33666      * Destroy this splitbar. 
33667      * @param {Boolean} removeEl True to remove the element
33668      */
33669     destroy : function(removeEl){
33670         if(this.shim){
33671             this.shim.remove();
33672         }
33673         this.dd.unreg();
33674         this.proxy.parentNode.removeChild(this.proxy);
33675         if(removeEl){
33676             this.el.remove();
33677         }
33678     }
33679 });
33680
33681 /**
33682  * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
33683  */
33684 Roo.bootstrap.SplitBar.createProxy = function(dir){
33685     var proxy = new Roo.Element(document.createElement("div"));
33686     proxy.unselectable();
33687     var cls = 'roo-splitbar-proxy';
33688     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
33689     document.body.appendChild(proxy.dom);
33690     return proxy.dom;
33691 };
33692
33693 /** 
33694  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
33695  * Default Adapter. It assumes the splitter and resizing element are not positioned
33696  * elements and only gets/sets the width of the element. Generally used for table based layouts.
33697  */
33698 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
33699 };
33700
33701 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
33702     // do nothing for now
33703     init : function(s){
33704     
33705     },
33706     /**
33707      * Called before drag operations to get the current size of the resizing element. 
33708      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33709      */
33710      getElementSize : function(s){
33711         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33712             return s.resizingEl.getWidth();
33713         }else{
33714             return s.resizingEl.getHeight();
33715         }
33716     },
33717     
33718     /**
33719      * Called after drag operations to set the size of the resizing element.
33720      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33721      * @param {Number} newSize The new size to set
33722      * @param {Function} onComplete A function to be invoked when resizing is complete
33723      */
33724     setElementSize : function(s, newSize, onComplete){
33725         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33726             if(!s.animate){
33727                 s.resizingEl.setWidth(newSize);
33728                 if(onComplete){
33729                     onComplete(s, newSize);
33730                 }
33731             }else{
33732                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
33733             }
33734         }else{
33735             
33736             if(!s.animate){
33737                 s.resizingEl.setHeight(newSize);
33738                 if(onComplete){
33739                     onComplete(s, newSize);
33740                 }
33741             }else{
33742                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
33743             }
33744         }
33745     }
33746 };
33747
33748 /** 
33749  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
33750  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
33751  * Adapter that  moves the splitter element to align with the resized sizing element. 
33752  * Used with an absolute positioned SplitBar.
33753  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
33754  * document.body, make sure you assign an id to the body element.
33755  */
33756 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
33757     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33758     this.container = Roo.get(container);
33759 };
33760
33761 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
33762     init : function(s){
33763         this.basic.init(s);
33764     },
33765     
33766     getElementSize : function(s){
33767         return this.basic.getElementSize(s);
33768     },
33769     
33770     setElementSize : function(s, newSize, onComplete){
33771         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
33772     },
33773     
33774     moveSplitter : function(s){
33775         var yes = Roo.bootstrap.SplitBar;
33776         switch(s.placement){
33777             case yes.LEFT:
33778                 s.el.setX(s.resizingEl.getRight());
33779                 break;
33780             case yes.RIGHT:
33781                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
33782                 break;
33783             case yes.TOP:
33784                 s.el.setY(s.resizingEl.getBottom());
33785                 break;
33786             case yes.BOTTOM:
33787                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
33788                 break;
33789         }
33790     }
33791 };
33792
33793 /**
33794  * Orientation constant - Create a vertical SplitBar
33795  * @static
33796  * @type Number
33797  */
33798 Roo.bootstrap.SplitBar.VERTICAL = 1;
33799
33800 /**
33801  * Orientation constant - Create a horizontal SplitBar
33802  * @static
33803  * @type Number
33804  */
33805 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
33806
33807 /**
33808  * Placement constant - The resizing element is to the left of the splitter element
33809  * @static
33810  * @type Number
33811  */
33812 Roo.bootstrap.SplitBar.LEFT = 1;
33813
33814 /**
33815  * Placement constant - The resizing element is to the right of the splitter element
33816  * @static
33817  * @type Number
33818  */
33819 Roo.bootstrap.SplitBar.RIGHT = 2;
33820
33821 /**
33822  * Placement constant - The resizing element is positioned above the splitter element
33823  * @static
33824  * @type Number
33825  */
33826 Roo.bootstrap.SplitBar.TOP = 3;
33827
33828 /**
33829  * Placement constant - The resizing element is positioned under splitter element
33830  * @static
33831  * @type Number
33832  */
33833 Roo.bootstrap.SplitBar.BOTTOM = 4;
33834 Roo.namespace("Roo.bootstrap.layout");/*
33835  * Based on:
33836  * Ext JS Library 1.1.1
33837  * Copyright(c) 2006-2007, Ext JS, LLC.
33838  *
33839  * Originally Released Under LGPL - original licence link has changed is not relivant.
33840  *
33841  * Fork - LGPL
33842  * <script type="text/javascript">
33843  */
33844
33845 /**
33846  * @class Roo.bootstrap.layout.Manager
33847  * @extends Roo.bootstrap.Component
33848  * Base class for layout managers.
33849  */
33850 Roo.bootstrap.layout.Manager = function(config)
33851 {
33852     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
33853
33854
33855
33856
33857
33858     /** false to disable window resize monitoring @type Boolean */
33859     this.monitorWindowResize = true;
33860     this.regions = {};
33861     this.addEvents({
33862         /**
33863          * @event layout
33864          * Fires when a layout is performed.
33865          * @param {Roo.LayoutManager} this
33866          */
33867         "layout" : true,
33868         /**
33869          * @event regionresized
33870          * Fires when the user resizes a region.
33871          * @param {Roo.LayoutRegion} region The resized region
33872          * @param {Number} newSize The new size (width for east/west, height for north/south)
33873          */
33874         "regionresized" : true,
33875         /**
33876          * @event regioncollapsed
33877          * Fires when a region is collapsed.
33878          * @param {Roo.LayoutRegion} region The collapsed region
33879          */
33880         "regioncollapsed" : true,
33881         /**
33882          * @event regionexpanded
33883          * Fires when a region is expanded.
33884          * @param {Roo.LayoutRegion} region The expanded region
33885          */
33886         "regionexpanded" : true
33887     });
33888     this.updating = false;
33889
33890     if (config.el) {
33891         this.el = Roo.get(config.el);
33892         this.initEvents();
33893     }
33894
33895 };
33896
33897 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
33898
33899
33900     regions : null,
33901
33902     monitorWindowResize : true,
33903
33904
33905     updating : false,
33906
33907
33908     onRender : function(ct, position)
33909     {
33910         if(!this.el){
33911             this.el = Roo.get(ct);
33912             this.initEvents();
33913         }
33914         //this.fireEvent('render',this);
33915     },
33916
33917
33918     initEvents: function()
33919     {
33920
33921
33922         // ie scrollbar fix
33923         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
33924             document.body.scroll = "no";
33925         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
33926             this.el.position('relative');
33927         }
33928         this.id = this.el.id;
33929         this.el.addClass("roo-layout-container");
33930         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
33931         if(this.el.dom != document.body ) {
33932             this.el.on('resize', this.layout,this);
33933             this.el.on('show', this.layout,this);
33934         }
33935
33936     },
33937
33938     /**
33939      * Returns true if this layout is currently being updated
33940      * @return {Boolean}
33941      */
33942     isUpdating : function(){
33943         return this.updating;
33944     },
33945
33946     /**
33947      * Suspend the LayoutManager from doing auto-layouts while
33948      * making multiple add or remove calls
33949      */
33950     beginUpdate : function(){
33951         this.updating = true;
33952     },
33953
33954     /**
33955      * Restore auto-layouts and optionally disable the manager from performing a layout
33956      * @param {Boolean} noLayout true to disable a layout update
33957      */
33958     endUpdate : function(noLayout){
33959         this.updating = false;
33960         if(!noLayout){
33961             this.layout();
33962         }
33963     },
33964
33965     layout: function(){
33966         // abstract...
33967     },
33968
33969     onRegionResized : function(region, newSize){
33970         this.fireEvent("regionresized", region, newSize);
33971         this.layout();
33972     },
33973
33974     onRegionCollapsed : function(region){
33975         this.fireEvent("regioncollapsed", region);
33976     },
33977
33978     onRegionExpanded : function(region){
33979         this.fireEvent("regionexpanded", region);
33980     },
33981
33982     /**
33983      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
33984      * performs box-model adjustments.
33985      * @return {Object} The size as an object {width: (the width), height: (the height)}
33986      */
33987     getViewSize : function()
33988     {
33989         var size;
33990         if(this.el.dom != document.body){
33991             size = this.el.getSize();
33992         }else{
33993             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
33994         }
33995         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
33996         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
33997         return size;
33998     },
33999
34000     /**
34001      * Returns the Element this layout is bound to.
34002      * @return {Roo.Element}
34003      */
34004     getEl : function(){
34005         return this.el;
34006     },
34007
34008     /**
34009      * Returns the specified region.
34010      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34011      * @return {Roo.LayoutRegion}
34012      */
34013     getRegion : function(target){
34014         return this.regions[target.toLowerCase()];
34015     },
34016
34017     onWindowResize : function(){
34018         if(this.monitorWindowResize){
34019             this.layout();
34020         }
34021     }
34022 });
34023 /*
34024  * Based on:
34025  * Ext JS Library 1.1.1
34026  * Copyright(c) 2006-2007, Ext JS, LLC.
34027  *
34028  * Originally Released Under LGPL - original licence link has changed is not relivant.
34029  *
34030  * Fork - LGPL
34031  * <script type="text/javascript">
34032  */
34033 /**
34034  * @class Roo.bootstrap.layout.Border
34035  * @extends Roo.bootstrap.layout.Manager
34036  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34037  * please see: examples/bootstrap/nested.html<br><br>
34038  
34039 <b>The container the layout is rendered into can be either the body element or any other element.
34040 If it is not the body element, the container needs to either be an absolute positioned element,
34041 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34042 the container size if it is not the body element.</b>
34043
34044 * @constructor
34045 * Create a new Border
34046 * @param {Object} config Configuration options
34047  */
34048 Roo.bootstrap.layout.Border = function(config){
34049     config = config || {};
34050     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34051     
34052     
34053     
34054     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34055         if(config[region]){
34056             config[region].region = region;
34057             this.addRegion(config[region]);
34058         }
34059     },this);
34060     
34061 };
34062
34063 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34064
34065 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34066     /**
34067      * Creates and adds a new region if it doesn't already exist.
34068      * @param {String} target The target region key (north, south, east, west or center).
34069      * @param {Object} config The regions config object
34070      * @return {BorderLayoutRegion} The new region
34071      */
34072     addRegion : function(config)
34073     {
34074         if(!this.regions[config.region]){
34075             var r = this.factory(config);
34076             this.bindRegion(r);
34077         }
34078         return this.regions[config.region];
34079     },
34080
34081     // private (kinda)
34082     bindRegion : function(r){
34083         this.regions[r.config.region] = r;
34084         
34085         r.on("visibilitychange",    this.layout, this);
34086         r.on("paneladded",          this.layout, this);
34087         r.on("panelremoved",        this.layout, this);
34088         r.on("invalidated",         this.layout, this);
34089         r.on("resized",             this.onRegionResized, this);
34090         r.on("collapsed",           this.onRegionCollapsed, this);
34091         r.on("expanded",            this.onRegionExpanded, this);
34092     },
34093
34094     /**
34095      * Performs a layout update.
34096      */
34097     layout : function()
34098     {
34099         if(this.updating) {
34100             return;
34101         }
34102         
34103         // render all the rebions if they have not been done alreayd?
34104         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34105             if(this.regions[region] && !this.regions[region].bodyEl){
34106                 this.regions[region].onRender(this.el)
34107             }
34108         },this);
34109         
34110         var size = this.getViewSize();
34111         var w = size.width;
34112         var h = size.height;
34113         var centerW = w;
34114         var centerH = h;
34115         var centerY = 0;
34116         var centerX = 0;
34117         //var x = 0, y = 0;
34118
34119         var rs = this.regions;
34120         var north = rs["north"];
34121         var south = rs["south"]; 
34122         var west = rs["west"];
34123         var east = rs["east"];
34124         var center = rs["center"];
34125         //if(this.hideOnLayout){ // not supported anymore
34126             //c.el.setStyle("display", "none");
34127         //}
34128         if(north && north.isVisible()){
34129             var b = north.getBox();
34130             var m = north.getMargins();
34131             b.width = w - (m.left+m.right);
34132             b.x = m.left;
34133             b.y = m.top;
34134             centerY = b.height + b.y + m.bottom;
34135             centerH -= centerY;
34136             north.updateBox(this.safeBox(b));
34137         }
34138         if(south && south.isVisible()){
34139             var b = south.getBox();
34140             var m = south.getMargins();
34141             b.width = w - (m.left+m.right);
34142             b.x = m.left;
34143             var totalHeight = (b.height + m.top + m.bottom);
34144             b.y = h - totalHeight + m.top;
34145             centerH -= totalHeight;
34146             south.updateBox(this.safeBox(b));
34147         }
34148         if(west && west.isVisible()){
34149             var b = west.getBox();
34150             var m = west.getMargins();
34151             b.height = centerH - (m.top+m.bottom);
34152             b.x = m.left;
34153             b.y = centerY + m.top;
34154             var totalWidth = (b.width + m.left + m.right);
34155             centerX += totalWidth;
34156             centerW -= totalWidth;
34157             west.updateBox(this.safeBox(b));
34158         }
34159         if(east && east.isVisible()){
34160             var b = east.getBox();
34161             var m = east.getMargins();
34162             b.height = centerH - (m.top+m.bottom);
34163             var totalWidth = (b.width + m.left + m.right);
34164             b.x = w - totalWidth + m.left;
34165             b.y = centerY + m.top;
34166             centerW -= totalWidth;
34167             east.updateBox(this.safeBox(b));
34168         }
34169         if(center){
34170             var m = center.getMargins();
34171             var centerBox = {
34172                 x: centerX + m.left,
34173                 y: centerY + m.top,
34174                 width: centerW - (m.left+m.right),
34175                 height: centerH - (m.top+m.bottom)
34176             };
34177             //if(this.hideOnLayout){
34178                 //center.el.setStyle("display", "block");
34179             //}
34180             center.updateBox(this.safeBox(centerBox));
34181         }
34182         this.el.repaint();
34183         this.fireEvent("layout", this);
34184     },
34185
34186     // private
34187     safeBox : function(box){
34188         box.width = Math.max(0, box.width);
34189         box.height = Math.max(0, box.height);
34190         return box;
34191     },
34192
34193     /**
34194      * Adds a ContentPanel (or subclass) to this layout.
34195      * @param {String} target The target region key (north, south, east, west or center).
34196      * @param {Roo.ContentPanel} panel The panel to add
34197      * @return {Roo.ContentPanel} The added panel
34198      */
34199     add : function(target, panel){
34200          
34201         target = target.toLowerCase();
34202         return this.regions[target].add(panel);
34203     },
34204
34205     /**
34206      * Remove a ContentPanel (or subclass) to this layout.
34207      * @param {String} target The target region key (north, south, east, west or center).
34208      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34209      * @return {Roo.ContentPanel} The removed panel
34210      */
34211     remove : function(target, panel){
34212         target = target.toLowerCase();
34213         return this.regions[target].remove(panel);
34214     },
34215
34216     /**
34217      * Searches all regions for a panel with the specified id
34218      * @param {String} panelId
34219      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34220      */
34221     findPanel : function(panelId){
34222         var rs = this.regions;
34223         for(var target in rs){
34224             if(typeof rs[target] != "function"){
34225                 var p = rs[target].getPanel(panelId);
34226                 if(p){
34227                     return p;
34228                 }
34229             }
34230         }
34231         return null;
34232     },
34233
34234     /**
34235      * Searches all regions for a panel with the specified id and activates (shows) it.
34236      * @param {String/ContentPanel} panelId The panels id or the panel itself
34237      * @return {Roo.ContentPanel} The shown panel or null
34238      */
34239     showPanel : function(panelId) {
34240       var rs = this.regions;
34241       for(var target in rs){
34242          var r = rs[target];
34243          if(typeof r != "function"){
34244             if(r.hasPanel(panelId)){
34245                return r.showPanel(panelId);
34246             }
34247          }
34248       }
34249       return null;
34250    },
34251
34252    /**
34253      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34254      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34255      */
34256    /*
34257     restoreState : function(provider){
34258         if(!provider){
34259             provider = Roo.state.Manager;
34260         }
34261         var sm = new Roo.LayoutStateManager();
34262         sm.init(this, provider);
34263     },
34264 */
34265  
34266  
34267     /**
34268      * Adds a xtype elements to the layout.
34269      * <pre><code>
34270
34271 layout.addxtype({
34272        xtype : 'ContentPanel',
34273        region: 'west',
34274        items: [ .... ]
34275    }
34276 );
34277
34278 layout.addxtype({
34279         xtype : 'NestedLayoutPanel',
34280         region: 'west',
34281         layout: {
34282            center: { },
34283            west: { }   
34284         },
34285         items : [ ... list of content panels or nested layout panels.. ]
34286    }
34287 );
34288 </code></pre>
34289      * @param {Object} cfg Xtype definition of item to add.
34290      */
34291     addxtype : function(cfg)
34292     {
34293         // basically accepts a pannel...
34294         // can accept a layout region..!?!?
34295         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34296         
34297         
34298         // theory?  children can only be panels??
34299         
34300         //if (!cfg.xtype.match(/Panel$/)) {
34301         //    return false;
34302         //}
34303         var ret = false;
34304         
34305         if (typeof(cfg.region) == 'undefined') {
34306             Roo.log("Failed to add Panel, region was not set");
34307             Roo.log(cfg);
34308             return false;
34309         }
34310         var region = cfg.region;
34311         delete cfg.region;
34312         
34313           
34314         var xitems = [];
34315         if (cfg.items) {
34316             xitems = cfg.items;
34317             delete cfg.items;
34318         }
34319         var nb = false;
34320         
34321         switch(cfg.xtype) 
34322         {
34323             case 'Content':  // ContentPanel (el, cfg)
34324             case 'Scroll':  // ContentPanel (el, cfg)
34325             case 'View': 
34326                 cfg.autoCreate = true;
34327                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34328                 //} else {
34329                 //    var el = this.el.createChild();
34330                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34331                 //}
34332                 
34333                 this.add(region, ret);
34334                 break;
34335             
34336             /*
34337             case 'TreePanel': // our new panel!
34338                 cfg.el = this.el.createChild();
34339                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34340                 this.add(region, ret);
34341                 break;
34342             */
34343             
34344             case 'Nest': 
34345                 // create a new Layout (which is  a Border Layout...
34346                 
34347                 var clayout = cfg.layout;
34348                 clayout.el  = this.el.createChild();
34349                 clayout.items   = clayout.items  || [];
34350                 
34351                 delete cfg.layout;
34352                 
34353                 // replace this exitems with the clayout ones..
34354                 xitems = clayout.items;
34355                  
34356                 // force background off if it's in center...
34357                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34358                     cfg.background = false;
34359                 }
34360                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
34361                 
34362                 
34363                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34364                 //console.log('adding nested layout panel '  + cfg.toSource());
34365                 this.add(region, ret);
34366                 nb = {}; /// find first...
34367                 break;
34368             
34369             case 'Grid':
34370                 
34371                 // needs grid and region
34372                 
34373                 //var el = this.getRegion(region).el.createChild();
34374                 /*
34375                  *var el = this.el.createChild();
34376                 // create the grid first...
34377                 cfg.grid.container = el;
34378                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34379                 */
34380                 
34381                 if (region == 'center' && this.active ) {
34382                     cfg.background = false;
34383                 }
34384                 
34385                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34386                 
34387                 this.add(region, ret);
34388                 /*
34389                 if (cfg.background) {
34390                     // render grid on panel activation (if panel background)
34391                     ret.on('activate', function(gp) {
34392                         if (!gp.grid.rendered) {
34393                     //        gp.grid.render(el);
34394                         }
34395                     });
34396                 } else {
34397                   //  cfg.grid.render(el);
34398                 }
34399                 */
34400                 break;
34401            
34402            
34403             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34404                 // it was the old xcomponent building that caused this before.
34405                 // espeically if border is the top element in the tree.
34406                 ret = this;
34407                 break; 
34408                 
34409                     
34410                 
34411                 
34412                 
34413             default:
34414                 /*
34415                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34416                     
34417                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34418                     this.add(region, ret);
34419                 } else {
34420                 */
34421                     Roo.log(cfg);
34422                     throw "Can not add '" + cfg.xtype + "' to Border";
34423                     return null;
34424              
34425                                 
34426              
34427         }
34428         this.beginUpdate();
34429         // add children..
34430         var region = '';
34431         var abn = {};
34432         Roo.each(xitems, function(i)  {
34433             region = nb && i.region ? i.region : false;
34434             
34435             var add = ret.addxtype(i);
34436            
34437             if (region) {
34438                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
34439                 if (!i.background) {
34440                     abn[region] = nb[region] ;
34441                 }
34442             }
34443             
34444         });
34445         this.endUpdate();
34446
34447         // make the last non-background panel active..
34448         //if (nb) { Roo.log(abn); }
34449         if (nb) {
34450             
34451             for(var r in abn) {
34452                 region = this.getRegion(r);
34453                 if (region) {
34454                     // tried using nb[r], but it does not work..
34455                      
34456                     region.showPanel(abn[r]);
34457                    
34458                 }
34459             }
34460         }
34461         return ret;
34462         
34463     },
34464     
34465     
34466 // private
34467     factory : function(cfg)
34468     {
34469         
34470         var validRegions = Roo.bootstrap.layout.Border.regions;
34471
34472         var target = cfg.region;
34473         cfg.mgr = this;
34474         
34475         var r = Roo.bootstrap.layout;
34476         Roo.log(target);
34477         switch(target){
34478             case "north":
34479                 return new r.North(cfg);
34480             case "south":
34481                 return new r.South(cfg);
34482             case "east":
34483                 return new r.East(cfg);
34484             case "west":
34485                 return new r.West(cfg);
34486             case "center":
34487                 return new r.Center(cfg);
34488         }
34489         throw 'Layout region "'+target+'" not supported.';
34490     }
34491     
34492     
34493 });
34494  /*
34495  * Based on:
34496  * Ext JS Library 1.1.1
34497  * Copyright(c) 2006-2007, Ext JS, LLC.
34498  *
34499  * Originally Released Under LGPL - original licence link has changed is not relivant.
34500  *
34501  * Fork - LGPL
34502  * <script type="text/javascript">
34503  */
34504  
34505 /**
34506  * @class Roo.bootstrap.layout.Basic
34507  * @extends Roo.util.Observable
34508  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
34509  * and does not have a titlebar, tabs or any other features. All it does is size and position 
34510  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
34511  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34512  * @cfg {string}   region  the region that it inhabits..
34513  * @cfg {bool}   skipConfig skip config?
34514  * 
34515
34516  */
34517 Roo.bootstrap.layout.Basic = function(config){
34518     
34519     this.mgr = config.mgr;
34520     
34521     this.position = config.region;
34522     
34523     var skipConfig = config.skipConfig;
34524     
34525     this.events = {
34526         /**
34527          * @scope Roo.BasicLayoutRegion
34528          */
34529         
34530         /**
34531          * @event beforeremove
34532          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
34533          * @param {Roo.LayoutRegion} this
34534          * @param {Roo.ContentPanel} panel The panel
34535          * @param {Object} e The cancel event object
34536          */
34537         "beforeremove" : true,
34538         /**
34539          * @event invalidated
34540          * Fires when the layout for this region is changed.
34541          * @param {Roo.LayoutRegion} this
34542          */
34543         "invalidated" : true,
34544         /**
34545          * @event visibilitychange
34546          * Fires when this region is shown or hidden 
34547          * @param {Roo.LayoutRegion} this
34548          * @param {Boolean} visibility true or false
34549          */
34550         "visibilitychange" : true,
34551         /**
34552          * @event paneladded
34553          * Fires when a panel is added. 
34554          * @param {Roo.LayoutRegion} this
34555          * @param {Roo.ContentPanel} panel The panel
34556          */
34557         "paneladded" : true,
34558         /**
34559          * @event panelremoved
34560          * Fires when a panel is removed. 
34561          * @param {Roo.LayoutRegion} this
34562          * @param {Roo.ContentPanel} panel The panel
34563          */
34564         "panelremoved" : true,
34565         /**
34566          * @event beforecollapse
34567          * Fires when this region before collapse.
34568          * @param {Roo.LayoutRegion} this
34569          */
34570         "beforecollapse" : true,
34571         /**
34572          * @event collapsed
34573          * Fires when this region is collapsed.
34574          * @param {Roo.LayoutRegion} this
34575          */
34576         "collapsed" : true,
34577         /**
34578          * @event expanded
34579          * Fires when this region is expanded.
34580          * @param {Roo.LayoutRegion} this
34581          */
34582         "expanded" : true,
34583         /**
34584          * @event slideshow
34585          * Fires when this region is slid into view.
34586          * @param {Roo.LayoutRegion} this
34587          */
34588         "slideshow" : true,
34589         /**
34590          * @event slidehide
34591          * Fires when this region slides out of view. 
34592          * @param {Roo.LayoutRegion} this
34593          */
34594         "slidehide" : true,
34595         /**
34596          * @event panelactivated
34597          * Fires when a panel is activated. 
34598          * @param {Roo.LayoutRegion} this
34599          * @param {Roo.ContentPanel} panel The activated panel
34600          */
34601         "panelactivated" : true,
34602         /**
34603          * @event resized
34604          * Fires when the user resizes this region. 
34605          * @param {Roo.LayoutRegion} this
34606          * @param {Number} newSize The new size (width for east/west, height for north/south)
34607          */
34608         "resized" : true
34609     };
34610     /** A collection of panels in this region. @type Roo.util.MixedCollection */
34611     this.panels = new Roo.util.MixedCollection();
34612     this.panels.getKey = this.getPanelId.createDelegate(this);
34613     this.box = null;
34614     this.activePanel = null;
34615     // ensure listeners are added...
34616     
34617     if (config.listeners || config.events) {
34618         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
34619             listeners : config.listeners || {},
34620             events : config.events || {}
34621         });
34622     }
34623     
34624     if(skipConfig !== true){
34625         this.applyConfig(config);
34626     }
34627 };
34628
34629 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
34630 {
34631     getPanelId : function(p){
34632         return p.getId();
34633     },
34634     
34635     applyConfig : function(config){
34636         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34637         this.config = config;
34638         
34639     },
34640     
34641     /**
34642      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
34643      * the width, for horizontal (north, south) the height.
34644      * @param {Number} newSize The new width or height
34645      */
34646     resizeTo : function(newSize){
34647         var el = this.el ? this.el :
34648                  (this.activePanel ? this.activePanel.getEl() : null);
34649         if(el){
34650             switch(this.position){
34651                 case "east":
34652                 case "west":
34653                     el.setWidth(newSize);
34654                     this.fireEvent("resized", this, newSize);
34655                 break;
34656                 case "north":
34657                 case "south":
34658                     el.setHeight(newSize);
34659                     this.fireEvent("resized", this, newSize);
34660                 break;                
34661             }
34662         }
34663     },
34664     
34665     getBox : function(){
34666         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
34667     },
34668     
34669     getMargins : function(){
34670         return this.margins;
34671     },
34672     
34673     updateBox : function(box){
34674         this.box = box;
34675         var el = this.activePanel.getEl();
34676         el.dom.style.left = box.x + "px";
34677         el.dom.style.top = box.y + "px";
34678         this.activePanel.setSize(box.width, box.height);
34679     },
34680     
34681     /**
34682      * Returns the container element for this region.
34683      * @return {Roo.Element}
34684      */
34685     getEl : function(){
34686         return this.activePanel;
34687     },
34688     
34689     /**
34690      * Returns true if this region is currently visible.
34691      * @return {Boolean}
34692      */
34693     isVisible : function(){
34694         return this.activePanel ? true : false;
34695     },
34696     
34697     setActivePanel : function(panel){
34698         panel = this.getPanel(panel);
34699         if(this.activePanel && this.activePanel != panel){
34700             this.activePanel.setActiveState(false);
34701             this.activePanel.getEl().setLeftTop(-10000,-10000);
34702         }
34703         this.activePanel = panel;
34704         panel.setActiveState(true);
34705         if(this.box){
34706             panel.setSize(this.box.width, this.box.height);
34707         }
34708         this.fireEvent("panelactivated", this, panel);
34709         this.fireEvent("invalidated");
34710     },
34711     
34712     /**
34713      * Show the specified panel.
34714      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
34715      * @return {Roo.ContentPanel} The shown panel or null
34716      */
34717     showPanel : function(panel){
34718         panel = this.getPanel(panel);
34719         if(panel){
34720             this.setActivePanel(panel);
34721         }
34722         return panel;
34723     },
34724     
34725     /**
34726      * Get the active panel for this region.
34727      * @return {Roo.ContentPanel} The active panel or null
34728      */
34729     getActivePanel : function(){
34730         return this.activePanel;
34731     },
34732     
34733     /**
34734      * Add the passed ContentPanel(s)
34735      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
34736      * @return {Roo.ContentPanel} The panel added (if only one was added)
34737      */
34738     add : function(panel){
34739         if(arguments.length > 1){
34740             for(var i = 0, len = arguments.length; i < len; i++) {
34741                 this.add(arguments[i]);
34742             }
34743             return null;
34744         }
34745         if(this.hasPanel(panel)){
34746             this.showPanel(panel);
34747             return panel;
34748         }
34749         var el = panel.getEl();
34750         if(el.dom.parentNode != this.mgr.el.dom){
34751             this.mgr.el.dom.appendChild(el.dom);
34752         }
34753         if(panel.setRegion){
34754             panel.setRegion(this);
34755         }
34756         this.panels.add(panel);
34757         el.setStyle("position", "absolute");
34758         if(!panel.background){
34759             this.setActivePanel(panel);
34760             if(this.config.initialSize && this.panels.getCount()==1){
34761                 this.resizeTo(this.config.initialSize);
34762             }
34763         }
34764         this.fireEvent("paneladded", this, panel);
34765         return panel;
34766     },
34767     
34768     /**
34769      * Returns true if the panel is in this region.
34770      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34771      * @return {Boolean}
34772      */
34773     hasPanel : function(panel){
34774         if(typeof panel == "object"){ // must be panel obj
34775             panel = panel.getId();
34776         }
34777         return this.getPanel(panel) ? true : false;
34778     },
34779     
34780     /**
34781      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
34782      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34783      * @param {Boolean} preservePanel Overrides the config preservePanel option
34784      * @return {Roo.ContentPanel} The panel that was removed
34785      */
34786     remove : function(panel, preservePanel){
34787         panel = this.getPanel(panel);
34788         if(!panel){
34789             return null;
34790         }
34791         var e = {};
34792         this.fireEvent("beforeremove", this, panel, e);
34793         if(e.cancel === true){
34794             return null;
34795         }
34796         var panelId = panel.getId();
34797         this.panels.removeKey(panelId);
34798         return panel;
34799     },
34800     
34801     /**
34802      * Returns the panel specified or null if it's not in this region.
34803      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
34804      * @return {Roo.ContentPanel}
34805      */
34806     getPanel : function(id){
34807         if(typeof id == "object"){ // must be panel obj
34808             return id;
34809         }
34810         return this.panels.get(id);
34811     },
34812     
34813     /**
34814      * Returns this regions position (north/south/east/west/center).
34815      * @return {String} 
34816      */
34817     getPosition: function(){
34818         return this.position;    
34819     }
34820 });/*
34821  * Based on:
34822  * Ext JS Library 1.1.1
34823  * Copyright(c) 2006-2007, Ext JS, LLC.
34824  *
34825  * Originally Released Under LGPL - original licence link has changed is not relivant.
34826  *
34827  * Fork - LGPL
34828  * <script type="text/javascript">
34829  */
34830  
34831 /**
34832  * @class Roo.bootstrap.layout.Region
34833  * @extends Roo.bootstrap.layout.Basic
34834  * This class represents a region in a layout manager.
34835  
34836  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
34837  * @cfg {Object}    cmargins        Margins for the element when collapsed (defaults to: north/south {top: 2, left: 0, right:0, bottom: 2} or east/west {top: 0, left: 2, right:2, bottom: 0})
34838  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
34839  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
34840  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
34841  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
34842  * @cfg {String}    title           The title for the region (overrides panel titles)
34843  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
34844  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
34845  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
34846  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
34847  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
34848  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
34849  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
34850  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
34851  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
34852  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
34853
34854  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
34855  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
34856  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
34857  * @cfg {Number}    width           For East/West panels
34858  * @cfg {Number}    height          For North/South panels
34859  * @cfg {Boolean}   split           To show the splitter
34860  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
34861  * 
34862  * @cfg {string}   cls             Extra CSS classes to add to region
34863  * 
34864  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34865  * @cfg {string}   region  the region that it inhabits..
34866  *
34867
34868  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
34869  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
34870
34871  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
34872  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
34873  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
34874  */
34875 Roo.bootstrap.layout.Region = function(config)
34876 {
34877     this.applyConfig(config);
34878
34879     var mgr = config.mgr;
34880     var pos = config.region;
34881     config.skipConfig = true;
34882     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
34883     
34884     if (mgr.el) {
34885         this.onRender(mgr.el);   
34886     }
34887      
34888     this.visible = true;
34889     this.collapsed = false;
34890     this.unrendered_panels = [];
34891 };
34892
34893 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
34894
34895     position: '', // set by wrapper (eg. north/south etc..)
34896     unrendered_panels : null,  // unrendered panels.
34897     createBody : function(){
34898         /** This region's body element 
34899         * @type Roo.Element */
34900         this.bodyEl = this.el.createChild({
34901                 tag: "div",
34902                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
34903         });
34904     },
34905
34906     onRender: function(ctr, pos)
34907     {
34908         var dh = Roo.DomHelper;
34909         /** This region's container element 
34910         * @type Roo.Element */
34911         this.el = dh.append(ctr.dom, {
34912                 tag: "div",
34913                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
34914             }, true);
34915         /** This region's title element 
34916         * @type Roo.Element */
34917     
34918         this.titleEl = dh.append(this.el.dom,
34919             {
34920                     tag: "div",
34921                     unselectable: "on",
34922                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
34923                     children:[
34924                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
34925                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
34926                     ]}, true);
34927         
34928         this.titleEl.enableDisplayMode();
34929         /** This region's title text element 
34930         * @type HTMLElement */
34931         this.titleTextEl = this.titleEl.dom.firstChild;
34932         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
34933         /*
34934         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
34935         this.closeBtn.enableDisplayMode();
34936         this.closeBtn.on("click", this.closeClicked, this);
34937         this.closeBtn.hide();
34938     */
34939         this.createBody(this.config);
34940         if(this.config.hideWhenEmpty){
34941             this.hide();
34942             this.on("paneladded", this.validateVisibility, this);
34943             this.on("panelremoved", this.validateVisibility, this);
34944         }
34945         if(this.autoScroll){
34946             this.bodyEl.setStyle("overflow", "auto");
34947         }else{
34948             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
34949         }
34950         //if(c.titlebar !== false){
34951             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
34952                 this.titleEl.hide();
34953             }else{
34954                 this.titleEl.show();
34955                 if(this.config.title){
34956                     this.titleTextEl.innerHTML = this.config.title;
34957                 }
34958             }
34959         //}
34960         if(this.config.collapsed){
34961             this.collapse(true);
34962         }
34963         if(this.config.hidden){
34964             this.hide();
34965         }
34966         
34967         if (this.unrendered_panels && this.unrendered_panels.length) {
34968             for (var i =0;i< this.unrendered_panels.length; i++) {
34969                 this.add(this.unrendered_panels[i]);
34970             }
34971             this.unrendered_panels = null;
34972             
34973         }
34974         
34975     },
34976     
34977     applyConfig : function(c)
34978     {
34979         /*
34980          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
34981             var dh = Roo.DomHelper;
34982             if(c.titlebar !== false){
34983                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
34984                 this.collapseBtn.on("click", this.collapse, this);
34985                 this.collapseBtn.enableDisplayMode();
34986                 /*
34987                 if(c.showPin === true || this.showPin){
34988                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
34989                     this.stickBtn.enableDisplayMode();
34990                     this.stickBtn.on("click", this.expand, this);
34991                     this.stickBtn.hide();
34992                 }
34993                 
34994             }
34995             */
34996             /** This region's collapsed element
34997             * @type Roo.Element */
34998             /*
34999              *
35000             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35001                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35002             ]}, true);
35003             
35004             if(c.floatable !== false){
35005                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35006                this.collapsedEl.on("click", this.collapseClick, this);
35007             }
35008
35009             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35010                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35011                    id: "message", unselectable: "on", style:{"float":"left"}});
35012                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35013              }
35014             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35015             this.expandBtn.on("click", this.expand, this);
35016             
35017         }
35018         
35019         if(this.collapseBtn){
35020             this.collapseBtn.setVisible(c.collapsible == true);
35021         }
35022         
35023         this.cmargins = c.cmargins || this.cmargins ||
35024                          (this.position == "west" || this.position == "east" ?
35025                              {top: 0, left: 2, right:2, bottom: 0} :
35026                              {top: 2, left: 0, right:0, bottom: 2});
35027         */
35028         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35029         
35030         
35031         this.bottomTabs = c.tabPosition != "top";
35032         
35033         this.autoScroll = c.autoScroll || false;
35034         
35035         
35036        
35037         
35038         this.duration = c.duration || .30;
35039         this.slideDuration = c.slideDuration || .45;
35040         this.config = c;
35041        
35042     },
35043     /**
35044      * Returns true if this region is currently visible.
35045      * @return {Boolean}
35046      */
35047     isVisible : function(){
35048         return this.visible;
35049     },
35050
35051     /**
35052      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35053      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35054      */
35055     //setCollapsedTitle : function(title){
35056     //    title = title || "&#160;";
35057      //   if(this.collapsedTitleTextEl){
35058       //      this.collapsedTitleTextEl.innerHTML = title;
35059        // }
35060     //},
35061
35062     getBox : function(){
35063         var b;
35064       //  if(!this.collapsed){
35065             b = this.el.getBox(false, true);
35066        // }else{
35067           //  b = this.collapsedEl.getBox(false, true);
35068         //}
35069         return b;
35070     },
35071
35072     getMargins : function(){
35073         return this.margins;
35074         //return this.collapsed ? this.cmargins : this.margins;
35075     },
35076 /*
35077     highlight : function(){
35078         this.el.addClass("x-layout-panel-dragover");
35079     },
35080
35081     unhighlight : function(){
35082         this.el.removeClass("x-layout-panel-dragover");
35083     },
35084 */
35085     updateBox : function(box)
35086     {
35087         if (!this.bodyEl) {
35088             return; // not rendered yet..
35089         }
35090         
35091         this.box = box;
35092         if(!this.collapsed){
35093             this.el.dom.style.left = box.x + "px";
35094             this.el.dom.style.top = box.y + "px";
35095             this.updateBody(box.width, box.height);
35096         }else{
35097             this.collapsedEl.dom.style.left = box.x + "px";
35098             this.collapsedEl.dom.style.top = box.y + "px";
35099             this.collapsedEl.setSize(box.width, box.height);
35100         }
35101         if(this.tabs){
35102             this.tabs.autoSizeTabs();
35103         }
35104     },
35105
35106     updateBody : function(w, h)
35107     {
35108         if(w !== null){
35109             this.el.setWidth(w);
35110             w -= this.el.getBorderWidth("rl");
35111             if(this.config.adjustments){
35112                 w += this.config.adjustments[0];
35113             }
35114         }
35115         if(h !== null && h > 0){
35116             this.el.setHeight(h);
35117             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35118             h -= this.el.getBorderWidth("tb");
35119             if(this.config.adjustments){
35120                 h += this.config.adjustments[1];
35121             }
35122             this.bodyEl.setHeight(h);
35123             if(this.tabs){
35124                 h = this.tabs.syncHeight(h);
35125             }
35126         }
35127         if(this.panelSize){
35128             w = w !== null ? w : this.panelSize.width;
35129             h = h !== null ? h : this.panelSize.height;
35130         }
35131         if(this.activePanel){
35132             var el = this.activePanel.getEl();
35133             w = w !== null ? w : el.getWidth();
35134             h = h !== null ? h : el.getHeight();
35135             this.panelSize = {width: w, height: h};
35136             this.activePanel.setSize(w, h);
35137         }
35138         if(Roo.isIE && this.tabs){
35139             this.tabs.el.repaint();
35140         }
35141     },
35142
35143     /**
35144      * Returns the container element for this region.
35145      * @return {Roo.Element}
35146      */
35147     getEl : function(){
35148         return this.el;
35149     },
35150
35151     /**
35152      * Hides this region.
35153      */
35154     hide : function(){
35155         //if(!this.collapsed){
35156             this.el.dom.style.left = "-2000px";
35157             this.el.hide();
35158         //}else{
35159          //   this.collapsedEl.dom.style.left = "-2000px";
35160          //   this.collapsedEl.hide();
35161        // }
35162         this.visible = false;
35163         this.fireEvent("visibilitychange", this, false);
35164     },
35165
35166     /**
35167      * Shows this region if it was previously hidden.
35168      */
35169     show : function(){
35170         //if(!this.collapsed){
35171             this.el.show();
35172         //}else{
35173         //    this.collapsedEl.show();
35174        // }
35175         this.visible = true;
35176         this.fireEvent("visibilitychange", this, true);
35177     },
35178 /*
35179     closeClicked : function(){
35180         if(this.activePanel){
35181             this.remove(this.activePanel);
35182         }
35183     },
35184
35185     collapseClick : function(e){
35186         if(this.isSlid){
35187            e.stopPropagation();
35188            this.slideIn();
35189         }else{
35190            e.stopPropagation();
35191            this.slideOut();
35192         }
35193     },
35194 */
35195     /**
35196      * Collapses this region.
35197      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35198      */
35199     /*
35200     collapse : function(skipAnim, skipCheck = false){
35201         if(this.collapsed) {
35202             return;
35203         }
35204         
35205         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35206             
35207             this.collapsed = true;
35208             if(this.split){
35209                 this.split.el.hide();
35210             }
35211             if(this.config.animate && skipAnim !== true){
35212                 this.fireEvent("invalidated", this);
35213                 this.animateCollapse();
35214             }else{
35215                 this.el.setLocation(-20000,-20000);
35216                 this.el.hide();
35217                 this.collapsedEl.show();
35218                 this.fireEvent("collapsed", this);
35219                 this.fireEvent("invalidated", this);
35220             }
35221         }
35222         
35223     },
35224 */
35225     animateCollapse : function(){
35226         // overridden
35227     },
35228
35229     /**
35230      * Expands this region if it was previously collapsed.
35231      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35232      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35233      */
35234     /*
35235     expand : function(e, skipAnim){
35236         if(e) {
35237             e.stopPropagation();
35238         }
35239         if(!this.collapsed || this.el.hasActiveFx()) {
35240             return;
35241         }
35242         if(this.isSlid){
35243             this.afterSlideIn();
35244             skipAnim = true;
35245         }
35246         this.collapsed = false;
35247         if(this.config.animate && skipAnim !== true){
35248             this.animateExpand();
35249         }else{
35250             this.el.show();
35251             if(this.split){
35252                 this.split.el.show();
35253             }
35254             this.collapsedEl.setLocation(-2000,-2000);
35255             this.collapsedEl.hide();
35256             this.fireEvent("invalidated", this);
35257             this.fireEvent("expanded", this);
35258         }
35259     },
35260 */
35261     animateExpand : function(){
35262         // overridden
35263     },
35264
35265     initTabs : function()
35266     {
35267         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35268         
35269         var ts = new Roo.bootstrap.panel.Tabs({
35270                 el: this.bodyEl.dom,
35271                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35272                 disableTooltips: this.config.disableTabTips,
35273                 toolbar : this.config.toolbar
35274             });
35275         
35276         if(this.config.hideTabs){
35277             ts.stripWrap.setDisplayed(false);
35278         }
35279         this.tabs = ts;
35280         ts.resizeTabs = this.config.resizeTabs === true;
35281         ts.minTabWidth = this.config.minTabWidth || 40;
35282         ts.maxTabWidth = this.config.maxTabWidth || 250;
35283         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35284         ts.monitorResize = false;
35285         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35286         ts.bodyEl.addClass('roo-layout-tabs-body');
35287         this.panels.each(this.initPanelAsTab, this);
35288     },
35289
35290     initPanelAsTab : function(panel){
35291         var ti = this.tabs.addTab(
35292             panel.getEl().id,
35293             panel.getTitle(),
35294             null,
35295             this.config.closeOnTab && panel.isClosable(),
35296             panel.tpl
35297         );
35298         if(panel.tabTip !== undefined){
35299             ti.setTooltip(panel.tabTip);
35300         }
35301         ti.on("activate", function(){
35302               this.setActivePanel(panel);
35303         }, this);
35304         
35305         if(this.config.closeOnTab){
35306             ti.on("beforeclose", function(t, e){
35307                 e.cancel = true;
35308                 this.remove(panel);
35309             }, this);
35310         }
35311         
35312         panel.tabItem = ti;
35313         
35314         return ti;
35315     },
35316
35317     updatePanelTitle : function(panel, title)
35318     {
35319         if(this.activePanel == panel){
35320             this.updateTitle(title);
35321         }
35322         if(this.tabs){
35323             var ti = this.tabs.getTab(panel.getEl().id);
35324             ti.setText(title);
35325             if(panel.tabTip !== undefined){
35326                 ti.setTooltip(panel.tabTip);
35327             }
35328         }
35329     },
35330
35331     updateTitle : function(title){
35332         if(this.titleTextEl && !this.config.title){
35333             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
35334         }
35335     },
35336
35337     setActivePanel : function(panel)
35338     {
35339         panel = this.getPanel(panel);
35340         if(this.activePanel && this.activePanel != panel){
35341             this.activePanel.setActiveState(false);
35342         }
35343         this.activePanel = panel;
35344         panel.setActiveState(true);
35345         if(this.panelSize){
35346             panel.setSize(this.panelSize.width, this.panelSize.height);
35347         }
35348         if(this.closeBtn){
35349             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35350         }
35351         this.updateTitle(panel.getTitle());
35352         if(this.tabs){
35353             this.fireEvent("invalidated", this);
35354         }
35355         this.fireEvent("panelactivated", this, panel);
35356     },
35357
35358     /**
35359      * Shows the specified panel.
35360      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35361      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35362      */
35363     showPanel : function(panel)
35364     {
35365         panel = this.getPanel(panel);
35366         if(panel){
35367             if(this.tabs){
35368                 var tab = this.tabs.getTab(panel.getEl().id);
35369                 if(tab.isHidden()){
35370                     this.tabs.unhideTab(tab.id);
35371                 }
35372                 tab.activate();
35373             }else{
35374                 this.setActivePanel(panel);
35375             }
35376         }
35377         return panel;
35378     },
35379
35380     /**
35381      * Get the active panel for this region.
35382      * @return {Roo.ContentPanel} The active panel or null
35383      */
35384     getActivePanel : function(){
35385         return this.activePanel;
35386     },
35387
35388     validateVisibility : function(){
35389         if(this.panels.getCount() < 1){
35390             this.updateTitle("&#160;");
35391             this.closeBtn.hide();
35392             this.hide();
35393         }else{
35394             if(!this.isVisible()){
35395                 this.show();
35396             }
35397         }
35398     },
35399
35400     /**
35401      * Adds the passed ContentPanel(s) to this region.
35402      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35403      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35404      */
35405     add : function(panel)
35406     {
35407         if(arguments.length > 1){
35408             for(var i = 0, len = arguments.length; i < len; i++) {
35409                 this.add(arguments[i]);
35410             }
35411             return null;
35412         }
35413         
35414         // if we have not been rendered yet, then we can not really do much of this..
35415         if (!this.bodyEl) {
35416             this.unrendered_panels.push(panel);
35417             return panel;
35418         }
35419         
35420         
35421         
35422         
35423         if(this.hasPanel(panel)){
35424             this.showPanel(panel);
35425             return panel;
35426         }
35427         panel.setRegion(this);
35428         this.panels.add(panel);
35429        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
35430             // sinle panel - no tab...?? would it not be better to render it with the tabs,
35431             // and hide them... ???
35432             this.bodyEl.dom.appendChild(panel.getEl().dom);
35433             if(panel.background !== true){
35434                 this.setActivePanel(panel);
35435             }
35436             this.fireEvent("paneladded", this, panel);
35437             return panel;
35438         }
35439         */
35440         if(!this.tabs){
35441             this.initTabs();
35442         }else{
35443             this.initPanelAsTab(panel);
35444         }
35445         
35446         
35447         if(panel.background !== true){
35448             this.tabs.activate(panel.getEl().id);
35449         }
35450         this.fireEvent("paneladded", this, panel);
35451         return panel;
35452     },
35453
35454     /**
35455      * Hides the tab for the specified panel.
35456      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35457      */
35458     hidePanel : function(panel){
35459         if(this.tabs && (panel = this.getPanel(panel))){
35460             this.tabs.hideTab(panel.getEl().id);
35461         }
35462     },
35463
35464     /**
35465      * Unhides the tab for a previously hidden panel.
35466      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35467      */
35468     unhidePanel : function(panel){
35469         if(this.tabs && (panel = this.getPanel(panel))){
35470             this.tabs.unhideTab(panel.getEl().id);
35471         }
35472     },
35473
35474     clearPanels : function(){
35475         while(this.panels.getCount() > 0){
35476              this.remove(this.panels.first());
35477         }
35478     },
35479
35480     /**
35481      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35482      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35483      * @param {Boolean} preservePanel Overrides the config preservePanel option
35484      * @return {Roo.ContentPanel} The panel that was removed
35485      */
35486     remove : function(panel, preservePanel)
35487     {
35488         panel = this.getPanel(panel);
35489         if(!panel){
35490             return null;
35491         }
35492         var e = {};
35493         this.fireEvent("beforeremove", this, panel, e);
35494         if(e.cancel === true){
35495             return null;
35496         }
35497         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
35498         var panelId = panel.getId();
35499         this.panels.removeKey(panelId);
35500         if(preservePanel){
35501             document.body.appendChild(panel.getEl().dom);
35502         }
35503         if(this.tabs){
35504             this.tabs.removeTab(panel.getEl().id);
35505         }else if (!preservePanel){
35506             this.bodyEl.dom.removeChild(panel.getEl().dom);
35507         }
35508         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
35509             var p = this.panels.first();
35510             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
35511             tempEl.appendChild(p.getEl().dom);
35512             this.bodyEl.update("");
35513             this.bodyEl.dom.appendChild(p.getEl().dom);
35514             tempEl = null;
35515             this.updateTitle(p.getTitle());
35516             this.tabs = null;
35517             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
35518             this.setActivePanel(p);
35519         }
35520         panel.setRegion(null);
35521         if(this.activePanel == panel){
35522             this.activePanel = null;
35523         }
35524         if(this.config.autoDestroy !== false && preservePanel !== true){
35525             try{panel.destroy();}catch(e){}
35526         }
35527         this.fireEvent("panelremoved", this, panel);
35528         return panel;
35529     },
35530
35531     /**
35532      * Returns the TabPanel component used by this region
35533      * @return {Roo.TabPanel}
35534      */
35535     getTabs : function(){
35536         return this.tabs;
35537     },
35538
35539     createTool : function(parentEl, className){
35540         var btn = Roo.DomHelper.append(parentEl, {
35541             tag: "div",
35542             cls: "x-layout-tools-button",
35543             children: [ {
35544                 tag: "div",
35545                 cls: "roo-layout-tools-button-inner " + className,
35546                 html: "&#160;"
35547             }]
35548         }, true);
35549         btn.addClassOnOver("roo-layout-tools-button-over");
35550         return btn;
35551     }
35552 });/*
35553  * Based on:
35554  * Ext JS Library 1.1.1
35555  * Copyright(c) 2006-2007, Ext JS, LLC.
35556  *
35557  * Originally Released Under LGPL - original licence link has changed is not relivant.
35558  *
35559  * Fork - LGPL
35560  * <script type="text/javascript">
35561  */
35562  
35563
35564
35565 /**
35566  * @class Roo.SplitLayoutRegion
35567  * @extends Roo.LayoutRegion
35568  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
35569  */
35570 Roo.bootstrap.layout.Split = function(config){
35571     this.cursor = config.cursor;
35572     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
35573 };
35574
35575 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
35576 {
35577     splitTip : "Drag to resize.",
35578     collapsibleSplitTip : "Drag to resize. Double click to hide.",
35579     useSplitTips : false,
35580
35581     applyConfig : function(config){
35582         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
35583     },
35584     
35585     onRender : function(ctr,pos) {
35586         
35587         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
35588         if(!this.config.split){
35589             return;
35590         }
35591         if(!this.split){
35592             
35593             var splitEl = Roo.DomHelper.append(ctr.dom,  {
35594                             tag: "div",
35595                             id: this.el.id + "-split",
35596                             cls: "roo-layout-split roo-layout-split-"+this.position,
35597                             html: "&#160;"
35598             });
35599             /** The SplitBar for this region 
35600             * @type Roo.SplitBar */
35601             // does not exist yet...
35602             Roo.log([this.position, this.orientation]);
35603             
35604             this.split = new Roo.bootstrap.SplitBar({
35605                 dragElement : splitEl,
35606                 resizingElement: this.el,
35607                 orientation : this.orientation
35608             });
35609             
35610             this.split.on("moved", this.onSplitMove, this);
35611             this.split.useShim = this.config.useShim === true;
35612             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
35613             if(this.useSplitTips){
35614                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
35615             }
35616             //if(config.collapsible){
35617             //    this.split.el.on("dblclick", this.collapse,  this);
35618             //}
35619         }
35620         if(typeof this.config.minSize != "undefined"){
35621             this.split.minSize = this.config.minSize;
35622         }
35623         if(typeof this.config.maxSize != "undefined"){
35624             this.split.maxSize = this.config.maxSize;
35625         }
35626         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
35627             this.hideSplitter();
35628         }
35629         
35630     },
35631
35632     getHMaxSize : function(){
35633          var cmax = this.config.maxSize || 10000;
35634          var center = this.mgr.getRegion("center");
35635          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
35636     },
35637
35638     getVMaxSize : function(){
35639          var cmax = this.config.maxSize || 10000;
35640          var center = this.mgr.getRegion("center");
35641          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
35642     },
35643
35644     onSplitMove : function(split, newSize){
35645         this.fireEvent("resized", this, newSize);
35646     },
35647     
35648     /** 
35649      * Returns the {@link Roo.SplitBar} for this region.
35650      * @return {Roo.SplitBar}
35651      */
35652     getSplitBar : function(){
35653         return this.split;
35654     },
35655     
35656     hide : function(){
35657         this.hideSplitter();
35658         Roo.bootstrap.layout.Split.superclass.hide.call(this);
35659     },
35660
35661     hideSplitter : function(){
35662         if(this.split){
35663             this.split.el.setLocation(-2000,-2000);
35664             this.split.el.hide();
35665         }
35666     },
35667
35668     show : function(){
35669         if(this.split){
35670             this.split.el.show();
35671         }
35672         Roo.bootstrap.layout.Split.superclass.show.call(this);
35673     },
35674     
35675     beforeSlide: function(){
35676         if(Roo.isGecko){// firefox overflow auto bug workaround
35677             this.bodyEl.clip();
35678             if(this.tabs) {
35679                 this.tabs.bodyEl.clip();
35680             }
35681             if(this.activePanel){
35682                 this.activePanel.getEl().clip();
35683                 
35684                 if(this.activePanel.beforeSlide){
35685                     this.activePanel.beforeSlide();
35686                 }
35687             }
35688         }
35689     },
35690     
35691     afterSlide : function(){
35692         if(Roo.isGecko){// firefox overflow auto bug workaround
35693             this.bodyEl.unclip();
35694             if(this.tabs) {
35695                 this.tabs.bodyEl.unclip();
35696             }
35697             if(this.activePanel){
35698                 this.activePanel.getEl().unclip();
35699                 if(this.activePanel.afterSlide){
35700                     this.activePanel.afterSlide();
35701                 }
35702             }
35703         }
35704     },
35705
35706     initAutoHide : function(){
35707         if(this.autoHide !== false){
35708             if(!this.autoHideHd){
35709                 var st = new Roo.util.DelayedTask(this.slideIn, this);
35710                 this.autoHideHd = {
35711                     "mouseout": function(e){
35712                         if(!e.within(this.el, true)){
35713                             st.delay(500);
35714                         }
35715                     },
35716                     "mouseover" : function(e){
35717                         st.cancel();
35718                     },
35719                     scope : this
35720                 };
35721             }
35722             this.el.on(this.autoHideHd);
35723         }
35724     },
35725
35726     clearAutoHide : function(){
35727         if(this.autoHide !== false){
35728             this.el.un("mouseout", this.autoHideHd.mouseout);
35729             this.el.un("mouseover", this.autoHideHd.mouseover);
35730         }
35731     },
35732
35733     clearMonitor : function(){
35734         Roo.get(document).un("click", this.slideInIf, this);
35735     },
35736
35737     // these names are backwards but not changed for compat
35738     slideOut : function(){
35739         if(this.isSlid || this.el.hasActiveFx()){
35740             return;
35741         }
35742         this.isSlid = true;
35743         if(this.collapseBtn){
35744             this.collapseBtn.hide();
35745         }
35746         this.closeBtnState = this.closeBtn.getStyle('display');
35747         this.closeBtn.hide();
35748         if(this.stickBtn){
35749             this.stickBtn.show();
35750         }
35751         this.el.show();
35752         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
35753         this.beforeSlide();
35754         this.el.setStyle("z-index", 10001);
35755         this.el.slideIn(this.getSlideAnchor(), {
35756             callback: function(){
35757                 this.afterSlide();
35758                 this.initAutoHide();
35759                 Roo.get(document).on("click", this.slideInIf, this);
35760                 this.fireEvent("slideshow", this);
35761             },
35762             scope: this,
35763             block: true
35764         });
35765     },
35766
35767     afterSlideIn : function(){
35768         this.clearAutoHide();
35769         this.isSlid = false;
35770         this.clearMonitor();
35771         this.el.setStyle("z-index", "");
35772         if(this.collapseBtn){
35773             this.collapseBtn.show();
35774         }
35775         this.closeBtn.setStyle('display', this.closeBtnState);
35776         if(this.stickBtn){
35777             this.stickBtn.hide();
35778         }
35779         this.fireEvent("slidehide", this);
35780     },
35781
35782     slideIn : function(cb){
35783         if(!this.isSlid || this.el.hasActiveFx()){
35784             Roo.callback(cb);
35785             return;
35786         }
35787         this.isSlid = false;
35788         this.beforeSlide();
35789         this.el.slideOut(this.getSlideAnchor(), {
35790             callback: function(){
35791                 this.el.setLeftTop(-10000, -10000);
35792                 this.afterSlide();
35793                 this.afterSlideIn();
35794                 Roo.callback(cb);
35795             },
35796             scope: this,
35797             block: true
35798         });
35799     },
35800     
35801     slideInIf : function(e){
35802         if(!e.within(this.el)){
35803             this.slideIn();
35804         }
35805     },
35806
35807     animateCollapse : function(){
35808         this.beforeSlide();
35809         this.el.setStyle("z-index", 20000);
35810         var anchor = this.getSlideAnchor();
35811         this.el.slideOut(anchor, {
35812             callback : function(){
35813                 this.el.setStyle("z-index", "");
35814                 this.collapsedEl.slideIn(anchor, {duration:.3});
35815                 this.afterSlide();
35816                 this.el.setLocation(-10000,-10000);
35817                 this.el.hide();
35818                 this.fireEvent("collapsed", this);
35819             },
35820             scope: this,
35821             block: true
35822         });
35823     },
35824
35825     animateExpand : function(){
35826         this.beforeSlide();
35827         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
35828         this.el.setStyle("z-index", 20000);
35829         this.collapsedEl.hide({
35830             duration:.1
35831         });
35832         this.el.slideIn(this.getSlideAnchor(), {
35833             callback : function(){
35834                 this.el.setStyle("z-index", "");
35835                 this.afterSlide();
35836                 if(this.split){
35837                     this.split.el.show();
35838                 }
35839                 this.fireEvent("invalidated", this);
35840                 this.fireEvent("expanded", this);
35841             },
35842             scope: this,
35843             block: true
35844         });
35845     },
35846
35847     anchors : {
35848         "west" : "left",
35849         "east" : "right",
35850         "north" : "top",
35851         "south" : "bottom"
35852     },
35853
35854     sanchors : {
35855         "west" : "l",
35856         "east" : "r",
35857         "north" : "t",
35858         "south" : "b"
35859     },
35860
35861     canchors : {
35862         "west" : "tl-tr",
35863         "east" : "tr-tl",
35864         "north" : "tl-bl",
35865         "south" : "bl-tl"
35866     },
35867
35868     getAnchor : function(){
35869         return this.anchors[this.position];
35870     },
35871
35872     getCollapseAnchor : function(){
35873         return this.canchors[this.position];
35874     },
35875
35876     getSlideAnchor : function(){
35877         return this.sanchors[this.position];
35878     },
35879
35880     getAlignAdj : function(){
35881         var cm = this.cmargins;
35882         switch(this.position){
35883             case "west":
35884                 return [0, 0];
35885             break;
35886             case "east":
35887                 return [0, 0];
35888             break;
35889             case "north":
35890                 return [0, 0];
35891             break;
35892             case "south":
35893                 return [0, 0];
35894             break;
35895         }
35896     },
35897
35898     getExpandAdj : function(){
35899         var c = this.collapsedEl, cm = this.cmargins;
35900         switch(this.position){
35901             case "west":
35902                 return [-(cm.right+c.getWidth()+cm.left), 0];
35903             break;
35904             case "east":
35905                 return [cm.right+c.getWidth()+cm.left, 0];
35906             break;
35907             case "north":
35908                 return [0, -(cm.top+cm.bottom+c.getHeight())];
35909             break;
35910             case "south":
35911                 return [0, cm.top+cm.bottom+c.getHeight()];
35912             break;
35913         }
35914     }
35915 });/*
35916  * Based on:
35917  * Ext JS Library 1.1.1
35918  * Copyright(c) 2006-2007, Ext JS, LLC.
35919  *
35920  * Originally Released Under LGPL - original licence link has changed is not relivant.
35921  *
35922  * Fork - LGPL
35923  * <script type="text/javascript">
35924  */
35925 /*
35926  * These classes are private internal classes
35927  */
35928 Roo.bootstrap.layout.Center = function(config){
35929     config.region = "center";
35930     Roo.bootstrap.layout.Region.call(this, config);
35931     this.visible = true;
35932     this.minWidth = config.minWidth || 20;
35933     this.minHeight = config.minHeight || 20;
35934 };
35935
35936 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
35937     hide : function(){
35938         // center panel can't be hidden
35939     },
35940     
35941     show : function(){
35942         // center panel can't be hidden
35943     },
35944     
35945     getMinWidth: function(){
35946         return this.minWidth;
35947     },
35948     
35949     getMinHeight: function(){
35950         return this.minHeight;
35951     }
35952 });
35953
35954
35955
35956
35957  
35958
35959
35960
35961
35962
35963 Roo.bootstrap.layout.North = function(config)
35964 {
35965     config.region = 'north';
35966     config.cursor = 'n-resize';
35967     
35968     Roo.bootstrap.layout.Split.call(this, config);
35969     
35970     
35971     if(this.split){
35972         this.split.placement = Roo.bootstrap.SplitBar.TOP;
35973         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
35974         this.split.el.addClass("roo-layout-split-v");
35975     }
35976     var size = config.initialSize || config.height;
35977     if(typeof size != "undefined"){
35978         this.el.setHeight(size);
35979     }
35980 };
35981 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
35982 {
35983     orientation: Roo.bootstrap.SplitBar.VERTICAL,
35984     
35985     
35986     
35987     getBox : function(){
35988         if(this.collapsed){
35989             return this.collapsedEl.getBox();
35990         }
35991         var box = this.el.getBox();
35992         if(this.split){
35993             box.height += this.split.el.getHeight();
35994         }
35995         return box;
35996     },
35997     
35998     updateBox : function(box){
35999         if(this.split && !this.collapsed){
36000             box.height -= this.split.el.getHeight();
36001             this.split.el.setLeft(box.x);
36002             this.split.el.setTop(box.y+box.height);
36003             this.split.el.setWidth(box.width);
36004         }
36005         if(this.collapsed){
36006             this.updateBody(box.width, null);
36007         }
36008         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36009     }
36010 });
36011
36012
36013
36014
36015
36016 Roo.bootstrap.layout.South = function(config){
36017     config.region = 'south';
36018     config.cursor = 's-resize';
36019     Roo.bootstrap.layout.Split.call(this, config);
36020     if(this.split){
36021         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36022         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36023         this.split.el.addClass("roo-layout-split-v");
36024     }
36025     var size = config.initialSize || config.height;
36026     if(typeof size != "undefined"){
36027         this.el.setHeight(size);
36028     }
36029 };
36030
36031 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36032     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36033     getBox : function(){
36034         if(this.collapsed){
36035             return this.collapsedEl.getBox();
36036         }
36037         var box = this.el.getBox();
36038         if(this.split){
36039             var sh = this.split.el.getHeight();
36040             box.height += sh;
36041             box.y -= sh;
36042         }
36043         return box;
36044     },
36045     
36046     updateBox : function(box){
36047         if(this.split && !this.collapsed){
36048             var sh = this.split.el.getHeight();
36049             box.height -= sh;
36050             box.y += sh;
36051             this.split.el.setLeft(box.x);
36052             this.split.el.setTop(box.y-sh);
36053             this.split.el.setWidth(box.width);
36054         }
36055         if(this.collapsed){
36056             this.updateBody(box.width, null);
36057         }
36058         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36059     }
36060 });
36061
36062 Roo.bootstrap.layout.East = function(config){
36063     config.region = "east";
36064     config.cursor = "e-resize";
36065     Roo.bootstrap.layout.Split.call(this, config);
36066     if(this.split){
36067         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36068         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36069         this.split.el.addClass("roo-layout-split-h");
36070     }
36071     var size = config.initialSize || config.width;
36072     if(typeof size != "undefined"){
36073         this.el.setWidth(size);
36074     }
36075 };
36076 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36077     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36078     getBox : function(){
36079         if(this.collapsed){
36080             return this.collapsedEl.getBox();
36081         }
36082         var box = this.el.getBox();
36083         if(this.split){
36084             var sw = this.split.el.getWidth();
36085             box.width += sw;
36086             box.x -= sw;
36087         }
36088         return box;
36089     },
36090
36091     updateBox : function(box){
36092         if(this.split && !this.collapsed){
36093             var sw = this.split.el.getWidth();
36094             box.width -= sw;
36095             this.split.el.setLeft(box.x);
36096             this.split.el.setTop(box.y);
36097             this.split.el.setHeight(box.height);
36098             box.x += sw;
36099         }
36100         if(this.collapsed){
36101             this.updateBody(null, box.height);
36102         }
36103         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36104     }
36105 });
36106
36107 Roo.bootstrap.layout.West = function(config){
36108     config.region = "west";
36109     config.cursor = "w-resize";
36110     
36111     Roo.bootstrap.layout.Split.call(this, config);
36112     if(this.split){
36113         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36114         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36115         this.split.el.addClass("roo-layout-split-h");
36116     }
36117     
36118 };
36119 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36120     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36121     
36122     onRender: function(ctr, pos)
36123     {
36124         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36125         var size = this.config.initialSize || this.config.width;
36126         if(typeof size != "undefined"){
36127             this.el.setWidth(size);
36128         }
36129     },
36130     
36131     getBox : function(){
36132         if(this.collapsed){
36133             return this.collapsedEl.getBox();
36134         }
36135         var box = this.el.getBox();
36136         if(this.split){
36137             box.width += this.split.el.getWidth();
36138         }
36139         return box;
36140     },
36141     
36142     updateBox : function(box){
36143         if(this.split && !this.collapsed){
36144             var sw = this.split.el.getWidth();
36145             box.width -= sw;
36146             this.split.el.setLeft(box.x+box.width);
36147             this.split.el.setTop(box.y);
36148             this.split.el.setHeight(box.height);
36149         }
36150         if(this.collapsed){
36151             this.updateBody(null, box.height);
36152         }
36153         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36154     }
36155 });
36156 Roo.namespace("Roo.bootstrap.panel");/*
36157  * Based on:
36158  * Ext JS Library 1.1.1
36159  * Copyright(c) 2006-2007, Ext JS, LLC.
36160  *
36161  * Originally Released Under LGPL - original licence link has changed is not relivant.
36162  *
36163  * Fork - LGPL
36164  * <script type="text/javascript">
36165  */
36166 /**
36167  * @class Roo.ContentPanel
36168  * @extends Roo.util.Observable
36169  * A basic ContentPanel element.
36170  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36171  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36172  * @cfg {Boolean/Object} autoCreate True to auto generate the DOM element for this panel, or a {@link Roo.DomHelper} config of the element to create
36173  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36174  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36175  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36176  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36177  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36178  * @cfg {String} title          The title for this panel
36179  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36180  * @cfg {String} url            Calls {@link #setUrl} with this value
36181  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36182  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36183  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36184  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36185  * @cfg {Boolean} badges render the badges
36186
36187  * @constructor
36188  * Create a new ContentPanel.
36189  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36190  * @param {String/Object} config A string to set only the title or a config object
36191  * @param {String} content (optional) Set the HTML content for this panel
36192  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36193  */
36194 Roo.bootstrap.panel.Content = function( config){
36195     
36196     this.tpl = config.tpl || false;
36197     
36198     var el = config.el;
36199     var content = config.content;
36200
36201     if(config.autoCreate){ // xtype is available if this is called from factory
36202         el = Roo.id();
36203     }
36204     this.el = Roo.get(el);
36205     if(!this.el && config && config.autoCreate){
36206         if(typeof config.autoCreate == "object"){
36207             if(!config.autoCreate.id){
36208                 config.autoCreate.id = config.id||el;
36209             }
36210             this.el = Roo.DomHelper.append(document.body,
36211                         config.autoCreate, true);
36212         }else{
36213             var elcfg =  {   tag: "div",
36214                             cls: "roo-layout-inactive-content",
36215                             id: config.id||el
36216                             };
36217             if (config.html) {
36218                 elcfg.html = config.html;
36219                 
36220             }
36221                         
36222             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36223         }
36224     } 
36225     this.closable = false;
36226     this.loaded = false;
36227     this.active = false;
36228    
36229       
36230     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36231         
36232         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36233         
36234         this.wrapEl = this.el; //this.el.wrap();
36235         var ti = [];
36236         if (config.toolbar.items) {
36237             ti = config.toolbar.items ;
36238             delete config.toolbar.items ;
36239         }
36240         
36241         var nitems = [];
36242         this.toolbar.render(this.wrapEl, 'before');
36243         for(var i =0;i < ti.length;i++) {
36244           //  Roo.log(['add child', items[i]]);
36245             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36246         }
36247         this.toolbar.items = nitems;
36248         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36249         delete config.toolbar;
36250         
36251     }
36252     /*
36253     // xtype created footer. - not sure if will work as we normally have to render first..
36254     if (this.footer && !this.footer.el && this.footer.xtype) {
36255         if (!this.wrapEl) {
36256             this.wrapEl = this.el.wrap();
36257         }
36258     
36259         this.footer.container = this.wrapEl.createChild();
36260          
36261         this.footer = Roo.factory(this.footer, Roo);
36262         
36263     }
36264     */
36265     
36266      if(typeof config == "string"){
36267         this.title = config;
36268     }else{
36269         Roo.apply(this, config);
36270     }
36271     
36272     if(this.resizeEl){
36273         this.resizeEl = Roo.get(this.resizeEl, true);
36274     }else{
36275         this.resizeEl = this.el;
36276     }
36277     // handle view.xtype
36278     
36279  
36280     
36281     
36282     this.addEvents({
36283         /**
36284          * @event activate
36285          * Fires when this panel is activated. 
36286          * @param {Roo.ContentPanel} this
36287          */
36288         "activate" : true,
36289         /**
36290          * @event deactivate
36291          * Fires when this panel is activated. 
36292          * @param {Roo.ContentPanel} this
36293          */
36294         "deactivate" : true,
36295
36296         /**
36297          * @event resize
36298          * Fires when this panel is resized if fitToFrame is true.
36299          * @param {Roo.ContentPanel} this
36300          * @param {Number} width The width after any component adjustments
36301          * @param {Number} height The height after any component adjustments
36302          */
36303         "resize" : true,
36304         
36305          /**
36306          * @event render
36307          * Fires when this tab is created
36308          * @param {Roo.ContentPanel} this
36309          */
36310         "render" : true
36311         
36312         
36313         
36314     });
36315     
36316
36317     
36318     
36319     if(this.autoScroll){
36320         this.resizeEl.setStyle("overflow", "auto");
36321     } else {
36322         // fix randome scrolling
36323         //this.el.on('scroll', function() {
36324         //    Roo.log('fix random scolling');
36325         //    this.scrollTo('top',0); 
36326         //});
36327     }
36328     content = content || this.content;
36329     if(content){
36330         this.setContent(content);
36331     }
36332     if(config && config.url){
36333         this.setUrl(this.url, this.params, this.loadOnce);
36334     }
36335     
36336     
36337     
36338     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36339     
36340     if (this.view && typeof(this.view.xtype) != 'undefined') {
36341         this.view.el = this.el.appendChild(document.createElement("div"));
36342         this.view = Roo.factory(this.view); 
36343         this.view.render  &&  this.view.render(false, '');  
36344     }
36345     
36346     
36347     this.fireEvent('render', this);
36348 };
36349
36350 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36351     
36352     tabTip : '',
36353     
36354     setRegion : function(region){
36355         this.region = region;
36356         this.setActiveClass(region && !this.background);
36357     },
36358     
36359     
36360     setActiveClass: function(state)
36361     {
36362         if(state){
36363            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36364            this.el.setStyle('position','relative');
36365         }else{
36366            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36367            this.el.setStyle('position', 'absolute');
36368         } 
36369     },
36370     
36371     /**
36372      * Returns the toolbar for this Panel if one was configured. 
36373      * @return {Roo.Toolbar} 
36374      */
36375     getToolbar : function(){
36376         return this.toolbar;
36377     },
36378     
36379     setActiveState : function(active)
36380     {
36381         this.active = active;
36382         this.setActiveClass(active);
36383         if(!active){
36384             this.fireEvent("deactivate", this);
36385         }else{
36386             this.fireEvent("activate", this);
36387         }
36388     },
36389     /**
36390      * Updates this panel's element
36391      * @param {String} content The new content
36392      * @param {Boolean} loadScripts (optional) true to look for and process scripts
36393     */
36394     setContent : function(content, loadScripts){
36395         this.el.update(content, loadScripts);
36396     },
36397
36398     ignoreResize : function(w, h){
36399         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36400             return true;
36401         }else{
36402             this.lastSize = {width: w, height: h};
36403             return false;
36404         }
36405     },
36406     /**
36407      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36408      * @return {Roo.UpdateManager} The UpdateManager
36409      */
36410     getUpdateManager : function(){
36411         return this.el.getUpdateManager();
36412     },
36413      /**
36414      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36415      * @param {Object/String/Function} url The url for this request or a function to call to get the url or a config object containing any of the following options:
36416 <pre><code>
36417 panel.load({
36418     url: "your-url.php",
36419     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36420     callback: yourFunction,
36421     scope: yourObject, //(optional scope)
36422     discardUrl: false,
36423     nocache: false,
36424     text: "Loading...",
36425     timeout: 30,
36426     scripts: false
36427 });
36428 </code></pre>
36429      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
36430      * are shorthand for <i>disableCaching</i>, <i>indicatorText</i> and <i>loadScripts</i> and are used to set their associated property on this panel UpdateManager instance.
36431      * @param {String/Object} params (optional) The parameters to pass as either a URL encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
36432      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
36433      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used URL. If true, it will not store the URL.
36434      * @return {Roo.ContentPanel} this
36435      */
36436     load : function(){
36437         var um = this.el.getUpdateManager();
36438         um.update.apply(um, arguments);
36439         return this;
36440     },
36441
36442
36443     /**
36444      * Set a URL to be used to load the content for this panel. When this panel is activated, the content will be loaded from that URL.
36445      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
36446      * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
36447      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this panel is activated. (Defaults to false)
36448      * @return {Roo.UpdateManager} The UpdateManager
36449      */
36450     setUrl : function(url, params, loadOnce){
36451         if(this.refreshDelegate){
36452             this.removeListener("activate", this.refreshDelegate);
36453         }
36454         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36455         this.on("activate", this.refreshDelegate);
36456         return this.el.getUpdateManager();
36457     },
36458     
36459     _handleRefresh : function(url, params, loadOnce){
36460         if(!loadOnce || !this.loaded){
36461             var updater = this.el.getUpdateManager();
36462             updater.update(url, params, this._setLoaded.createDelegate(this));
36463         }
36464     },
36465     
36466     _setLoaded : function(){
36467         this.loaded = true;
36468     }, 
36469     
36470     /**
36471      * Returns this panel's id
36472      * @return {String} 
36473      */
36474     getId : function(){
36475         return this.el.id;
36476     },
36477     
36478     /** 
36479      * Returns this panel's element - used by regiosn to add.
36480      * @return {Roo.Element} 
36481      */
36482     getEl : function(){
36483         return this.wrapEl || this.el;
36484     },
36485     
36486    
36487     
36488     adjustForComponents : function(width, height)
36489     {
36490         //Roo.log('adjustForComponents ');
36491         if(this.resizeEl != this.el){
36492             width -= this.el.getFrameWidth('lr');
36493             height -= this.el.getFrameWidth('tb');
36494         }
36495         if(this.toolbar){
36496             var te = this.toolbar.getEl();
36497             te.setWidth(width);
36498             height -= te.getHeight();
36499         }
36500         if(this.footer){
36501             var te = this.footer.getEl();
36502             te.setWidth(width);
36503             height -= te.getHeight();
36504         }
36505         
36506         
36507         if(this.adjustments){
36508             width += this.adjustments[0];
36509             height += this.adjustments[1];
36510         }
36511         return {"width": width, "height": height};
36512     },
36513     
36514     setSize : function(width, height){
36515         if(this.fitToFrame && !this.ignoreResize(width, height)){
36516             if(this.fitContainer && this.resizeEl != this.el){
36517                 this.el.setSize(width, height);
36518             }
36519             var size = this.adjustForComponents(width, height);
36520             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
36521             this.fireEvent('resize', this, size.width, size.height);
36522         }
36523     },
36524     
36525     /**
36526      * Returns this panel's title
36527      * @return {String} 
36528      */
36529     getTitle : function(){
36530         
36531         if (typeof(this.title) != 'object') {
36532             return this.title;
36533         }
36534         
36535         var t = '';
36536         for (var k in this.title) {
36537             if (!this.title.hasOwnProperty(k)) {
36538                 continue;
36539             }
36540             
36541             if (k.indexOf('-') >= 0) {
36542                 var s = k.split('-');
36543                 for (var i = 0; i<s.length; i++) {
36544                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
36545                 }
36546             } else {
36547                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
36548             }
36549         }
36550         return t;
36551     },
36552     
36553     /**
36554      * Set this panel's title
36555      * @param {String} title
36556      */
36557     setTitle : function(title){
36558         this.title = title;
36559         if(this.region){
36560             this.region.updatePanelTitle(this, title);
36561         }
36562     },
36563     
36564     /**
36565      * Returns true is this panel was configured to be closable
36566      * @return {Boolean} 
36567      */
36568     isClosable : function(){
36569         return this.closable;
36570     },
36571     
36572     beforeSlide : function(){
36573         this.el.clip();
36574         this.resizeEl.clip();
36575     },
36576     
36577     afterSlide : function(){
36578         this.el.unclip();
36579         this.resizeEl.unclip();
36580     },
36581     
36582     /**
36583      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
36584      *   Will fail silently if the {@link #setUrl} method has not been called.
36585      *   This does not activate the panel, just updates its content.
36586      */
36587     refresh : function(){
36588         if(this.refreshDelegate){
36589            this.loaded = false;
36590            this.refreshDelegate();
36591         }
36592     },
36593     
36594     /**
36595      * Destroys this panel
36596      */
36597     destroy : function(){
36598         this.el.removeAllListeners();
36599         var tempEl = document.createElement("span");
36600         tempEl.appendChild(this.el.dom);
36601         tempEl.innerHTML = "";
36602         this.el.remove();
36603         this.el = null;
36604     },
36605     
36606     /**
36607      * form - if the content panel contains a form - this is a reference to it.
36608      * @type {Roo.form.Form}
36609      */
36610     form : false,
36611     /**
36612      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
36613      *    This contains a reference to it.
36614      * @type {Roo.View}
36615      */
36616     view : false,
36617     
36618       /**
36619      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
36620      * <pre><code>
36621
36622 layout.addxtype({
36623        xtype : 'Form',
36624        items: [ .... ]
36625    }
36626 );
36627
36628 </code></pre>
36629      * @param {Object} cfg Xtype definition of item to add.
36630      */
36631     
36632     
36633     getChildContainer: function () {
36634         return this.getEl();
36635     }
36636     
36637     
36638     /*
36639         var  ret = new Roo.factory(cfg);
36640         return ret;
36641         
36642         
36643         // add form..
36644         if (cfg.xtype.match(/^Form$/)) {
36645             
36646             var el;
36647             //if (this.footer) {
36648             //    el = this.footer.container.insertSibling(false, 'before');
36649             //} else {
36650                 el = this.el.createChild();
36651             //}
36652
36653             this.form = new  Roo.form.Form(cfg);
36654             
36655             
36656             if ( this.form.allItems.length) {
36657                 this.form.render(el.dom);
36658             }
36659             return this.form;
36660         }
36661         // should only have one of theses..
36662         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
36663             // views.. should not be just added - used named prop 'view''
36664             
36665             cfg.el = this.el.appendChild(document.createElement("div"));
36666             // factory?
36667             
36668             var ret = new Roo.factory(cfg);
36669              
36670              ret.render && ret.render(false, ''); // render blank..
36671             this.view = ret;
36672             return ret;
36673         }
36674         return false;
36675     }
36676     \*/
36677 });
36678  
36679 /**
36680  * @class Roo.bootstrap.panel.Grid
36681  * @extends Roo.bootstrap.panel.Content
36682  * @constructor
36683  * Create a new GridPanel.
36684  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
36685  * @param {Object} config A the config object
36686   
36687  */
36688
36689
36690
36691 Roo.bootstrap.panel.Grid = function(config)
36692 {
36693     
36694       
36695     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
36696         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
36697
36698     config.el = this.wrapper;
36699     //this.el = this.wrapper;
36700     
36701       if (config.container) {
36702         // ctor'ed from a Border/panel.grid
36703         
36704         
36705         this.wrapper.setStyle("overflow", "hidden");
36706         this.wrapper.addClass('roo-grid-container');
36707
36708     }
36709     
36710     
36711     if(config.toolbar){
36712         var tool_el = this.wrapper.createChild();    
36713         this.toolbar = Roo.factory(config.toolbar);
36714         var ti = [];
36715         if (config.toolbar.items) {
36716             ti = config.toolbar.items ;
36717             delete config.toolbar.items ;
36718         }
36719         
36720         var nitems = [];
36721         this.toolbar.render(tool_el);
36722         for(var i =0;i < ti.length;i++) {
36723           //  Roo.log(['add child', items[i]]);
36724             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36725         }
36726         this.toolbar.items = nitems;
36727         
36728         delete config.toolbar;
36729     }
36730     
36731     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
36732     config.grid.scrollBody = true;;
36733     config.grid.monitorWindowResize = false; // turn off autosizing
36734     config.grid.autoHeight = false;
36735     config.grid.autoWidth = false;
36736     
36737     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
36738     
36739     if (config.background) {
36740         // render grid on panel activation (if panel background)
36741         this.on('activate', function(gp) {
36742             if (!gp.grid.rendered) {
36743                 gp.grid.render(this.wrapper);
36744                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
36745             }
36746         });
36747             
36748     } else {
36749         this.grid.render(this.wrapper);
36750         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
36751
36752     }
36753     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
36754     // ??? needed ??? config.el = this.wrapper;
36755     
36756     
36757     
36758   
36759     // xtype created footer. - not sure if will work as we normally have to render first..
36760     if (this.footer && !this.footer.el && this.footer.xtype) {
36761         
36762         var ctr = this.grid.getView().getFooterPanel(true);
36763         this.footer.dataSource = this.grid.dataSource;
36764         this.footer = Roo.factory(this.footer, Roo);
36765         this.footer.render(ctr);
36766         
36767     }
36768     
36769     
36770     
36771     
36772      
36773 };
36774
36775 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
36776     getId : function(){
36777         return this.grid.id;
36778     },
36779     
36780     /**
36781      * Returns the grid for this panel
36782      * @return {Roo.bootstrap.Table} 
36783      */
36784     getGrid : function(){
36785         return this.grid;    
36786     },
36787     
36788     setSize : function(width, height){
36789         if(!this.ignoreResize(width, height)){
36790             var grid = this.grid;
36791             var size = this.adjustForComponents(width, height);
36792             var gridel = grid.getGridEl();
36793             gridel.setSize(size.width, size.height);
36794             /*
36795             var thd = grid.getGridEl().select('thead',true).first();
36796             var tbd = grid.getGridEl().select('tbody', true).first();
36797             if (tbd) {
36798                 tbd.setSize(width, height - thd.getHeight());
36799             }
36800             */
36801             grid.autoSize();
36802         }
36803     },
36804      
36805     
36806     
36807     beforeSlide : function(){
36808         this.grid.getView().scroller.clip();
36809     },
36810     
36811     afterSlide : function(){
36812         this.grid.getView().scroller.unclip();
36813     },
36814     
36815     destroy : function(){
36816         this.grid.destroy();
36817         delete this.grid;
36818         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
36819     }
36820 });
36821
36822 /**
36823  * @class Roo.bootstrap.panel.Nest
36824  * @extends Roo.bootstrap.panel.Content
36825  * @constructor
36826  * Create a new Panel, that can contain a layout.Border.
36827  * 
36828  * 
36829  * @param {Roo.BorderLayout} layout The layout for this panel
36830  * @param {String/Object} config A string to set only the title or a config object
36831  */
36832 Roo.bootstrap.panel.Nest = function(config)
36833 {
36834     // construct with only one argument..
36835     /* FIXME - implement nicer consturctors
36836     if (layout.layout) {
36837         config = layout;
36838         layout = config.layout;
36839         delete config.layout;
36840     }
36841     if (layout.xtype && !layout.getEl) {
36842         // then layout needs constructing..
36843         layout = Roo.factory(layout, Roo);
36844     }
36845     */
36846     
36847     config.el =  config.layout.getEl();
36848     
36849     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
36850     
36851     config.layout.monitorWindowResize = false; // turn off autosizing
36852     this.layout = config.layout;
36853     this.layout.getEl().addClass("roo-layout-nested-layout");
36854     
36855     
36856     
36857     
36858 };
36859
36860 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
36861
36862     setSize : function(width, height){
36863         if(!this.ignoreResize(width, height)){
36864             var size = this.adjustForComponents(width, height);
36865             var el = this.layout.getEl();
36866             if (size.height < 1) {
36867                 el.setWidth(size.width);   
36868             } else {
36869                 el.setSize(size.width, size.height);
36870             }
36871             var touch = el.dom.offsetWidth;
36872             this.layout.layout();
36873             // ie requires a double layout on the first pass
36874             if(Roo.isIE && !this.initialized){
36875                 this.initialized = true;
36876                 this.layout.layout();
36877             }
36878         }
36879     },
36880     
36881     // activate all subpanels if not currently active..
36882     
36883     setActiveState : function(active){
36884         this.active = active;
36885         this.setActiveClass(active);
36886         
36887         if(!active){
36888             this.fireEvent("deactivate", this);
36889             return;
36890         }
36891         
36892         this.fireEvent("activate", this);
36893         // not sure if this should happen before or after..
36894         if (!this.layout) {
36895             return; // should not happen..
36896         }
36897         var reg = false;
36898         for (var r in this.layout.regions) {
36899             reg = this.layout.getRegion(r);
36900             if (reg.getActivePanel()) {
36901                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
36902                 reg.setActivePanel(reg.getActivePanel());
36903                 continue;
36904             }
36905             if (!reg.panels.length) {
36906                 continue;
36907             }
36908             reg.showPanel(reg.getPanel(0));
36909         }
36910         
36911         
36912         
36913         
36914     },
36915     
36916     /**
36917      * Returns the nested BorderLayout for this panel
36918      * @return {Roo.BorderLayout} 
36919      */
36920     getLayout : function(){
36921         return this.layout;
36922     },
36923     
36924      /**
36925      * Adds a xtype elements to the layout of the nested panel
36926      * <pre><code>
36927
36928 panel.addxtype({
36929        xtype : 'ContentPanel',
36930        region: 'west',
36931        items: [ .... ]
36932    }
36933 );
36934
36935 panel.addxtype({
36936         xtype : 'NestedLayoutPanel',
36937         region: 'west',
36938         layout: {
36939            center: { },
36940            west: { }   
36941         },
36942         items : [ ... list of content panels or nested layout panels.. ]
36943    }
36944 );
36945 </code></pre>
36946      * @param {Object} cfg Xtype definition of item to add.
36947      */
36948     addxtype : function(cfg) {
36949         return this.layout.addxtype(cfg);
36950     
36951     }
36952 });        /*
36953  * Based on:
36954  * Ext JS Library 1.1.1
36955  * Copyright(c) 2006-2007, Ext JS, LLC.
36956  *
36957  * Originally Released Under LGPL - original licence link has changed is not relivant.
36958  *
36959  * Fork - LGPL
36960  * <script type="text/javascript">
36961  */
36962 /**
36963  * @class Roo.TabPanel
36964  * @extends Roo.util.Observable
36965  * A lightweight tab container.
36966  * <br><br>
36967  * Usage:
36968  * <pre><code>
36969 // basic tabs 1, built from existing content
36970 var tabs = new Roo.TabPanel("tabs1");
36971 tabs.addTab("script", "View Script");
36972 tabs.addTab("markup", "View Markup");
36973 tabs.activate("script");
36974
36975 // more advanced tabs, built from javascript
36976 var jtabs = new Roo.TabPanel("jtabs");
36977 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
36978
36979 // set up the UpdateManager
36980 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
36981 var updater = tab2.getUpdateManager();
36982 updater.setDefaultUrl("ajax1.htm");
36983 tab2.on('activate', updater.refresh, updater, true);
36984
36985 // Use setUrl for Ajax loading
36986 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
36987 tab3.setUrl("ajax2.htm", null, true);
36988
36989 // Disabled tab
36990 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
36991 tab4.disable();
36992
36993 jtabs.activate("jtabs-1");
36994  * </code></pre>
36995  * @constructor
36996  * Create a new TabPanel.
36997  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
36998  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
36999  */
37000 Roo.bootstrap.panel.Tabs = function(config){
37001     /**
37002     * The container element for this TabPanel.
37003     * @type Roo.Element
37004     */
37005     this.el = Roo.get(config.el);
37006     delete config.el;
37007     if(config){
37008         if(typeof config == "boolean"){
37009             this.tabPosition = config ? "bottom" : "top";
37010         }else{
37011             Roo.apply(this, config);
37012         }
37013     }
37014     
37015     if(this.tabPosition == "bottom"){
37016         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37017         this.el.addClass("roo-tabs-bottom");
37018     }
37019     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37020     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37021     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37022     if(Roo.isIE){
37023         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37024     }
37025     if(this.tabPosition != "bottom"){
37026         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37027          * @type Roo.Element
37028          */
37029         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37030         this.el.addClass("roo-tabs-top");
37031     }
37032     this.items = [];
37033
37034     this.bodyEl.setStyle("position", "relative");
37035
37036     this.active = null;
37037     this.activateDelegate = this.activate.createDelegate(this);
37038
37039     this.addEvents({
37040         /**
37041          * @event tabchange
37042          * Fires when the active tab changes
37043          * @param {Roo.TabPanel} this
37044          * @param {Roo.TabPanelItem} activePanel The new active tab
37045          */
37046         "tabchange": true,
37047         /**
37048          * @event beforetabchange
37049          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37050          * @param {Roo.TabPanel} this
37051          * @param {Object} e Set cancel to true on this object to cancel the tab change
37052          * @param {Roo.TabPanelItem} tab The tab being changed to
37053          */
37054         "beforetabchange" : true
37055     });
37056
37057     Roo.EventManager.onWindowResize(this.onResize, this);
37058     this.cpad = this.el.getPadding("lr");
37059     this.hiddenCount = 0;
37060
37061
37062     // toolbar on the tabbar support...
37063     if (this.toolbar) {
37064         alert("no toolbar support yet");
37065         this.toolbar  = false;
37066         /*
37067         var tcfg = this.toolbar;
37068         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37069         this.toolbar = new Roo.Toolbar(tcfg);
37070         if (Roo.isSafari) {
37071             var tbl = tcfg.container.child('table', true);
37072             tbl.setAttribute('width', '100%');
37073         }
37074         */
37075         
37076     }
37077    
37078
37079
37080     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37081 };
37082
37083 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37084     /*
37085      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37086      */
37087     tabPosition : "top",
37088     /*
37089      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37090      */
37091     currentTabWidth : 0,
37092     /*
37093      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37094      */
37095     minTabWidth : 40,
37096     /*
37097      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37098      */
37099     maxTabWidth : 250,
37100     /*
37101      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37102      */
37103     preferredTabWidth : 175,
37104     /*
37105      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37106      */
37107     resizeTabs : false,
37108     /*
37109      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37110      */
37111     monitorResize : true,
37112     /*
37113      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37114      */
37115     toolbar : false,
37116
37117     /**
37118      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37119      * @param {String} id The id of the div to use <b>or create</b>
37120      * @param {String} text The text for the tab
37121      * @param {String} content (optional) Content to put in the TabPanelItem body
37122      * @param {Boolean} closable (optional) True to create a close icon on the tab
37123      * @return {Roo.TabPanelItem} The created TabPanelItem
37124      */
37125     addTab : function(id, text, content, closable, tpl)
37126     {
37127         var item = new Roo.bootstrap.panel.TabItem({
37128             panel: this,
37129             id : id,
37130             text : text,
37131             closable : closable,
37132             tpl : tpl
37133         });
37134         this.addTabItem(item);
37135         if(content){
37136             item.setContent(content);
37137         }
37138         return item;
37139     },
37140
37141     /**
37142      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37143      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37144      * @return {Roo.TabPanelItem}
37145      */
37146     getTab : function(id){
37147         return this.items[id];
37148     },
37149
37150     /**
37151      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37152      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37153      */
37154     hideTab : function(id){
37155         var t = this.items[id];
37156         if(!t.isHidden()){
37157            t.setHidden(true);
37158            this.hiddenCount++;
37159            this.autoSizeTabs();
37160         }
37161     },
37162
37163     /**
37164      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37165      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37166      */
37167     unhideTab : function(id){
37168         var t = this.items[id];
37169         if(t.isHidden()){
37170            t.setHidden(false);
37171            this.hiddenCount--;
37172            this.autoSizeTabs();
37173         }
37174     },
37175
37176     /**
37177      * Adds an existing {@link Roo.TabPanelItem}.
37178      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37179      */
37180     addTabItem : function(item){
37181         this.items[item.id] = item;
37182         this.items.push(item);
37183       //  if(this.resizeTabs){
37184     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37185   //         this.autoSizeTabs();
37186 //        }else{
37187 //            item.autoSize();
37188        // }
37189     },
37190
37191     /**
37192      * Removes a {@link Roo.TabPanelItem}.
37193      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37194      */
37195     removeTab : function(id){
37196         var items = this.items;
37197         var tab = items[id];
37198         if(!tab) { return; }
37199         var index = items.indexOf(tab);
37200         if(this.active == tab && items.length > 1){
37201             var newTab = this.getNextAvailable(index);
37202             if(newTab) {
37203                 newTab.activate();
37204             }
37205         }
37206         this.stripEl.dom.removeChild(tab.pnode.dom);
37207         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37208             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37209         }
37210         items.splice(index, 1);
37211         delete this.items[tab.id];
37212         tab.fireEvent("close", tab);
37213         tab.purgeListeners();
37214         this.autoSizeTabs();
37215     },
37216
37217     getNextAvailable : function(start){
37218         var items = this.items;
37219         var index = start;
37220         // look for a next tab that will slide over to
37221         // replace the one being removed
37222         while(index < items.length){
37223             var item = items[++index];
37224             if(item && !item.isHidden()){
37225                 return item;
37226             }
37227         }
37228         // if one isn't found select the previous tab (on the left)
37229         index = start;
37230         while(index >= 0){
37231             var item = items[--index];
37232             if(item && !item.isHidden()){
37233                 return item;
37234             }
37235         }
37236         return null;
37237     },
37238
37239     /**
37240      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37241      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37242      */
37243     disableTab : function(id){
37244         var tab = this.items[id];
37245         if(tab && this.active != tab){
37246             tab.disable();
37247         }
37248     },
37249
37250     /**
37251      * Enables a {@link Roo.TabPanelItem} that is disabled.
37252      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37253      */
37254     enableTab : function(id){
37255         var tab = this.items[id];
37256         tab.enable();
37257     },
37258
37259     /**
37260      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37261      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37262      * @return {Roo.TabPanelItem} The TabPanelItem.
37263      */
37264     activate : function(id){
37265         var tab = this.items[id];
37266         if(!tab){
37267             return null;
37268         }
37269         if(tab == this.active || tab.disabled){
37270             return tab;
37271         }
37272         var e = {};
37273         this.fireEvent("beforetabchange", this, e, tab);
37274         if(e.cancel !== true && !tab.disabled){
37275             if(this.active){
37276                 this.active.hide();
37277             }
37278             this.active = this.items[id];
37279             this.active.show();
37280             this.fireEvent("tabchange", this, this.active);
37281         }
37282         return tab;
37283     },
37284
37285     /**
37286      * Gets the active {@link Roo.TabPanelItem}.
37287      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37288      */
37289     getActiveTab : function(){
37290         return this.active;
37291     },
37292
37293     /**
37294      * Updates the tab body element to fit the height of the container element
37295      * for overflow scrolling
37296      * @param {Number} targetHeight (optional) Override the starting height from the elements height
37297      */
37298     syncHeight : function(targetHeight){
37299         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37300         var bm = this.bodyEl.getMargins();
37301         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37302         this.bodyEl.setHeight(newHeight);
37303         return newHeight;
37304     },
37305
37306     onResize : function(){
37307         if(this.monitorResize){
37308             this.autoSizeTabs();
37309         }
37310     },
37311
37312     /**
37313      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37314      */
37315     beginUpdate : function(){
37316         this.updating = true;
37317     },
37318
37319     /**
37320      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37321      */
37322     endUpdate : function(){
37323         this.updating = false;
37324         this.autoSizeTabs();
37325     },
37326
37327     /**
37328      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37329      */
37330     autoSizeTabs : function(){
37331         var count = this.items.length;
37332         var vcount = count - this.hiddenCount;
37333         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37334             return;
37335         }
37336         var w = Math.max(this.el.getWidth() - this.cpad, 10);
37337         var availWidth = Math.floor(w / vcount);
37338         var b = this.stripBody;
37339         if(b.getWidth() > w){
37340             var tabs = this.items;
37341             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37342             if(availWidth < this.minTabWidth){
37343                 /*if(!this.sleft){    // incomplete scrolling code
37344                     this.createScrollButtons();
37345                 }
37346                 this.showScroll();
37347                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37348             }
37349         }else{
37350             if(this.currentTabWidth < this.preferredTabWidth){
37351                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37352             }
37353         }
37354     },
37355
37356     /**
37357      * Returns the number of tabs in this TabPanel.
37358      * @return {Number}
37359      */
37360      getCount : function(){
37361          return this.items.length;
37362      },
37363
37364     /**
37365      * Resizes all the tabs to the passed width
37366      * @param {Number} The new width
37367      */
37368     setTabWidth : function(width){
37369         this.currentTabWidth = width;
37370         for(var i = 0, len = this.items.length; i < len; i++) {
37371                 if(!this.items[i].isHidden()) {
37372                 this.items[i].setWidth(width);
37373             }
37374         }
37375     },
37376
37377     /**
37378      * Destroys this TabPanel
37379      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37380      */
37381     destroy : function(removeEl){
37382         Roo.EventManager.removeResizeListener(this.onResize, this);
37383         for(var i = 0, len = this.items.length; i < len; i++){
37384             this.items[i].purgeListeners();
37385         }
37386         if(removeEl === true){
37387             this.el.update("");
37388             this.el.remove();
37389         }
37390     },
37391     
37392     createStrip : function(container)
37393     {
37394         var strip = document.createElement("nav");
37395         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37396         container.appendChild(strip);
37397         return strip;
37398     },
37399     
37400     createStripList : function(strip)
37401     {
37402         // div wrapper for retard IE
37403         // returns the "tr" element.
37404         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
37405         //'<div class="x-tabs-strip-wrap">'+
37406           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
37407           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
37408         return strip.firstChild; //.firstChild.firstChild.firstChild;
37409     },
37410     createBody : function(container)
37411     {
37412         var body = document.createElement("div");
37413         Roo.id(body, "tab-body");
37414         //Roo.fly(body).addClass("x-tabs-body");
37415         Roo.fly(body).addClass("tab-content");
37416         container.appendChild(body);
37417         return body;
37418     },
37419     createItemBody :function(bodyEl, id){
37420         var body = Roo.getDom(id);
37421         if(!body){
37422             body = document.createElement("div");
37423             body.id = id;
37424         }
37425         //Roo.fly(body).addClass("x-tabs-item-body");
37426         Roo.fly(body).addClass("tab-pane");
37427          bodyEl.insertBefore(body, bodyEl.firstChild);
37428         return body;
37429     },
37430     /** @private */
37431     createStripElements :  function(stripEl, text, closable, tpl)
37432     {
37433         var td = document.createElement("li"); // was td..
37434         
37435         
37436         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
37437         
37438         
37439         stripEl.appendChild(td);
37440         /*if(closable){
37441             td.className = "x-tabs-closable";
37442             if(!this.closeTpl){
37443                 this.closeTpl = new Roo.Template(
37444                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37445                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
37446                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
37447                 );
37448             }
37449             var el = this.closeTpl.overwrite(td, {"text": text});
37450             var close = el.getElementsByTagName("div")[0];
37451             var inner = el.getElementsByTagName("em")[0];
37452             return {"el": el, "close": close, "inner": inner};
37453         } else {
37454         */
37455         // not sure what this is..
37456 //            if(!this.tabTpl){
37457                 //this.tabTpl = new Roo.Template(
37458                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37459                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
37460                 //);
37461 //                this.tabTpl = new Roo.Template(
37462 //                   '<a href="#">' +
37463 //                   '<span unselectable="on"' +
37464 //                            (this.disableTooltips ? '' : ' title="{text}"') +
37465 //                            ' >{text}</span></a>'
37466 //                );
37467 //                
37468 //            }
37469
37470
37471             var template = tpl || this.tabTpl || false;
37472             
37473             if(!template){
37474                 
37475                 template = new Roo.Template(
37476                    '<a href="#">' +
37477                    '<span unselectable="on"' +
37478                             (this.disableTooltips ? '' : ' title="{text}"') +
37479                             ' >{text}</span></a>'
37480                 );
37481             }
37482             
37483             switch (typeof(template)) {
37484                 case 'object' :
37485                     break;
37486                 case 'string' :
37487                     template = new Roo.Template(template);
37488                     break;
37489                 default :
37490                     break;
37491             }
37492             
37493             var el = template.overwrite(td, {"text": text});
37494             
37495             var inner = el.getElementsByTagName("span")[0];
37496             
37497             return {"el": el, "inner": inner};
37498             
37499     }
37500         
37501     
37502 });
37503
37504 /**
37505  * @class Roo.TabPanelItem
37506  * @extends Roo.util.Observable
37507  * Represents an individual item (tab plus body) in a TabPanel.
37508  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
37509  * @param {String} id The id of this TabPanelItem
37510  * @param {String} text The text for the tab of this TabPanelItem
37511  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
37512  */
37513 Roo.bootstrap.panel.TabItem = function(config){
37514     /**
37515      * The {@link Roo.TabPanel} this TabPanelItem belongs to
37516      * @type Roo.TabPanel
37517      */
37518     this.tabPanel = config.panel;
37519     /**
37520      * The id for this TabPanelItem
37521      * @type String
37522      */
37523     this.id = config.id;
37524     /** @private */
37525     this.disabled = false;
37526     /** @private */
37527     this.text = config.text;
37528     /** @private */
37529     this.loaded = false;
37530     this.closable = config.closable;
37531
37532     /**
37533      * The body element for this TabPanelItem.
37534      * @type Roo.Element
37535      */
37536     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
37537     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
37538     this.bodyEl.setStyle("display", "block");
37539     this.bodyEl.setStyle("zoom", "1");
37540     //this.hideAction();
37541
37542     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
37543     /** @private */
37544     this.el = Roo.get(els.el);
37545     this.inner = Roo.get(els.inner, true);
37546     this.textEl = Roo.get(this.el.dom.firstChild, true);
37547     this.pnode = Roo.get(els.el.parentNode, true);
37548     this.el.on("mousedown", this.onTabMouseDown, this);
37549     this.el.on("click", this.onTabClick, this);
37550     /** @private */
37551     if(config.closable){
37552         var c = Roo.get(els.close, true);
37553         c.dom.title = this.closeText;
37554         c.addClassOnOver("close-over");
37555         c.on("click", this.closeClick, this);
37556      }
37557
37558     this.addEvents({
37559          /**
37560          * @event activate
37561          * Fires when this tab becomes the active tab.
37562          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37563          * @param {Roo.TabPanelItem} this
37564          */
37565         "activate": true,
37566         /**
37567          * @event beforeclose
37568          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
37569          * @param {Roo.TabPanelItem} this
37570          * @param {Object} e Set cancel to true on this object to cancel the close.
37571          */
37572         "beforeclose": true,
37573         /**
37574          * @event close
37575          * Fires when this tab is closed.
37576          * @param {Roo.TabPanelItem} this
37577          */
37578          "close": true,
37579         /**
37580          * @event deactivate
37581          * Fires when this tab is no longer the active tab.
37582          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37583          * @param {Roo.TabPanelItem} this
37584          */
37585          "deactivate" : true
37586     });
37587     this.hidden = false;
37588
37589     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
37590 };
37591
37592 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
37593            {
37594     purgeListeners : function(){
37595        Roo.util.Observable.prototype.purgeListeners.call(this);
37596        this.el.removeAllListeners();
37597     },
37598     /**
37599      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
37600      */
37601     show : function(){
37602         this.pnode.addClass("active");
37603         this.showAction();
37604         if(Roo.isOpera){
37605             this.tabPanel.stripWrap.repaint();
37606         }
37607         this.fireEvent("activate", this.tabPanel, this);
37608     },
37609
37610     /**
37611      * Returns true if this tab is the active tab.
37612      * @return {Boolean}
37613      */
37614     isActive : function(){
37615         return this.tabPanel.getActiveTab() == this;
37616     },
37617
37618     /**
37619      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
37620      */
37621     hide : function(){
37622         this.pnode.removeClass("active");
37623         this.hideAction();
37624         this.fireEvent("deactivate", this.tabPanel, this);
37625     },
37626
37627     hideAction : function(){
37628         this.bodyEl.hide();
37629         this.bodyEl.setStyle("position", "absolute");
37630         this.bodyEl.setLeft("-20000px");
37631         this.bodyEl.setTop("-20000px");
37632     },
37633
37634     showAction : function(){
37635         this.bodyEl.setStyle("position", "relative");
37636         this.bodyEl.setTop("");
37637         this.bodyEl.setLeft("");
37638         this.bodyEl.show();
37639     },
37640
37641     /**
37642      * Set the tooltip for the tab.
37643      * @param {String} tooltip The tab's tooltip
37644      */
37645     setTooltip : function(text){
37646         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
37647             this.textEl.dom.qtip = text;
37648             this.textEl.dom.removeAttribute('title');
37649         }else{
37650             this.textEl.dom.title = text;
37651         }
37652     },
37653
37654     onTabClick : function(e){
37655         e.preventDefault();
37656         this.tabPanel.activate(this.id);
37657     },
37658
37659     onTabMouseDown : function(e){
37660         e.preventDefault();
37661         this.tabPanel.activate(this.id);
37662     },
37663 /*
37664     getWidth : function(){
37665         return this.inner.getWidth();
37666     },
37667
37668     setWidth : function(width){
37669         var iwidth = width - this.pnode.getPadding("lr");
37670         this.inner.setWidth(iwidth);
37671         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
37672         this.pnode.setWidth(width);
37673     },
37674 */
37675     /**
37676      * Show or hide the tab
37677      * @param {Boolean} hidden True to hide or false to show.
37678      */
37679     setHidden : function(hidden){
37680         this.hidden = hidden;
37681         this.pnode.setStyle("display", hidden ? "none" : "");
37682     },
37683
37684     /**
37685      * Returns true if this tab is "hidden"
37686      * @return {Boolean}
37687      */
37688     isHidden : function(){
37689         return this.hidden;
37690     },
37691
37692     /**
37693      * Returns the text for this tab
37694      * @return {String}
37695      */
37696     getText : function(){
37697         return this.text;
37698     },
37699     /*
37700     autoSize : function(){
37701         //this.el.beginMeasure();
37702         this.textEl.setWidth(1);
37703         /*
37704          *  #2804 [new] Tabs in Roojs
37705          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
37706          */
37707         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
37708         //this.el.endMeasure();
37709     //},
37710
37711     /**
37712      * Sets the text for the tab (Note: this also sets the tooltip text)
37713      * @param {String} text The tab's text and tooltip
37714      */
37715     setText : function(text){
37716         this.text = text;
37717         this.textEl.update(text);
37718         this.setTooltip(text);
37719         //if(!this.tabPanel.resizeTabs){
37720         //    this.autoSize();
37721         //}
37722     },
37723     /**
37724      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
37725      */
37726     activate : function(){
37727         this.tabPanel.activate(this.id);
37728     },
37729
37730     /**
37731      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
37732      */
37733     disable : function(){
37734         if(this.tabPanel.active != this){
37735             this.disabled = true;
37736             this.pnode.addClass("disabled");
37737         }
37738     },
37739
37740     /**
37741      * Enables this TabPanelItem if it was previously disabled.
37742      */
37743     enable : function(){
37744         this.disabled = false;
37745         this.pnode.removeClass("disabled");
37746     },
37747
37748     /**
37749      * Sets the content for this TabPanelItem.
37750      * @param {String} content The content
37751      * @param {Boolean} loadScripts true to look for and load scripts
37752      */
37753     setContent : function(content, loadScripts){
37754         this.bodyEl.update(content, loadScripts);
37755     },
37756
37757     /**
37758      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
37759      * @return {Roo.UpdateManager} The UpdateManager
37760      */
37761     getUpdateManager : function(){
37762         return this.bodyEl.getUpdateManager();
37763     },
37764
37765     /**
37766      * Set a URL to be used to load the content for this TabPanelItem.
37767      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
37768      * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
37769      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this TabPanelItem is activated. (Defaults to false)
37770      * @return {Roo.UpdateManager} The UpdateManager
37771      */
37772     setUrl : function(url, params, loadOnce){
37773         if(this.refreshDelegate){
37774             this.un('activate', this.refreshDelegate);
37775         }
37776         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
37777         this.on("activate", this.refreshDelegate);
37778         return this.bodyEl.getUpdateManager();
37779     },
37780
37781     /** @private */
37782     _handleRefresh : function(url, params, loadOnce){
37783         if(!loadOnce || !this.loaded){
37784             var updater = this.bodyEl.getUpdateManager();
37785             updater.update(url, params, this._setLoaded.createDelegate(this));
37786         }
37787     },
37788
37789     /**
37790      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
37791      *   Will fail silently if the setUrl method has not been called.
37792      *   This does not activate the panel, just updates its content.
37793      */
37794     refresh : function(){
37795         if(this.refreshDelegate){
37796            this.loaded = false;
37797            this.refreshDelegate();
37798         }
37799     },
37800
37801     /** @private */
37802     _setLoaded : function(){
37803         this.loaded = true;
37804     },
37805
37806     /** @private */
37807     closeClick : function(e){
37808         var o = {};
37809         e.stopEvent();
37810         this.fireEvent("beforeclose", this, o);
37811         if(o.cancel !== true){
37812             this.tabPanel.removeTab(this.id);
37813         }
37814     },
37815     /**
37816      * The text displayed in the tooltip for the close icon.
37817      * @type String
37818      */
37819     closeText : "Close this tab"
37820 });
37821 /*
37822  * - LGPL
37823  *
37824  * element
37825  * 
37826  */
37827
37828 /**
37829  * @class Roo.bootstrap.PhoneInput
37830  * @extends Roo.bootstrap.TriggerField
37831  * Bootstrap PhoneInput class
37832  * 
37833  * @constructor
37834  * Create a new PhoneInput
37835  * @param {Object} config The config object
37836 */
37837
37838 Roo.bootstrap.PhoneInput = function(config){
37839     
37840     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
37841     
37842     this.addEvents({
37843         'touchviewdisplay' : true
37844     });
37845     
37846     this.item = []; //fetch country JSON
37847 };
37848
37849 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
37850      
37851      //setting properties..
37852      preferedCountries: undefined, //array
37853      
37854      filterCountries: undefined, //array
37855      
37856      displayMode: undefined, //string
37857      
37858      listWidth: undefined, //number
37859      
37860      validClass : "has-success",
37861      
37862      invalidClass: "has-warning",
37863      
37864      
37865     getAutoCreate : function()
37866     {  
37867         var cfg = false;
37868         
37869         //render
37870         /*
37871          * Render classic select for iso
37872          */
37873         if(Roo.isIOS && this.useNativeIOS){
37874             cfg = this.getAutoCreateNativeIOS();
37875             return cfg;
37876         }
37877         
37878         /*
37879          * Touch Devices
37880          */
37881         if(Roo.isTouch && this.mobileTouchView){
37882             cfg = this.getAutoCreateTouchView();
37883             return cfg;;
37884         }
37885         
37886         var align = this.labelAlign || this.parentLabelAlign();
37887         
37888         cfg = {
37889             cls : 'form-group'
37890         };
37891         
37892         var _this = this;
37893         
37894         var input =  {
37895             tag: 'input',
37896             id : id,
37897             type : this.inputType,
37898             cls : 'form-control',
37899             autocomplete: 'new-password',
37900             placeholder : this.placeholder || '' 
37901         };
37902         
37903         return cfg;
37904     },
37905     
37906     _initEventsCalled : false,
37907     
37908     // private
37909     initEvents: function()
37910     {   
37911         if (this._initEventsCalled) { // as we call render... prevent looping...
37912             return;
37913         }
37914         this._initEventsCalled = true;
37915         
37916         if (!this.store) {
37917             throw "can not find store for combo";
37918         }
37919         
37920         this.store = Roo.factory(this.store, Roo.data);
37921         this.store.parent = this;
37922         
37923         // if we are building from html. then this element is so complex, that we can not really
37924         // use the rendered HTML.
37925         // so we have to trash and replace the previous code.
37926         if (Roo.XComponent.build_from_html) {
37927             
37928             // remove this element....
37929             var e = this.el.dom, k=0;
37930             while (e ) { e = e.previousSibling;  ++k;}
37931
37932             this.el.remove();
37933             
37934             this.el=false;
37935             this.rendered = false;
37936             
37937             this.render(this.parent().getChildContainer(true), k);
37938             
37939             
37940             
37941         }
37942         
37943         if(Roo.isIOS && this.useNativeIOS){
37944             this.initIOSView();
37945             return;
37946         }
37947         
37948         /*
37949          * Touch Devices
37950          */
37951         
37952         if(Roo.isTouch && this.mobileTouchView){
37953             this.initTouchView();
37954             return;
37955         }
37956         
37957         if(this.tickable){
37958             this.initTickableEvents();
37959             return;
37960         }
37961         
37962         Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
37963         
37964         if(this.hiddenName){
37965             
37966             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
37967             
37968             this.hiddenField.dom.value =
37969                 this.hiddenValue !== undefined ? this.hiddenValue :
37970                 this.value !== undefined ? this.value : '';
37971
37972             // prevent input submission
37973             this.el.dom.removeAttribute('name');
37974             this.hiddenField.dom.setAttribute('name', this.hiddenName);
37975              
37976              
37977         }
37978         //if(Roo.isGecko){
37979         //    this.el.dom.setAttribute('autocomplete', 'off');
37980         //}
37981         
37982         var cls = 'x-combo-list';
37983         
37984         //this.list = new Roo.Layer({
37985         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
37986         //});
37987         
37988         var _this = this;
37989         
37990         (function(){
37991             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
37992             _this.list.setWidth(lw);
37993         }).defer(100);
37994         
37995         this.list.on('mouseover', this.onViewOver, this);
37996         this.list.on('mousemove', this.onViewMove, this);
37997         
37998         this.list.on('scroll', this.onViewScroll, this);
37999         
38000         /*
38001         this.list.swallowEvent('mousewheel');
38002         this.assetHeight = 0;
38003
38004         if(this.title){
38005             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
38006             this.assetHeight += this.header.getHeight();
38007         }
38008
38009         this.innerList = this.list.createChild({cls:cls+'-inner'});
38010         this.innerList.on('mouseover', this.onViewOver, this);
38011         this.innerList.on('mousemove', this.onViewMove, this);
38012         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
38013         
38014         if(this.allowBlank && !this.pageSize && !this.disableClear){
38015             this.footer = this.list.createChild({cls:cls+'-ft'});
38016             this.pageTb = new Roo.Toolbar(this.footer);
38017            
38018         }
38019         if(this.pageSize){
38020             this.footer = this.list.createChild({cls:cls+'-ft'});
38021             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
38022                     {pageSize: this.pageSize});
38023             
38024         }
38025         
38026         if (this.pageTb && this.allowBlank && !this.disableClear) {
38027             var _this = this;
38028             this.pageTb.add(new Roo.Toolbar.Fill(), {
38029                 cls: 'x-btn-icon x-btn-clear',
38030                 text: '&#160;',
38031                 handler: function()
38032                 {
38033                     _this.collapse();
38034                     _this.clearValue();
38035                     _this.onSelect(false, -1);
38036                 }
38037             });
38038         }
38039         if (this.footer) {
38040             this.assetHeight += this.footer.getHeight();
38041         }
38042         */
38043             
38044         if(!this.tpl){
38045             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
38046         }
38047
38048         this.view = new Roo.View(this.list, this.tpl, {
38049             singleSelect:true, store: this.store, selectedClass: this.selectedClass
38050         });
38051         //this.view.wrapEl.setDisplayed(false);
38052         this.view.on('click', this.onViewClick, this);
38053         
38054         
38055         this.store.on('beforeload', this.onBeforeLoad, this);
38056         this.store.on('load', this.onLoad, this);
38057         this.store.on('loadexception', this.onLoadException, this);
38058         /*
38059         if(this.resizable){
38060             this.resizer = new Roo.Resizable(this.list,  {
38061                pinned:true, handles:'se'
38062             });
38063             this.resizer.on('resize', function(r, w, h){
38064                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
38065                 this.listWidth = w;
38066                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
38067                 this.restrictHeight();
38068             }, this);
38069             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
38070         }
38071         */
38072         if(!this.editable){
38073             this.editable = true;
38074             this.setEditable(false);
38075         }
38076         
38077         /*
38078         
38079         if (typeof(this.events.add.listeners) != 'undefined') {
38080             
38081             this.addicon = this.wrap.createChild(
38082                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
38083        
38084             this.addicon.on('click', function(e) {
38085                 this.fireEvent('add', this);
38086             }, this);
38087         }
38088         if (typeof(this.events.edit.listeners) != 'undefined') {
38089             
38090             this.editicon = this.wrap.createChild(
38091                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
38092             if (this.addicon) {
38093                 this.editicon.setStyle('margin-left', '40px');
38094             }
38095             this.editicon.on('click', function(e) {
38096                 
38097                 // we fire even  if inothing is selected..
38098                 this.fireEvent('edit', this, this.lastData );
38099                 
38100             }, this);
38101         }
38102         */
38103         
38104         this.keyNav = new Roo.KeyNav(this.inputEl(), {
38105             "up" : function(e){
38106                 this.inKeyMode = true;
38107                 this.selectPrev();
38108             },
38109
38110             "down" : function(e){
38111                 if(!this.isExpanded()){
38112                     this.onTriggerClick();
38113                 }else{
38114                     this.inKeyMode = true;
38115                     this.selectNext();
38116                 }
38117             },
38118
38119             "enter" : function(e){
38120 //                this.onViewClick();
38121                 //return true;
38122                 this.collapse();
38123                 
38124                 if(this.fireEvent("specialkey", this, e)){
38125                     this.onViewClick(false);
38126                 }
38127                 
38128                 return true;
38129             },
38130
38131             "esc" : function(e){
38132                 this.collapse();
38133             },
38134
38135             "tab" : function(e){
38136                 this.collapse();
38137                 
38138                 if(this.fireEvent("specialkey", this, e)){
38139                     this.onViewClick(false);
38140                 }
38141                 
38142                 return true;
38143             },
38144
38145             scope : this,
38146
38147             doRelay : function(foo, bar, hname){
38148                 if(hname == 'down' || this.scope.isExpanded()){
38149                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
38150                 }
38151                 return true;
38152             },
38153
38154             forceKeyDown: true
38155         });
38156         
38157         
38158         this.queryDelay = Math.max(this.queryDelay || 10,
38159                 this.mode == 'local' ? 10 : 250);
38160         
38161         
38162         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
38163         
38164         if(this.typeAhead){
38165             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
38166         }
38167         if(this.editable !== false){
38168             this.inputEl().on("keyup", this.onKeyUp, this);
38169         }
38170         if(this.forceSelection){
38171             this.inputEl().on('blur', this.doForce, this);
38172         }
38173         
38174         if(this.multiple){
38175             this.choices = this.el.select('ul.roo-select2-choices', true).first();
38176             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
38177         }
38178     },
38179     
38180     initTickableEvents: function()
38181     {   
38182         this.createList();
38183         
38184         if(this.hiddenName){
38185             
38186             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
38187             
38188             this.hiddenField.dom.value =
38189                 this.hiddenValue !== undefined ? this.hiddenValue :
38190                 this.value !== undefined ? this.value : '';
38191
38192             // prevent input submission
38193             this.el.dom.removeAttribute('name');
38194             this.hiddenField.dom.setAttribute('name', this.hiddenName);
38195              
38196              
38197         }
38198         
38199 //        this.list = this.el.select('ul.dropdown-menu',true).first();
38200         
38201         this.choices = this.el.select('ul.roo-select2-choices', true).first();
38202         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
38203         if(this.triggerList){
38204             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
38205         }
38206          
38207         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
38208         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
38209         
38210         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
38211         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
38212         
38213         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
38214         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
38215         
38216         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
38217         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
38218         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
38219         
38220         this.okBtn.hide();
38221         this.cancelBtn.hide();
38222         
38223         var _this = this;
38224         
38225         (function(){
38226             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
38227             _this.list.setWidth(lw);
38228         }).defer(100);
38229         
38230         this.list.on('mouseover', this.onViewOver, this);
38231         this.list.on('mousemove', this.onViewMove, this);
38232         
38233         this.list.on('scroll', this.onViewScroll, this);
38234         
38235         if(!this.tpl){
38236             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>';
38237         }
38238
38239         this.view = new Roo.View(this.list, this.tpl, {
38240             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
38241         });
38242         
38243         //this.view.wrapEl.setDisplayed(false);
38244         this.view.on('click', this.onViewClick, this);
38245         
38246         
38247         
38248         this.store.on('beforeload', this.onBeforeLoad, this);
38249         this.store.on('load', this.onLoad, this);
38250         this.store.on('loadexception', this.onLoadException, this);
38251         
38252         if(this.editable){
38253             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
38254                 "up" : function(e){
38255                     this.inKeyMode = true;
38256                     this.selectPrev();
38257                 },
38258
38259                 "down" : function(e){
38260                     this.inKeyMode = true;
38261                     this.selectNext();
38262                 },
38263
38264                 "enter" : function(e){
38265                     if(this.fireEvent("specialkey", this, e)){
38266                         this.onViewClick(false);
38267                     }
38268                     
38269                     return true;
38270                 },
38271
38272                 "esc" : function(e){
38273                     this.onTickableFooterButtonClick(e, false, false);
38274                 },
38275
38276                 "tab" : function(e){
38277                     this.fireEvent("specialkey", this, e);
38278                     
38279                     this.onTickableFooterButtonClick(e, false, false);
38280                     
38281                     return true;
38282                 },
38283
38284                 scope : this,
38285
38286                 doRelay : function(e, fn, key){
38287                     if(this.scope.isExpanded()){
38288                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
38289                     }
38290                     return true;
38291                 },
38292
38293                 forceKeyDown: true
38294             });
38295         }
38296         
38297         this.queryDelay = Math.max(this.queryDelay || 10,
38298                 this.mode == 'local' ? 10 : 250);
38299         
38300         
38301         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
38302         
38303         if(this.typeAhead){
38304             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
38305         }
38306         
38307         if(this.editable !== false){
38308             this.tickableInputEl().on("keyup", this.onKeyUp, this);
38309         }
38310         
38311         this.indicator = this.indicatorEl();
38312         
38313         if(this.indicator){
38314             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
38315             this.indicator.hide();
38316         }
38317         
38318     },
38319
38320     onDestroy : function(){
38321         if(this.view){
38322             this.view.setStore(null);
38323             this.view.el.removeAllListeners();
38324             this.view.el.remove();
38325             this.view.purgeListeners();
38326         }
38327         if(this.list){
38328             this.list.dom.innerHTML  = '';
38329         }
38330         
38331         if(this.store){
38332             this.store.un('beforeload', this.onBeforeLoad, this);
38333             this.store.un('load', this.onLoad, this);
38334             this.store.un('loadexception', this.onLoadException, this);
38335         }
38336         Roo.bootstrap.PhoneInput.superclass.onDestroy.call(this);
38337     },
38338
38339     // private
38340     fireKey : function(e){
38341         if(e.isNavKeyPress() && !this.list.isVisible()){
38342             this.fireEvent("specialkey", this, e);
38343         }
38344     },
38345
38346     // private
38347     onResize: function(w, h){
38348 //        Roo.bootstrap.PhoneInput.superclass.onResize.apply(this, arguments);
38349 //        
38350 //        if(typeof w != 'number'){
38351 //            // we do not handle it!?!?
38352 //            return;
38353 //        }
38354 //        var tw = this.trigger.getWidth();
38355 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
38356 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
38357 //        var x = w - tw;
38358 //        this.inputEl().setWidth( this.adjustWidth('input', x));
38359 //            
38360 //        //this.trigger.setStyle('left', x+'px');
38361 //        
38362 //        if(this.list && this.listWidth === undefined){
38363 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
38364 //            this.list.setWidth(lw);
38365 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
38366 //        }
38367         
38368     
38369         
38370     },
38371
38372     /**
38373      * Allow or prevent the user from directly editing the field text.  If false is passed,
38374      * the user will only be able to select from the items defined in the dropdown list.  This method
38375      * is the runtime equivalent of setting the 'editable' config option at config time.
38376      * @param {Boolean} value True to allow the user to directly edit the field text
38377      */
38378     setEditable : function(value){
38379         if(value == this.editable){
38380             return;
38381         }
38382         this.editable = value;
38383         if(!value){
38384             this.inputEl().dom.setAttribute('readOnly', true);
38385             this.inputEl().on('mousedown', this.onTriggerClick,  this);
38386             this.inputEl().addClass('x-combo-noedit');
38387         }else{
38388             this.inputEl().dom.setAttribute('readOnly', false);
38389             this.inputEl().un('mousedown', this.onTriggerClick,  this);
38390             this.inputEl().removeClass('x-combo-noedit');
38391         }
38392     },
38393
38394     // private
38395     
38396     onBeforeLoad : function(combo,opts){
38397         if(!this.hasFocus){
38398             return;
38399         }
38400          if (!opts.add) {
38401             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
38402          }
38403         this.restrictHeight();
38404         this.selectedIndex = -1;
38405     },
38406
38407     // private
38408     onLoad : function(){
38409         
38410         this.hasQuery = false;
38411         
38412         if(!this.hasFocus){
38413             return;
38414         }
38415         
38416         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
38417             this.loading.hide();
38418         }
38419         
38420         if(this.store.getCount() > 0){
38421             
38422             this.expand();
38423             this.restrictHeight();
38424             if(this.lastQuery == this.allQuery){
38425                 if(this.editable && !this.tickable){
38426                     this.inputEl().dom.select();
38427                 }
38428                 
38429                 if(
38430                     !this.selectByValue(this.value, true) &&
38431                     this.autoFocus && 
38432                     (
38433                         !this.store.lastOptions ||
38434                         typeof(this.store.lastOptions.add) == 'undefined' || 
38435                         this.store.lastOptions.add != true
38436                     )
38437                 ){
38438                     this.select(0, true);
38439                 }
38440             }else{
38441                 if(this.autoFocus){
38442                     this.selectNext();
38443                 }
38444                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
38445                     this.taTask.delay(this.typeAheadDelay);
38446                 }
38447             }
38448         }else{
38449             this.onEmptyResults();
38450         }
38451         
38452         //this.el.focus();
38453     },
38454     // private
38455     onLoadException : function()
38456     {
38457         this.hasQuery = false;
38458         
38459         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
38460             this.loading.hide();
38461         }
38462         
38463         if(this.tickable && this.editable){
38464             return;
38465         }
38466         
38467         this.collapse();
38468         // only causes errors at present
38469         //Roo.log(this.store.reader.jsonData);
38470         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
38471             // fixme
38472             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
38473         //}
38474         
38475         
38476     },
38477     // private
38478     onTypeAhead : function(){
38479         if(this.store.getCount() > 0){
38480             var r = this.store.getAt(0);
38481             var newValue = r.data[this.displayField];
38482             var len = newValue.length;
38483             var selStart = this.getRawValue().length;
38484             
38485             if(selStart != len){
38486                 this.setRawValue(newValue);
38487                 this.selectText(selStart, newValue.length);
38488             }
38489         }
38490     },
38491
38492     // private
38493     onSelect : function(record, index){
38494         
38495         if(this.fireEvent('beforeselect', this, record, index) !== false){
38496         
38497             this.setFromData(index > -1 ? record.data : false);
38498             
38499             this.collapse();
38500             this.fireEvent('select', this, record, index);
38501         }
38502     },
38503
38504     /**
38505      * Returns the currently selected field value or empty string if no value is set.
38506      * @return {String} value The selected value
38507      */
38508     getValue : function()
38509     {
38510         if(Roo.isIOS && this.useNativeIOS){
38511             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
38512         }
38513         
38514         if(this.multiple){
38515             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
38516         }
38517         
38518         if(this.valueField){
38519             return typeof this.value != 'undefined' ? this.value : '';
38520         }else{
38521             return Roo.bootstrap.PhoneInput.superclass.getValue.call(this);
38522         }
38523     },
38524     
38525     getRawValue : function()
38526     {
38527         if(Roo.isIOS && this.useNativeIOS){
38528             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
38529         }
38530         
38531         var v = this.inputEl().getValue();
38532         
38533         return v;
38534     },
38535
38536     /**
38537      * Clears any text/value currently set in the field
38538      */
38539     clearValue : function(){
38540         
38541         if(this.hiddenField){
38542             this.hiddenField.dom.value = '';
38543         }
38544         this.value = '';
38545         this.setRawValue('');
38546         this.lastSelectionText = '';
38547         this.lastData = false;
38548         
38549         var close = this.closeTriggerEl();
38550         
38551         if(close){
38552             close.hide();
38553         }
38554         
38555         this.validate();
38556         
38557     },
38558
38559     /**
38560      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
38561      * will be displayed in the field.  If the value does not match the data value of an existing item,
38562      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
38563      * Otherwise the field will be blank (although the value will still be set).
38564      * @param {String} value The value to match
38565      */
38566     setValue : function(v)
38567     {
38568         if(Roo.isIOS && this.useNativeIOS){
38569             this.setIOSValue(v);
38570             return;
38571         }
38572         
38573         if(this.multiple){
38574             this.syncValue();
38575             return;
38576         }
38577         
38578         var text = v;
38579         if(this.valueField){
38580             var r = this.findRecord(this.valueField, v);
38581             if(r){
38582                 text = r.data[this.displayField];
38583             }else if(this.valueNotFoundText !== undefined){
38584                 text = this.valueNotFoundText;
38585             }
38586         }
38587         this.lastSelectionText = text;
38588         if(this.hiddenField){
38589             this.hiddenField.dom.value = v;
38590         }
38591         Roo.bootstrap.PhoneInput.superclass.setValue.call(this, text);
38592         this.value = v;
38593         
38594         var close = this.closeTriggerEl();
38595         
38596         if(close){
38597             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
38598         }
38599         
38600         this.validate();
38601     },
38602     /**
38603      * @property {Object} the last set data for the element
38604      */
38605     
38606     lastData : false,
38607     /**
38608      * Sets the value of the field based on a object which is related to the record format for the store.
38609      * @param {Object} value the value to set as. or false on reset?
38610      */
38611     setFromData : function(o){
38612         
38613         if(this.multiple){
38614             this.addItem(o);
38615             return;
38616         }
38617             
38618         var dv = ''; // display value
38619         var vv = ''; // value value..
38620         this.lastData = o;
38621         if (this.displayField) {
38622             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
38623         } else {
38624             // this is an error condition!!!
38625             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
38626         }
38627         
38628         if(this.valueField){
38629             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
38630         }
38631         
38632         var close = this.closeTriggerEl();
38633         
38634         if(close){
38635             (vv.length || vv * 1 > 0) ? close.show() : close.hide();
38636         }
38637         
38638         if(this.hiddenField){
38639             this.hiddenField.dom.value = vv;
38640             
38641             this.lastSelectionText = dv;
38642             Roo.bootstrap.PhoneInput.superclass.setValue.call(this, dv);
38643             this.value = vv;
38644             return;
38645         }
38646         // no hidden field.. - we store the value in 'value', but still display
38647         // display field!!!!
38648         this.lastSelectionText = dv;
38649         Roo.bootstrap.PhoneInput.superclass.setValue.call(this, dv);
38650         this.value = vv;
38651         
38652         
38653         
38654     },
38655     // private
38656     reset : function(){
38657         // overridden so that last data is reset..
38658         
38659         if(this.multiple){
38660             this.clearItem();
38661             return;
38662         }
38663         
38664         this.setValue(this.originalValue);
38665         //this.clearInvalid();
38666         this.lastData = false;
38667         if (this.view) {
38668             this.view.clearSelections();
38669         }
38670         
38671         this.validate();
38672     },
38673     // private
38674     findRecord : function(prop, value){
38675         var record;
38676         if(this.store.getCount() > 0){
38677             this.store.each(function(r){
38678                 if(r.data[prop] == value){
38679                     record = r;
38680                     return false;
38681                 }
38682                 return true;
38683             });
38684         }
38685         return record;
38686     },
38687     
38688     getName: function()
38689     {
38690         // returns hidden if it's set..
38691         if (!this.rendered) {return ''};
38692         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
38693         
38694     },
38695     // private
38696     onViewMove : function(e, t){
38697         this.inKeyMode = false;
38698     },
38699
38700     // private
38701     onViewOver : function(e, t){
38702         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
38703             return;
38704         }
38705         var item = this.view.findItemFromChild(t);
38706         
38707         if(item){
38708             var index = this.view.indexOf(item);
38709             this.select(index, false);
38710         }
38711     },
38712
38713     // private
38714     onViewClick : function(view, doFocus, el, e)
38715     {
38716         var index = this.view.getSelectedIndexes()[0];
38717         
38718         var r = this.store.getAt(index);
38719         
38720         if(this.tickable){
38721             
38722             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
38723                 return;
38724             }
38725             
38726             var rm = false;
38727             var _this = this;
38728             
38729             Roo.each(this.tickItems, function(v,k){
38730                 
38731                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
38732                     _this.tickItems.splice(k, 1);
38733                     
38734                     if(typeof(e) == 'undefined' && view == false){
38735                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
38736                     }
38737                     
38738                     rm = true;
38739                     return;
38740                 }
38741             });
38742             
38743             if(rm){
38744                 return;
38745             }
38746             
38747             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
38748                 this.tickItems.push(r.data);
38749             }
38750             
38751             if(typeof(e) == 'undefined' && view == false){
38752                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
38753             }
38754                     
38755             return;
38756         }
38757         
38758         if(r){
38759             this.onSelect(r, index);
38760         }
38761         if(doFocus !== false && !this.blockFocus){
38762             this.inputEl().focus();
38763         }
38764     },
38765
38766     // private
38767     restrictHeight : function(){
38768         //this.innerList.dom.style.height = '';
38769         //var inner = this.innerList.dom;
38770         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
38771         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
38772         //this.list.beginUpdate();
38773         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
38774         this.list.alignTo(this.inputEl(), this.listAlign);
38775         this.list.alignTo(this.inputEl(), this.listAlign);
38776         //this.list.endUpdate();
38777     },
38778
38779     // private
38780     onEmptyResults : function(){
38781         
38782         if(this.tickable && this.editable){
38783             this.restrictHeight();
38784             return;
38785         }
38786         
38787         this.collapse();
38788     },
38789
38790     /**
38791      * Returns true if the dropdown list is expanded, else false.
38792      */
38793     isExpanded : function(){
38794         return this.list.isVisible();
38795     },
38796
38797     /**
38798      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
38799      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
38800      * @param {String} value The data value of the item to select
38801      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
38802      * selected item if it is not currently in view (defaults to true)
38803      * @return {Boolean} True if the value matched an item in the list, else false
38804      */
38805     selectByValue : function(v, scrollIntoView){
38806         if(v !== undefined && v !== null){
38807             var r = this.findRecord(this.valueField || this.displayField, v);
38808             if(r){
38809                 this.select(this.store.indexOf(r), scrollIntoView);
38810                 return true;
38811             }
38812         }
38813         return false;
38814     },
38815
38816     /**
38817      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
38818      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
38819      * @param {Number} index The zero-based index of the list item to select
38820      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
38821      * selected item if it is not currently in view (defaults to true)
38822      */
38823     select : function(index, scrollIntoView){
38824         this.selectedIndex = index;
38825         this.view.select(index);
38826         if(scrollIntoView !== false){
38827             var el = this.view.getNode(index);
38828             /*
38829              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
38830              */
38831             if(el){
38832                 this.list.scrollChildIntoView(el, false);
38833             }
38834         }
38835     },
38836
38837     // private
38838     selectNext : function(){
38839         var ct = this.store.getCount();
38840         if(ct > 0){
38841             if(this.selectedIndex == -1){
38842                 this.select(0);
38843             }else if(this.selectedIndex < ct-1){
38844                 this.select(this.selectedIndex+1);
38845             }
38846         }
38847     },
38848
38849     // private
38850     selectPrev : function(){
38851         var ct = this.store.getCount();
38852         if(ct > 0){
38853             if(this.selectedIndex == -1){
38854                 this.select(0);
38855             }else if(this.selectedIndex != 0){
38856                 this.select(this.selectedIndex-1);
38857             }
38858         }
38859     },
38860
38861     // private
38862     onKeyUp : function(e){
38863         if(this.editable !== false && !e.isSpecialKey()){
38864             this.lastKey = e.getKey();
38865             this.dqTask.delay(this.queryDelay);
38866         }
38867     },
38868
38869     // private
38870     validateBlur : function(){
38871         return !this.list || !this.list.isVisible();   
38872     },
38873
38874     // private
38875     initQuery : function(){
38876         
38877         var v = this.getRawValue();
38878         
38879         if(this.tickable && this.editable){
38880             v = this.tickableInputEl().getValue();
38881         }
38882         
38883         this.doQuery(v);
38884     },
38885
38886     // private
38887     doForce : function(){
38888         if(this.inputEl().dom.value.length > 0){
38889             this.inputEl().dom.value =
38890                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
38891              
38892         }
38893     },
38894
38895     /**
38896      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
38897      * query allowing the query action to be canceled if needed.
38898      * @param {String} query The SQL query to execute
38899      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
38900      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
38901      * saved in the current store (defaults to false)
38902      */
38903     doQuery : function(q, forceAll){
38904         
38905         if(q === undefined || q === null){
38906             q = '';
38907         }
38908         var qe = {
38909             query: q,
38910             forceAll: forceAll,
38911             combo: this,
38912             cancel:false
38913         };
38914         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
38915             return false;
38916         }
38917         q = qe.query;
38918         
38919         forceAll = qe.forceAll;
38920         if(forceAll === true || (q.length >= this.minChars)){
38921             
38922             this.hasQuery = true;
38923             
38924             if(this.lastQuery != q || this.alwaysQuery){
38925                 this.lastQuery = q;
38926                 if(this.mode == 'local'){
38927                     this.selectedIndex = -1;
38928                     if(forceAll){
38929                         this.store.clearFilter();
38930                     }else{
38931                         
38932                         if(this.specialFilter){
38933                             this.fireEvent('specialfilter', this);
38934                             this.onLoad();
38935                             return;
38936                         }
38937                         
38938                         this.store.filter(this.displayField, q);
38939                     }
38940                     
38941                     this.store.fireEvent("datachanged", this.store);
38942                     
38943                     this.onLoad();
38944                     
38945                     
38946                 }else{
38947                     
38948                     this.store.baseParams[this.queryParam] = q;
38949                     
38950                     var options = {params : this.getParams(q)};
38951                     
38952                     if(this.loadNext){
38953                         options.add = true;
38954                         options.params.start = this.page * this.pageSize;
38955                     }
38956                     
38957                     this.store.load(options);
38958                     
38959                     /*
38960                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
38961                      *  we should expand the list on onLoad
38962                      *  so command out it
38963                      */
38964 //                    this.expand();
38965                 }
38966             }else{
38967                 this.selectedIndex = -1;
38968                 this.onLoad();   
38969             }
38970         }
38971         
38972         this.loadNext = false;
38973     },
38974     
38975     // private
38976     getParams : function(q){
38977         var p = {};
38978         //p[this.queryParam] = q;
38979         
38980         if(this.pageSize){
38981             p.start = 0;
38982             p.limit = this.pageSize;
38983         }
38984         return p;
38985     },
38986
38987     /**
38988      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
38989      */
38990     collapse : function(){
38991         if(!this.isExpanded()){
38992             return;
38993         }
38994         
38995         this.list.hide();
38996         
38997         this.hasFocus = false;
38998         
38999         if(this.tickable){
39000             this.okBtn.hide();
39001             this.cancelBtn.hide();
39002             this.trigger.show();
39003             
39004             if(this.editable){
39005                 this.tickableInputEl().dom.value = '';
39006                 this.tickableInputEl().blur();
39007             }
39008             
39009         }
39010         
39011         Roo.get(document).un('mousedown', this.collapseIf, this);
39012         Roo.get(document).un('mousewheel', this.collapseIf, this);
39013         if (!this.editable) {
39014             Roo.get(document).un('keydown', this.listKeyPress, this);
39015         }
39016         this.fireEvent('collapse', this);
39017         
39018         this.validate();
39019     },
39020
39021     // private
39022     collapseIf : function(e){
39023         var in_combo  = e.within(this.el);
39024         var in_list =  e.within(this.list);
39025         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
39026         
39027         if (in_combo || in_list || is_list) {
39028             //e.stopPropagation();
39029             return;
39030         }
39031         
39032         if(this.tickable){
39033             this.onTickableFooterButtonClick(e, false, false);
39034         }
39035
39036         this.collapse();
39037         
39038     },
39039
39040     /**
39041      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
39042      */
39043     expand : function(){
39044        
39045         if(this.isExpanded() || !this.hasFocus){
39046             return;
39047         }
39048         
39049         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
39050         this.list.setWidth(lw);
39051         
39052         Roo.log('expand');
39053         
39054         this.list.show();
39055         
39056         this.restrictHeight();
39057         
39058         if(this.tickable){
39059             
39060             this.tickItems = Roo.apply([], this.item);
39061             
39062             this.okBtn.show();
39063             this.cancelBtn.show();
39064             this.trigger.hide();
39065             
39066             if(this.editable){
39067                 this.tickableInputEl().focus();
39068             }
39069             
39070         }
39071         
39072         Roo.get(document).on('mousedown', this.collapseIf, this);
39073         Roo.get(document).on('mousewheel', this.collapseIf, this);
39074         if (!this.editable) {
39075             Roo.get(document).on('keydown', this.listKeyPress, this);
39076         }
39077         
39078         this.fireEvent('expand', this);
39079     },
39080
39081     // private
39082     // Implements the default empty TriggerField.onTriggerClick function
39083     onTriggerClick : function(e)
39084     {
39085         Roo.log('trigger click');
39086         
39087         if(this.disabled || !this.triggerList){
39088             return;
39089         }
39090         
39091         this.page = 0;
39092         this.loadNext = false;
39093         
39094         if(this.isExpanded()){
39095             this.collapse();
39096             if (!this.blockFocus) {
39097                 this.inputEl().focus();
39098             }
39099             
39100         }else {
39101             this.hasFocus = true;
39102             if(this.triggerAction == 'all') {
39103                 this.doQuery(this.allQuery, true);
39104             } else {
39105                 this.doQuery(this.getRawValue());
39106             }
39107             if (!this.blockFocus) {
39108                 this.inputEl().focus();
39109             }
39110         }
39111     },
39112     
39113     onTickableTriggerClick : function(e)
39114     {
39115         if(this.disabled){
39116             return;
39117         }
39118         
39119         this.page = 0;
39120         this.loadNext = false;
39121         this.hasFocus = true;
39122         
39123         if(this.triggerAction == 'all') {
39124             this.doQuery(this.allQuery, true);
39125         } else {
39126             this.doQuery(this.getRawValue());
39127         }
39128     },
39129     
39130     onSearchFieldClick : function(e)
39131     {
39132         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
39133             this.onTickableFooterButtonClick(e, false, false);
39134             return;
39135         }
39136         
39137         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
39138             return;
39139         }
39140         
39141         this.page = 0;
39142         this.loadNext = false;
39143         this.hasFocus = true;
39144         
39145         if(this.triggerAction == 'all') {
39146             this.doQuery(this.allQuery, true);
39147         } else {
39148             this.doQuery(this.getRawValue());
39149         }
39150     },
39151     
39152     listKeyPress : function(e)
39153     {
39154         //Roo.log('listkeypress');
39155         // scroll to first matching element based on key pres..
39156         if (e.isSpecialKey()) {
39157             return false;
39158         }
39159         var k = String.fromCharCode(e.getKey()).toUpperCase();
39160         //Roo.log(k);
39161         var match  = false;
39162         var csel = this.view.getSelectedNodes();
39163         var cselitem = false;
39164         if (csel.length) {
39165             var ix = this.view.indexOf(csel[0]);
39166             cselitem  = this.store.getAt(ix);
39167             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
39168                 cselitem = false;
39169             }
39170             
39171         }
39172         
39173         this.store.each(function(v) { 
39174             if (cselitem) {
39175                 // start at existing selection.
39176                 if (cselitem.id == v.id) {
39177                     cselitem = false;
39178                 }
39179                 return true;
39180             }
39181                 
39182             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
39183                 match = this.store.indexOf(v);
39184                 return false;
39185             }
39186             return true;
39187         }, this);
39188         
39189         if (match === false) {
39190             return true; // no more action?
39191         }
39192         // scroll to?
39193         this.view.select(match);
39194         var sn = Roo.get(this.view.getSelectedNodes()[0]);
39195         sn.scrollIntoView(sn.dom.parentNode, false);
39196     },
39197     
39198     onViewScroll : function(e, t){
39199         
39200         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){
39201             return;
39202         }
39203         
39204         this.hasQuery = true;
39205         
39206         this.loading = this.list.select('.loading', true).first();
39207         
39208         if(this.loading === null){
39209             this.list.createChild({
39210                 tag: 'div',
39211                 cls: 'loading roo-select2-more-results roo-select2-active',
39212                 html: 'Loading more results...'
39213             });
39214             
39215             this.loading = this.list.select('.loading', true).first();
39216             
39217             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
39218             
39219             this.loading.hide();
39220         }
39221         
39222         this.loading.show();
39223         
39224         var _combo = this;
39225         
39226         this.page++;
39227         this.loadNext = true;
39228         
39229         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
39230         
39231         return;
39232     },
39233     
39234     addItem : function(o)
39235     {   
39236         var dv = ''; // display value
39237         
39238         if (this.displayField) {
39239             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
39240         } else {
39241             // this is an error condition!!!
39242             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
39243         }
39244         
39245         if(!dv.length){
39246             return;
39247         }
39248         
39249         var choice = this.choices.createChild({
39250             tag: 'li',
39251             cls: 'roo-select2-search-choice',
39252             cn: [
39253                 {
39254                     tag: 'div',
39255                     html: dv
39256                 },
39257                 {
39258                     tag: 'a',
39259                     href: '#',
39260                     cls: 'roo-select2-search-choice-close fa fa-times',
39261                     tabindex: '-1'
39262                 }
39263             ]
39264             
39265         }, this.searchField);
39266         
39267         var close = choice.select('a.roo-select2-search-choice-close', true).first();
39268         
39269         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
39270         
39271         this.item.push(o);
39272         
39273         this.lastData = o;
39274         
39275         this.syncValue();
39276         
39277         this.inputEl().dom.value = '';
39278         
39279         this.validate();
39280     },
39281     
39282     onRemoveItem : function(e, _self, o)
39283     {
39284         e.preventDefault();
39285         
39286         this.lastItem = Roo.apply([], this.item);
39287         
39288         var index = this.item.indexOf(o.data) * 1;
39289         
39290         if( index < 0){
39291             Roo.log('not this item?!');
39292             return;
39293         }
39294         
39295         this.item.splice(index, 1);
39296         o.item.remove();
39297         
39298         this.syncValue();
39299         
39300         this.fireEvent('remove', this, e);
39301         
39302         this.validate();
39303         
39304     },
39305     
39306     syncValue : function()
39307     {
39308         if(!this.item.length){
39309             this.clearValue();
39310             return;
39311         }
39312             
39313         var value = [];
39314         var _this = this;
39315         Roo.each(this.item, function(i){
39316             if(_this.valueField){
39317                 value.push(i[_this.valueField]);
39318                 return;
39319             }
39320
39321             value.push(i);
39322         });
39323
39324         this.value = value.join(',');
39325
39326         if(this.hiddenField){
39327             this.hiddenField.dom.value = this.value;
39328         }
39329         
39330         this.store.fireEvent("datachanged", this.store);
39331         
39332         this.validate();
39333     },
39334     
39335     clearItem : function()
39336     {
39337         if(!this.multiple){
39338             return;
39339         }
39340         
39341         this.item = [];
39342         
39343         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
39344            c.remove();
39345         });
39346         
39347         this.syncValue();
39348         
39349         this.validate();
39350         
39351         if(this.tickable && !Roo.isTouch){
39352             this.view.refresh();
39353         }
39354     },
39355     
39356     inputEl: function ()
39357     {
39358         if(Roo.isIOS && this.useNativeIOS){
39359             return this.el.select('select.roo-ios-select', true).first();
39360         }
39361         
39362         if(Roo.isTouch && this.mobileTouchView){
39363             return this.el.select('input.form-control',true).first();
39364         }
39365         
39366         if(this.tickable){
39367             return this.searchField;
39368         }
39369         
39370         return this.el.select('input.form-control',true).first();
39371     },
39372     
39373     onTickableFooterButtonClick : function(e, btn, el)
39374     {
39375         e.preventDefault();
39376         
39377         this.lastItem = Roo.apply([], this.item);
39378         
39379         if(btn && btn.name == 'cancel'){
39380             this.tickItems = Roo.apply([], this.item);
39381             this.collapse();
39382             return;
39383         }
39384         
39385         this.clearItem();
39386         
39387         var _this = this;
39388         
39389         Roo.each(this.tickItems, function(o){
39390             _this.addItem(o);
39391         });
39392         
39393         this.collapse();
39394         
39395     },
39396     
39397     validate : function()
39398     {
39399         var v = this.getRawValue();
39400         
39401         if(this.multiple){
39402             v = this.getValue();
39403         }
39404         
39405         if(this.disabled || this.allowBlank || v.length){
39406             this.markValid();
39407             return true;
39408         }
39409         
39410         this.markInvalid();
39411         return false;
39412     },
39413     
39414     tickableInputEl : function()
39415     {
39416         if(!this.tickable || !this.editable){
39417             return this.inputEl();
39418         }
39419         
39420         return this.inputEl().select('.roo-select2-search-field-input', true).first();
39421     },
39422     
39423     
39424     getAutoCreateTouchView : function()
39425     {
39426         var id = Roo.id();
39427         
39428         var cfg = {
39429             cls: 'form-group' //input-group
39430         };
39431         
39432         var input =  {
39433             tag: 'input',
39434             id : id,
39435             type : this.inputType,
39436             cls : 'form-control x-combo-noedit',
39437             autocomplete: 'new-password',
39438             placeholder : this.placeholder || '',
39439             readonly : true
39440         };
39441         
39442         if (this.name) {
39443             input.name = this.name;
39444         }
39445         
39446         if (this.size) {
39447             input.cls += ' input-' + this.size;
39448         }
39449         
39450         if (this.disabled) {
39451             input.disabled = true;
39452         }
39453         
39454         var inputblock = {
39455             cls : '',
39456             cn : [
39457                 input
39458             ]
39459         };
39460         
39461         if(this.before){
39462             inputblock.cls += ' input-group';
39463             
39464             inputblock.cn.unshift({
39465                 tag :'span',
39466                 cls : 'input-group-addon',
39467                 html : this.before
39468             });
39469         }
39470         
39471         if(this.removable && !this.multiple){
39472             inputblock.cls += ' roo-removable';
39473             
39474             inputblock.cn.push({
39475                 tag: 'button',
39476                 html : 'x',
39477                 cls : 'roo-combo-removable-btn close'
39478             });
39479         }
39480
39481         if(this.hasFeedback && !this.allowBlank){
39482             
39483             inputblock.cls += ' has-feedback';
39484             
39485             inputblock.cn.push({
39486                 tag: 'span',
39487                 cls: 'glyphicon form-control-feedback'
39488             });
39489             
39490         }
39491         
39492         if (this.after) {
39493             
39494             inputblock.cls += (this.before) ? '' : ' input-group';
39495             
39496             inputblock.cn.push({
39497                 tag :'span',
39498                 cls : 'input-group-addon',
39499                 html : this.after
39500             });
39501         }
39502
39503         var box = {
39504             tag: 'div',
39505             cn: [
39506                 {
39507                     tag: 'input',
39508                     type : 'hidden',
39509                     cls: 'form-hidden-field'
39510                 },
39511                 inputblock
39512             ]
39513             
39514         };
39515         
39516         if(this.multiple){
39517             box = {
39518                 tag: 'div',
39519                 cn: [
39520                     {
39521                         tag: 'input',
39522                         type : 'hidden',
39523                         cls: 'form-hidden-field'
39524                     },
39525                     {
39526                         tag: 'ul',
39527                         cls: 'roo-select2-choices',
39528                         cn:[
39529                             {
39530                                 tag: 'li',
39531                                 cls: 'roo-select2-search-field',
39532                                 cn: [
39533
39534                                     inputblock
39535                                 ]
39536                             }
39537                         ]
39538                     }
39539                 ]
39540             }
39541         };
39542         
39543         var PhoneInput = {
39544             cls: 'roo-select2-container input-group roo-touchview-PhoneInput ',
39545             cn: [
39546                 box
39547             ]
39548         };
39549         
39550         if(!this.multiple && this.showToggleBtn){
39551             
39552             var caret = {
39553                         tag: 'span',
39554                         cls: 'caret'
39555             };
39556             
39557             if (this.caret != false) {
39558                 caret = {
39559                      tag: 'i',
39560                      cls: 'fa fa-' + this.caret
39561                 };
39562                 
39563             }
39564             
39565             PhoneInput.cn.push({
39566                 tag :'span',
39567                 cls : 'input-group-addon btn dropdown-toggle',
39568                 cn : [
39569                     caret,
39570                     {
39571                         tag: 'span',
39572                         cls: 'PhoneInput-clear',
39573                         cn  : [
39574                             {
39575                                 tag : 'i',
39576                                 cls: 'icon-remove'
39577                             }
39578                         ]
39579                     }
39580                 ]
39581
39582             })
39583         }
39584         
39585         if(this.multiple){
39586             PhoneInput.cls += ' roo-select2-container-multi';
39587         }
39588         
39589         var align = this.labelAlign || this.parentLabelAlign();
39590         
39591         if (align ==='left' && this.fieldLabel.length) {
39592
39593             cfg.cn = [
39594                 {
39595                    tag : 'i',
39596                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
39597                    tooltip : 'This field is required'
39598                 },
39599                 {
39600                     tag: 'label',
39601                     cls : 'control-label',
39602                     html : this.fieldLabel
39603
39604                 },
39605                 {
39606                     cls : '', 
39607                     cn: [
39608                         PhoneInput
39609                     ]
39610                 }
39611             ];
39612             
39613             var labelCfg = cfg.cn[1];
39614             var contentCfg = cfg.cn[2];
39615             
39616
39617             if(this.indicatorpos == 'right'){
39618                 cfg.cn = [
39619                     {
39620                         tag: 'label',
39621                         cls : 'control-label',
39622                         html : this.fieldLabel,
39623                         cn : [
39624                             {
39625                                tag : 'i',
39626                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
39627                                tooltip : 'This field is required'
39628                             }
39629                         ]
39630                     },
39631                     {
39632                         cls : '', 
39633                         cn: [
39634                             PhoneInput
39635                         ]
39636                     }
39637                 ];
39638             }
39639             
39640             labelCfg = cfg.cn[0];
39641             contentCfg = cfg.cn[2];
39642             
39643             if(this.labelWidth > 12){
39644                 labelCfg.style = "width: " + this.labelWidth + 'px';
39645             }
39646             
39647             if(this.labelWidth < 13 && this.labelmd == 0){
39648                 this.labelmd = this.labelWidth;
39649             }
39650             
39651             if(this.labellg > 0){
39652                 labelCfg.cls += ' col-lg-' + this.labellg;
39653                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
39654             }
39655             
39656             if(this.labelmd > 0){
39657                 labelCfg.cls += ' col-md-' + this.labelmd;
39658                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
39659             }
39660             
39661             if(this.labelsm > 0){
39662                 labelCfg.cls += ' col-sm-' + this.labelsm;
39663                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
39664             }
39665             
39666             if(this.labelxs > 0){
39667                 labelCfg.cls += ' col-xs-' + this.labelxs;
39668                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
39669             }
39670                 
39671                 
39672         } else if ( this.fieldLabel.length) {
39673             cfg.cn = [
39674                 {
39675                    tag : 'i',
39676                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
39677                    tooltip : 'This field is required'
39678                 },
39679                 {
39680                     tag: 'label',
39681                     cls : 'control-label',
39682                     html : this.fieldLabel
39683
39684                 },
39685                 {
39686                     cls : '', 
39687                     cn: [
39688                         PhoneInput
39689                     ]
39690                 }
39691             ];
39692             
39693             if(this.indicatorpos == 'right'){
39694                 cfg.cn = [
39695                     {
39696                         tag: 'label',
39697                         cls : 'control-label',
39698                         html : this.fieldLabel,
39699                         cn : [
39700                             {
39701                                tag : 'i',
39702                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
39703                                tooltip : 'This field is required'
39704                             }
39705                         ]
39706                     },
39707                     {
39708                         cls : '', 
39709                         cn: [
39710                             PhoneInput
39711                         ]
39712                     }
39713                 ];
39714             }
39715         } else {
39716             cfg.cn = PhoneInput;    
39717         }
39718         
39719         
39720         var settings = this;
39721         
39722         ['xs','sm','md','lg'].map(function(size){
39723             if (settings[size]) {
39724                 cfg.cls += ' col-' + size + '-' + settings[size];
39725             }
39726         });
39727         
39728         return cfg;
39729     },
39730     
39731     initTouchView : function()
39732     {
39733         this.renderTouchView();
39734         
39735         this.touchViewEl.on('scroll', function(){
39736             this.el.dom.scrollTop = 0;
39737         }, this);
39738         
39739         this.originalValue = this.getValue();
39740         
39741         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
39742         
39743         this.inputEl().on("click", this.showTouchView, this);
39744         if (this.triggerEl) {
39745             this.triggerEl.on("click", this.showTouchView, this);
39746         }
39747         
39748         
39749         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
39750         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
39751         
39752         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
39753         
39754         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
39755         this.store.on('load', this.onTouchViewLoad, this);
39756         this.store.on('loadexception', this.onTouchViewLoadException, this);
39757         
39758         if(this.hiddenName){
39759             
39760             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
39761             
39762             this.hiddenField.dom.value =
39763                 this.hiddenValue !== undefined ? this.hiddenValue :
39764                 this.value !== undefined ? this.value : '';
39765         
39766             this.el.dom.removeAttribute('name');
39767             this.hiddenField.dom.setAttribute('name', this.hiddenName);
39768         }
39769         
39770         if(this.multiple){
39771             this.choices = this.el.select('ul.roo-select2-choices', true).first();
39772             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
39773         }
39774         
39775         if(this.removable && !this.multiple){
39776             var close = this.closeTriggerEl();
39777             if(close){
39778                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
39779                 close.on('click', this.removeBtnClick, this, close);
39780             }
39781         }
39782         /*
39783          * fix the bug in Safari iOS8
39784          */
39785         this.inputEl().on("focus", function(e){
39786             document.activeElement.blur();
39787         }, this);
39788         
39789         return;
39790         
39791         
39792     },
39793     
39794     renderTouchView : function()
39795     {
39796         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.PhoneInput.touchViewTemplate);
39797         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
39798         
39799         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
39800         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
39801         
39802         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
39803         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
39804         this.touchViewBodyEl.setStyle('overflow', 'auto');
39805         
39806         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
39807         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
39808         
39809         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
39810         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
39811         
39812     },
39813     
39814     showTouchView : function()
39815     {
39816         if(this.disabled){
39817             return;
39818         }
39819         
39820         this.touchViewHeaderEl.hide();
39821
39822         if(this.modalTitle.length){
39823             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
39824             this.touchViewHeaderEl.show();
39825         }
39826
39827         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
39828         this.touchViewEl.show();
39829
39830         this.touchViewEl.select('.modal-dialog', true).first().setStyle('margin', '0px');
39831         this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
39832                 Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
39833
39834         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
39835
39836         if(this.modalTitle.length){
39837             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
39838         }
39839         
39840         this.touchViewBodyEl.setHeight(bodyHeight);
39841
39842         if(this.animate){
39843             var _this = this;
39844             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
39845         }else{
39846             this.touchViewEl.addClass('in');
39847         }
39848
39849         this.doTouchViewQuery();
39850         
39851     },
39852     
39853     hideTouchView : function()
39854     {
39855         this.touchViewEl.removeClass('in');
39856
39857         if(this.animate){
39858             var _this = this;
39859             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
39860         }else{
39861             this.touchViewEl.setStyle('display', 'none');
39862         }
39863         
39864     },
39865     
39866     setTouchViewValue : function()
39867     {
39868         if(this.multiple){
39869             this.clearItem();
39870         
39871             var _this = this;
39872
39873             Roo.each(this.tickItems, function(o){
39874                 this.addItem(o);
39875             }, this);
39876         }
39877         
39878         this.hideTouchView();
39879     },
39880     
39881     doTouchViewQuery : function()
39882     {
39883         var qe = {
39884             query: '',
39885             forceAll: true,
39886             combo: this,
39887             cancel:false
39888         };
39889         
39890         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
39891             return false;
39892         }
39893         
39894         if(!this.alwaysQuery || this.mode == 'local'){
39895             this.onTouchViewLoad();
39896             return;
39897         }
39898         
39899         this.store.load();
39900     },
39901     
39902     onTouchViewBeforeLoad : function(combo,opts)
39903     {
39904         return;
39905     },
39906
39907     // private
39908     onTouchViewLoad : function()
39909     {
39910         if(this.store.getCount() < 1){
39911             this.onTouchViewEmptyResults();
39912             return;
39913         }
39914         
39915         this.clearTouchView();
39916         
39917         var rawValue = this.getRawValue();
39918         
39919         var template = (this.multiple) ? Roo.bootstrap.PhoneInput.listItemCheckbox : Roo.bootstrap.PhoneInput.listItemRadio;
39920         
39921         this.tickItems = [];
39922         
39923         this.store.data.each(function(d, rowIndex){
39924             var row = this.touchViewListGroup.createChild(template);
39925             
39926             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
39927                 row.addClass(d.data.cls);
39928             }
39929             
39930             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
39931                 var cfg = {
39932                     data : d.data,
39933                     html : d.data[this.displayField]
39934                 };
39935                 
39936                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
39937                     row.select('.roo-PhoneInput-list-group-item-value', true).first().dom.innerHTML = cfg.html;
39938                 }
39939             }
39940             row.removeClass('selected');
39941             if(!this.multiple && this.valueField &&
39942                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
39943             {
39944                 // radio buttons..
39945                 row.select('.roo-PhoneInput-list-group-item-box > input', true).first().attr('checked', true);
39946                 row.addClass('selected');
39947             }
39948             
39949             if(this.multiple && this.valueField &&
39950                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
39951             {
39952                 
39953                 // checkboxes...
39954                 row.select('.roo-PhoneInput-list-group-item-box > input', true).first().attr('checked', true);
39955                 this.tickItems.push(d.data);
39956             }
39957             
39958             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
39959             
39960         }, this);
39961         
39962         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-PhoneInput-list-group-item-box > input:checked', true).first();
39963         
39964         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
39965
39966         if(this.modalTitle.length){
39967             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
39968         }
39969
39970         var listHeight = this.touchViewListGroup.getHeight();
39971         
39972         var _this = this;
39973         
39974         if(firstChecked && listHeight > bodyHeight){
39975             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
39976         }
39977         
39978     },
39979     
39980     onTouchViewLoadException : function()
39981     {
39982         this.hideTouchView();
39983     },
39984     
39985     onTouchViewEmptyResults : function()
39986     {
39987         this.clearTouchView();
39988         
39989         this.touchViewListGroup.createChild(Roo.bootstrap.PhoneInput.emptyResult);
39990         
39991         this.touchViewListGroup.select('.roo-PhoneInput-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
39992         
39993     },
39994     
39995     clearTouchView : function()
39996     {
39997         this.touchViewListGroup.dom.innerHTML = '';
39998     },
39999     
40000     onTouchViewClick : function(e, el, o)
40001     {
40002         e.preventDefault();
40003         
40004         var row = o.row;
40005         var rowIndex = o.rowIndex;
40006         
40007         var r = this.store.getAt(rowIndex);
40008         
40009         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
40010             
40011             if(!this.multiple){
40012                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-PhoneInput-list-group-item-box > input:checked', true).elements, function(c){
40013                     c.dom.removeAttribute('checked');
40014                 }, this);
40015
40016                 row.select('.roo-PhoneInput-list-group-item-box > input', true).first().attr('checked', true);
40017
40018                 this.setFromData(r.data);
40019
40020                 var close = this.closeTriggerEl();
40021
40022                 if(close){
40023                     close.show();
40024                 }
40025
40026                 this.hideTouchView();
40027
40028                 this.fireEvent('select', this, r, rowIndex);
40029
40030                 return;
40031             }
40032
40033             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
40034                 row.select('.roo-PhoneInput-list-group-item-box > input', true).first().dom.removeAttribute('checked');
40035                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
40036                 return;
40037             }
40038
40039             row.select('.roo-PhoneInput-list-group-item-box > input', true).first().attr('checked', true);
40040             this.addItem(r.data);
40041             this.tickItems.push(r.data);
40042         }
40043     },
40044     
40045     getAutoCreateNativeIOS : function()
40046     {
40047         var cfg = {
40048             cls: 'form-group' //input-group,
40049         };
40050         
40051         var PhoneInput =  {
40052             tag: 'select',
40053             cls : 'roo-ios-select'
40054         };
40055         
40056         if (this.name) {
40057             PhoneInput.name = this.name;
40058         }
40059         
40060         if (this.disabled) {
40061             PhoneInput.disabled = true;
40062         }
40063         
40064         var settings = this;
40065         
40066         ['xs','sm','md','lg'].map(function(size){
40067             if (settings[size]) {
40068                 cfg.cls += ' col-' + size + '-' + settings[size];
40069             }
40070         });
40071         
40072         cfg.cn = PhoneInput;
40073         
40074         return cfg;
40075         
40076     },
40077     
40078     initIOSView : function()
40079     {
40080         this.store.on('load', this.onIOSViewLoad, this);
40081         
40082         return;
40083     },
40084     
40085     onIOSViewLoad : function()
40086     {
40087         if(this.store.getCount() < 1){
40088             return;
40089         }
40090         
40091         this.clearIOSView();
40092         
40093         if(this.allowBlank) {
40094             
40095             var default_text = '-- SELECT --';
40096             
40097             var opt = this.inputEl().createChild({
40098                 tag: 'option',
40099                 value : 0,
40100                 html : default_text
40101             });
40102             
40103             var o = {};
40104             o[this.valueField] = 0;
40105             o[this.displayField] = default_text;
40106             
40107             this.ios_options.push({
40108                 data : o,
40109                 el : opt
40110             });
40111             
40112         }
40113         
40114         this.store.data.each(function(d, rowIndex){
40115             
40116             var html = '';
40117             
40118             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
40119                 html = d.data[this.displayField];
40120             }
40121             
40122             var value = '';
40123             
40124             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
40125                 value = d.data[this.valueField];
40126             }
40127             
40128             var option = {
40129                 tag: 'option',
40130                 value : value,
40131                 html : html
40132             };
40133             
40134             if(this.value == d.data[this.valueField]){
40135                 option['selected'] = true;
40136             }
40137             
40138             var opt = this.inputEl().createChild(option);
40139             
40140             this.ios_options.push({
40141                 data : d.data,
40142                 el : opt
40143             });
40144             
40145         }, this);
40146         
40147         this.inputEl().on('change', function(){
40148            this.fireEvent('select', this);
40149         }, this);
40150         
40151     },
40152     
40153     clearIOSView: function()
40154     {
40155         this.inputEl().dom.innerHTML = '';
40156         
40157         this.ios_options = [];
40158     },
40159     
40160     setIOSValue: function(v)
40161     {
40162         this.value = v;
40163         
40164         if(!this.ios_options){
40165             return;
40166         }
40167         
40168         Roo.each(this.ios_options, function(opts){
40169            
40170            opts.el.dom.removeAttribute('selected');
40171            
40172            if(opts.data[this.valueField] != v){
40173                return;
40174            }
40175            
40176            opts.el.dom.setAttribute('selected', true);
40177            
40178         }, this);
40179     }
40180
40181     /** 
40182     * @cfg {Boolean} grow 
40183     * @hide 
40184     */
40185     /** 
40186     * @cfg {Number} growMin 
40187     * @hide 
40188     */
40189     /** 
40190     * @cfg {Number} growMax 
40191     * @hide 
40192     */
40193     /**
40194      * @hide
40195      * @method autoSize
40196      */
40197 });
40198
40199 Roo.apply(Roo.bootstrap.PhoneInput,  {
40200     
40201     header : {
40202         tag: 'div',
40203         cls: 'modal-header',
40204         cn: [
40205             {
40206                 tag: 'h4',
40207                 cls: 'modal-title'
40208             }
40209         ]
40210     },
40211     
40212     body : {
40213         tag: 'div',
40214         cls: 'modal-body',
40215         cn: [
40216             {
40217                 tag: 'ul',
40218                 cls: 'list-group'
40219             }
40220         ]
40221     },
40222     
40223     listItemRadio : {
40224         tag: 'li',
40225         cls: 'list-group-item',
40226         cn: [
40227             {
40228                 tag: 'span',
40229                 cls: 'roo-PhoneInput-list-group-item-value'
40230             },
40231             {
40232                 tag: 'div',
40233                 cls: 'roo-PhoneInput-list-group-item-box pull-xs-right radio-inline radio radio-info',
40234                 cn: [
40235                     {
40236                         tag: 'input',
40237                         type: 'radio'
40238                     },
40239                     {
40240                         tag: 'label'
40241                     }
40242                 ]
40243             }
40244         ]
40245     },
40246     
40247     listItemCheckbox : {
40248         tag: 'li',
40249         cls: 'list-group-item',
40250         cn: [
40251             {
40252                 tag: 'span',
40253                 cls: 'roo-PhoneInput-list-group-item-value'
40254             },
40255             {
40256                 tag: 'div',
40257                 cls: 'roo-PhoneInput-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
40258                 cn: [
40259                     {
40260                         tag: 'input',
40261                         type: 'checkbox'
40262                     },
40263                     {
40264                         tag: 'label'
40265                     }
40266                 ]
40267             }
40268         ]
40269     },
40270     
40271     emptyResult : {
40272         tag: 'div',
40273         cls: 'alert alert-danger roo-PhoneInput-touch-view-empty-result'
40274     },
40275     
40276     footer : {
40277         tag: 'div',
40278         cls: 'modal-footer',
40279         cn: [
40280             {
40281                 tag: 'div',
40282                 cls: 'row',
40283                 cn: [
40284                     {
40285                         tag: 'div',
40286                         cls: 'col-xs-6 text-left',
40287                         cn: {
40288                             tag: 'button',
40289                             cls: 'btn btn-danger roo-touch-view-cancel',
40290                             html: 'Cancel'
40291                         }
40292                     },
40293                     {
40294                         tag: 'div',
40295                         cls: 'col-xs-6 text-right',
40296                         cn: {
40297                             tag: 'button',
40298                             cls: 'btn btn-success roo-touch-view-ok',
40299                             html: 'OK'
40300                         }
40301                     }
40302                 ]
40303             }
40304         ]
40305         
40306     }
40307 });