roojs-all.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             // if parent was disabled, then do not try and create the children..
251             if(!this[cntr](true)){
252                 tree.items = [];
253                 return tree;
254             }
255            
256             cn = Roo.factory(tree);
257            
258             cn.parentType = this.xtype; //??
259             cn.parentId = this.id;
260             
261             var build_from_html =  Roo.XComponent.build_from_html;
262             
263             
264             // does the container contain child eleemnts with 'xtype' attributes.
265             // that match this xtype..
266             // note - when we render we create these as well..
267             // so we should check to see if body has xtype set.
268             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
269                
270                 var self_cntr_el = Roo.get(this[cntr](false));
271                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
272                 if (echild) { 
273                     //Roo.log(Roo.XComponent.build_from_html);
274                     //Roo.log("got echild:");
275                     //Roo.log(echild);
276                 }
277                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
278                 // and are not displayed -this causes this to use up the wrong element when matching.
279                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
280                 
281                 
282                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
283                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
284                   
285                   
286                   
287                     cn.el = echild;
288                   //  Roo.log("GOT");
289                     //echild.dom.removeAttribute('xtype');
290                 } else {
291                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
292                     Roo.debug && Roo.log(self_cntr_el);
293                     Roo.debug && Roo.log(echild);
294                     Roo.debug && Roo.log(cn);
295                 }
296             }
297            
298             
299            
300             // if object has flexy:if - then it may or may not be rendered.
301             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
302                 // skip a flexy if element.
303                 Roo.debug && Roo.log('skipping render');
304                 Roo.debug && Roo.log(tree);
305                 if (!cn.el) {
306                     Roo.debug && Roo.log('skipping all children');
307                     skip_children = true;
308                 }
309                 
310              } else {
311                  
312                 // actually if flexy:foreach is found, we really want to create 
313                 // multiple copies here...
314                 //Roo.log('render');
315                 //Roo.log(this[cntr]());
316                 // some elements do not have render methods.. like the layouts...
317                 /*
318                 if(this[cntr](true) === false){
319                     cn.items = [];
320                     return cn;
321                 }
322                 */
323                 cn.render && cn.render(this[cntr](true));
324                 
325              }
326             // then add the element..
327         }
328          
329         // handle the kids..
330         
331         var nitems = [];
332         /*
333         if (typeof (tree.menu) != 'undefined') {
334             tree.menu.parentType = cn.xtype;
335             tree.menu.triggerEl = cn.el;
336             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
337             
338         }
339         */
340         if (!tree.items || !tree.items.length) {
341             cn.items = nitems;
342             //Roo.log(["no children", this]);
343             
344             return cn;
345         }
346          
347         var items = tree.items;
348         delete tree.items;
349         
350         //Roo.log(items.length);
351             // add the items..
352         if (!skip_children) {    
353             for(var i =0;i < items.length;i++) {
354               //  Roo.log(['add child', items[i]]);
355                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
356             }
357         }
358         
359         cn.items = nitems;
360         
361         //Roo.log("fire childrenrendered");
362         
363         cn.fireEvent('childrenrendered', this);
364         
365         return cn;
366     },
367     /**
368      * Show a component - removes 'hidden' class
369      */
370     show : function()
371     {
372         if (this.el) {
373             this.el.removeClass('hidden');
374         }
375     },
376     /**
377      * Hide a component - adds 'hidden' class
378      */
379     hide: function()
380     {
381         if (this.el && !this.el.hasClass('hidden')) {
382             this.el.addClass('hidden');
383         }
384     }
385 });
386
387  /*
388  * - LGPL
389  *
390  * Body
391  *
392  */
393
394 /**
395  * @class Roo.bootstrap.Body
396  * @extends Roo.bootstrap.Component
397  * Bootstrap Body class
398  *
399  * @constructor
400  * Create a new body
401  * @param {Object} config The config object
402  */
403
404 Roo.bootstrap.Body = function(config){
405
406     config = config || {};
407
408     Roo.bootstrap.Body.superclass.constructor.call(this, config);
409     this.el = Roo.get(config.el ? config.el : document.body );
410     if (this.cls && this.cls.length) {
411         Roo.get(document.body).addClass(this.cls);
412     }
413 };
414
415 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
416
417     is_body : true,// just to make sure it's constructed?
418
419         autoCreate : {
420         cls: 'container'
421     },
422     onRender : function(ct, position)
423     {
424        /* Roo.log("Roo.bootstrap.Body - onRender");
425         if (this.cls && this.cls.length) {
426             Roo.get(document.body).addClass(this.cls);
427         }
428         // style??? xttr???
429         */
430     }
431
432
433
434
435 });
436 /*
437  * - LGPL
438  *
439  * button group
440  * 
441  */
442
443
444 /**
445  * @class Roo.bootstrap.ButtonGroup
446  * @extends Roo.bootstrap.Component
447  * Bootstrap ButtonGroup class
448  * @cfg {String} size lg | sm | xs (default empty normal)
449  * @cfg {String} align vertical | justified  (default none)
450  * @cfg {String} direction up | down (default down)
451  * @cfg {Boolean} toolbar false | true
452  * @cfg {Boolean} btn true | false
453  * 
454  * 
455  * @constructor
456  * Create a new Input
457  * @param {Object} config The config object
458  */
459
460 Roo.bootstrap.ButtonGroup = function(config){
461     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
462 };
463
464 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
465     
466     size: '',
467     align: '',
468     direction: '',
469     toolbar: false,
470     btn: true,
471
472     getAutoCreate : function(){
473         var cfg = {
474             cls: 'btn-group',
475             html : null
476         };
477         
478         cfg.html = this.html || cfg.html;
479         
480         if (this.toolbar) {
481             cfg = {
482                 cls: 'btn-toolbar',
483                 html: null
484             };
485             
486             return cfg;
487         }
488         
489         if (['vertical','justified'].indexOf(this.align)!==-1) {
490             cfg.cls = 'btn-group-' + this.align;
491             
492             if (this.align == 'justified') {
493                 console.log(this.items);
494             }
495         }
496         
497         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
498             cfg.cls += ' btn-group-' + this.size;
499         }
500         
501         if (this.direction == 'up') {
502             cfg.cls += ' dropup' ;
503         }
504         
505         return cfg;
506     }
507    
508 });
509
510  /*
511  * - LGPL
512  *
513  * button
514  * 
515  */
516
517 /**
518  * @class Roo.bootstrap.Button
519  * @extends Roo.bootstrap.Component
520  * Bootstrap Button class
521  * @cfg {String} html The button content
522  * @cfg {String} weight (default | primary | success | info | warning | danger | link ) default 
523  * @cfg {String} size ( lg | sm | xs)
524  * @cfg {String} tag ( a | input | submit)
525  * @cfg {String} href empty or href
526  * @cfg {Boolean} disabled default false;
527  * @cfg {Boolean} isClose default false;
528  * @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)
529  * @cfg {String} badge text for badge
530  * @cfg {String} theme default 
531  * @cfg {Boolean} inverse 
532  * @cfg {Boolean} toggle 
533  * @cfg {String} ontext text for on toggle state
534  * @cfg {String} offtext text for off toggle state
535  * @cfg {Boolean} defaulton 
536  * @cfg {Boolean} preventDefault  default true
537  * @cfg {Boolean} removeClass remove the standard class..
538  * @cfg {String} target  target for a href. (_self|_blank|_parent|_top| other)
539  * 
540  * @constructor
541  * Create a new button
542  * @param {Object} config The config object
543  */
544
545
546 Roo.bootstrap.Button = function(config){
547     Roo.bootstrap.Button.superclass.constructor.call(this, config);
548     this.weightClass = ["btn-default", 
549                        "btn-primary", 
550                        "btn-success", 
551                        "btn-info", 
552                        "btn-warning",
553                        "btn-danger",
554                        "btn-link"
555                       ],  
556     this.addEvents({
557         // raw events
558         /**
559          * @event click
560          * When a butotn is pressed
561          * @param {Roo.bootstrap.Button} this
562          * @param {Roo.EventObject} e
563          */
564         "click" : true,
565          /**
566          * @event toggle
567          * After the button has been toggles
568          * @param {Roo.EventObject} e
569          * @param {boolean} pressed (also available as button.pressed)
570          */
571         "toggle" : true
572     });
573 };
574
575 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
576     html: false,
577     active: false,
578     weight: '',
579     size: '',
580     tag: 'button',
581     href: '',
582     disabled: false,
583     isClose: false,
584     glyphicon: '',
585     badge: '',
586     theme: 'default',
587     inverse: false,
588     
589     toggle: false,
590     ontext: 'ON',
591     offtext: 'OFF',
592     defaulton: true,
593     preventDefault: true,
594     removeClass: false,
595     name: false,
596     target: false,
597     
598     
599     pressed : null,
600      
601     
602     getAutoCreate : function(){
603         
604         var cfg = {
605             tag : 'button',
606             cls : 'roo-button',
607             html: ''
608         };
609         
610         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
611             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
612             this.tag = 'button';
613         } else {
614             cfg.tag = this.tag;
615         }
616         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
617         
618         if (this.toggle == true) {
619             cfg={
620                 tag: 'div',
621                 cls: 'slider-frame roo-button',
622                 cn: [
623                     {
624                         tag: 'span',
625                         'data-on-text':'ON',
626                         'data-off-text':'OFF',
627                         cls: 'slider-button',
628                         html: this.offtext
629                     }
630                 ]
631             };
632             
633             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
634                 cfg.cls += ' '+this.weight;
635             }
636             
637             return cfg;
638         }
639         
640         if (this.isClose) {
641             cfg.cls += ' close';
642             
643             cfg["aria-hidden"] = true;
644             
645             cfg.html = "&times;";
646             
647             return cfg;
648         }
649         
650          
651         if (this.theme==='default') {
652             cfg.cls = 'btn roo-button';
653             
654             //if (this.parentType != 'Navbar') {
655             this.weight = this.weight.length ?  this.weight : 'default';
656             //}
657             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
658                 
659                 cfg.cls += ' btn-' + this.weight;
660             }
661         } else if (this.theme==='glow') {
662             
663             cfg.tag = 'a';
664             cfg.cls = 'btn-glow roo-button';
665             
666             if (['default', 'primary', 'success', 'info', 'warning', 'danger', 'link'].indexOf(this.weight) > -1) {
667                 
668                 cfg.cls += ' ' + this.weight;
669             }
670         }
671    
672         
673         if (this.inverse) {
674             this.cls += ' inverse';
675         }
676         
677         
678         if (this.active) {
679             cfg.cls += ' active';
680         }
681         
682         if (this.disabled) {
683             cfg.disabled = 'disabled';
684         }
685         
686         if (this.items) {
687             Roo.log('changing to ul' );
688             cfg.tag = 'ul';
689             this.glyphicon = 'caret';
690         }
691         
692         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
693          
694         //gsRoo.log(this.parentType);
695         if (this.parentType === 'Navbar' && !this.parent().bar) {
696             Roo.log('changing to li?');
697             
698             cfg.tag = 'li';
699             
700             cfg.cls = '';
701             cfg.cn =  [{
702                 tag : 'a',
703                 cls : 'roo-button',
704                 html : this.html,
705                 href : this.href || '#'
706             }];
707             if (this.menu) {
708                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
709                 cfg.cls += ' dropdown';
710             }   
711             
712             delete cfg.html;
713             
714         }
715         
716        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
717         
718         if (this.glyphicon) {
719             cfg.html = ' ' + cfg.html;
720             
721             cfg.cn = [
722                 {
723                     tag: 'span',
724                     cls: 'glyphicon glyphicon-' + this.glyphicon
725                 }
726             ];
727         }
728         
729         if (this.badge) {
730             cfg.html += ' ';
731             
732             cfg.tag = 'a';
733             
734 //            cfg.cls='btn roo-button';
735             
736             cfg.href=this.href;
737             
738             var value = cfg.html;
739             
740             if(this.glyphicon){
741                 value = {
742                             tag: 'span',
743                             cls: 'glyphicon glyphicon-' + this.glyphicon,
744                             html: this.html
745                         };
746                 
747             }
748             
749             cfg.cn = [
750                 value,
751                 {
752                     tag: 'span',
753                     cls: 'badge',
754                     html: this.badge
755                 }
756             ];
757             
758             cfg.html='';
759         }
760         
761         if (this.menu) {
762             cfg.cls += ' dropdown';
763             cfg.html = typeof(cfg.html) != 'undefined' ? cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
764         }
765         
766         if (cfg.tag !== 'a' && this.href !== '') {
767             throw "Tag must be a to set href.";
768         } else if (this.href.length > 0) {
769             cfg.href = this.href;
770         }
771         
772         if(this.removeClass){
773             cfg.cls = '';
774         }
775         
776         if(this.target){
777             cfg.target = this.target;
778         }
779         
780         return cfg;
781     },
782     initEvents: function() {
783        // Roo.log('init events?');
784 //        Roo.log(this.el.dom);
785         // add the menu...
786         
787         if (typeof (this.menu) != 'undefined') {
788             this.menu.parentType = this.xtype;
789             this.menu.triggerEl = this.el;
790             this.addxtype(Roo.apply({}, this.menu));
791         }
792
793
794        if (this.el.hasClass('roo-button')) {
795             this.el.on('click', this.onClick, this);
796        } else {
797             this.el.select('.roo-button').on('click', this.onClick, this);
798        }
799        
800        if(this.removeClass){
801            this.el.on('click', this.onClick, this);
802        }
803        
804        this.el.enableDisplayMode();
805         
806     },
807     onClick : function(e)
808     {
809         if (this.disabled) {
810             return;
811         }
812         
813         
814         Roo.log('button on click ');
815         if(this.preventDefault){
816             e.preventDefault();
817         }
818         if (this.pressed === true || this.pressed === false) {
819             this.pressed = !this.pressed;
820             this.el[this.pressed ? 'addClass' : 'removeClass']('active');
821             this.fireEvent('toggle', this, e, this.pressed);
822         }
823         
824         
825         this.fireEvent('click', this, e);
826     },
827     
828     /**
829      * Enables this button
830      */
831     enable : function()
832     {
833         this.disabled = false;
834         this.el.removeClass('disabled');
835     },
836     
837     /**
838      * Disable this button
839      */
840     disable : function()
841     {
842         this.disabled = true;
843         this.el.addClass('disabled');
844     },
845      /**
846      * sets the active state on/off, 
847      * @param {Boolean} state (optional) Force a particular state
848      */
849     setActive : function(v) {
850         
851         this.el[v ? 'addClass' : 'removeClass']('active');
852     },
853      /**
854      * toggles the current active state 
855      */
856     toggleActive : function()
857     {
858        var active = this.el.hasClass('active');
859        this.setActive(!active);
860        
861         
862     },
863     setText : function(str)
864     {
865         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
866     },
867     getText : function()
868     {
869         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
870     },
871     hide: function() {
872        
873      
874         this.el.hide();   
875     },
876     show: function() {
877        
878         this.el.show();   
879     },
880     setWeight : function(str)
881     {
882           this.el.removeClass(this.weightClass);
883         this.el.addClass('btn-' + str);        
884     }
885     
886     
887 });
888
889  /*
890  * - LGPL
891  *
892  * column
893  * 
894  */
895
896 /**
897  * @class Roo.bootstrap.Column
898  * @extends Roo.bootstrap.Component
899  * Bootstrap Column class
900  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
901  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
902  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
903  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
904  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
905  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
906  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
907  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
908  *
909  * 
910  * @cfg {Boolean} hidden (true|false) hide the element
911  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
912  * @cfg {String} fa (ban|check|...) font awesome icon
913  * @cfg {Number} fasize (1|2|....) font awsome size
914
915  * @cfg {String} icon (info-sign|check|...) glyphicon name
916
917  * @cfg {String} html content of column.
918  * 
919  * @constructor
920  * Create a new Column
921  * @param {Object} config The config object
922  */
923
924 Roo.bootstrap.Column = function(config){
925     Roo.bootstrap.Column.superclass.constructor.call(this, config);
926 };
927
928 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
929     
930     xs: false,
931     sm: false,
932     md: false,
933     lg: false,
934     xsoff: false,
935     smoff: false,
936     mdoff: false,
937     lgoff: false,
938     html: '',
939     offset: 0,
940     alert: false,
941     fa: false,
942     icon : false,
943     hidden : false,
944     fasize : 1,
945     
946     getAutoCreate : function(){
947         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
948         
949         cfg = {
950             tag: 'div',
951             cls: 'column'
952         };
953         
954         var settings=this;
955         ['xs','sm','md','lg'].map(function(size){
956             //Roo.log( size + ':' + settings[size]);
957             
958             if (settings[size+'off'] !== false) {
959                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
960             }
961             
962             if (settings[size] === false) {
963                 return;
964             }
965             
966             if (!settings[size]) { // 0 = hidden
967                 cfg.cls += ' hidden-' + size;
968                 return;
969             }
970             cfg.cls += ' col-' + size + '-' + settings[size];
971             
972         });
973         
974         if (this.hidden) {
975             cfg.cls += ' hidden';
976         }
977         
978         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
979             cfg.cls +=' alert alert-' + this.alert;
980         }
981         
982         
983         if (this.html.length) {
984             cfg.html = this.html;
985         }
986         if (this.fa) {
987             var fasize = '';
988             if (this.fasize > 1) {
989                 fasize = ' fa-' + this.fasize + 'x';
990             }
991             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
992             
993             
994         }
995         if (this.icon) {
996             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
997         }
998         
999         return cfg;
1000     }
1001    
1002 });
1003
1004  
1005
1006  /*
1007  * - LGPL
1008  *
1009  * page container.
1010  * 
1011  */
1012
1013
1014 /**
1015  * @class Roo.bootstrap.Container
1016  * @extends Roo.bootstrap.Component
1017  * Bootstrap Container class
1018  * @cfg {Boolean} jumbotron is it a jumbotron element
1019  * @cfg {String} html content of element
1020  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1021  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1022  * @cfg {String} header content of header (for panel)
1023  * @cfg {String} footer content of footer (for panel)
1024  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1025  * @cfg {String} tag (header|aside|section) type of HTML tag.
1026  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1027  * @cfg {String} fa font awesome icon
1028  * @cfg {String} icon (info-sign|check|...) glyphicon name
1029  * @cfg {Boolean} hidden (true|false) hide the element
1030  * @cfg {Boolean} expandable (true|false) default false
1031  * @cfg {Boolean} expanded (true|false) default true
1032  * @cfg {String} rheader contet on the right of header
1033  * @cfg {Boolean} clickable (true|false) default false
1034
1035  *     
1036  * @constructor
1037  * Create a new Container
1038  * @param {Object} config The config object
1039  */
1040
1041 Roo.bootstrap.Container = function(config){
1042     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1043     
1044     this.addEvents({
1045         // raw events
1046          /**
1047          * @event expand
1048          * After the panel has been expand
1049          * 
1050          * @param {Roo.bootstrap.Container} this
1051          */
1052         "expand" : true,
1053         /**
1054          * @event collapse
1055          * After the panel has been collapsed
1056          * 
1057          * @param {Roo.bootstrap.Container} this
1058          */
1059         "collapse" : true,
1060         /**
1061          * @event click
1062          * When a element is chick
1063          * @param {Roo.bootstrap.Container} this
1064          * @param {Roo.EventObject} e
1065          */
1066         "click" : true
1067     });
1068 };
1069
1070 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1071     
1072     jumbotron : false,
1073     well: '',
1074     panel : '',
1075     header: '',
1076     footer : '',
1077     sticky: '',
1078     tag : false,
1079     alert : false,
1080     fa: false,
1081     icon : false,
1082     expandable : false,
1083     rheader : '',
1084     expanded : true,
1085     clickable: false,
1086   
1087      
1088     getChildContainer : function() {
1089         
1090         if(!this.el){
1091             return false;
1092         }
1093         
1094         if (this.panel.length) {
1095             return this.el.select('.panel-body',true).first();
1096         }
1097         
1098         return this.el;
1099     },
1100     
1101     
1102     getAutoCreate : function(){
1103         
1104         var cfg = {
1105             tag : this.tag || 'div',
1106             html : '',
1107             cls : ''
1108         };
1109         if (this.jumbotron) {
1110             cfg.cls = 'jumbotron';
1111         }
1112         
1113         
1114         
1115         // - this is applied by the parent..
1116         //if (this.cls) {
1117         //    cfg.cls = this.cls + '';
1118         //}
1119         
1120         if (this.sticky.length) {
1121             
1122             var bd = Roo.get(document.body);
1123             if (!bd.hasClass('bootstrap-sticky')) {
1124                 bd.addClass('bootstrap-sticky');
1125                 Roo.select('html',true).setStyle('height', '100%');
1126             }
1127              
1128             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1129         }
1130         
1131         
1132         if (this.well.length) {
1133             switch (this.well) {
1134                 case 'lg':
1135                 case 'sm':
1136                     cfg.cls +=' well well-' +this.well;
1137                     break;
1138                 default:
1139                     cfg.cls +=' well';
1140                     break;
1141             }
1142         }
1143         
1144         if (this.hidden) {
1145             cfg.cls += ' hidden';
1146         }
1147         
1148         
1149         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1150             cfg.cls +=' alert alert-' + this.alert;
1151         }
1152         
1153         var body = cfg;
1154         
1155         if (this.panel.length) {
1156             cfg.cls += ' panel panel-' + this.panel;
1157             cfg.cn = [];
1158             if (this.header.length) {
1159                 
1160                 var h = [];
1161                 
1162                 if(this.expandable){
1163                     
1164                     cfg.cls = cfg.cls + ' expandable';
1165                     
1166                     h.push({
1167                         tag: 'i',
1168                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1169                     });
1170                     
1171                 }
1172                 
1173                 h.push(
1174                     {
1175                         tag: 'span',
1176                         cls : 'panel-title',
1177                         html : (this.expandable ? '&nbsp;' : '') + this.header
1178                     },
1179                     {
1180                         tag: 'span',
1181                         cls: 'panel-header-right',
1182                         html: this.rheader
1183                     }
1184                 );
1185                 
1186                 cfg.cn.push({
1187                     cls : 'panel-heading',
1188                     style : this.expandable ? 'cursor: pointer' : '',
1189                     cn : h
1190                 });
1191                 
1192             }
1193             
1194             body = false;
1195             cfg.cn.push({
1196                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1197                 html : this.html
1198             });
1199             
1200             
1201             if (this.footer.length) {
1202                 cfg.cn.push({
1203                     cls : 'panel-footer',
1204                     html : this.footer
1205                     
1206                 });
1207             }
1208             
1209         }
1210         
1211         if (body) {
1212             body.html = this.html || cfg.html;
1213             // prefix with the icons..
1214             if (this.fa) {
1215                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1216             }
1217             if (this.icon) {
1218                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1219             }
1220             
1221             
1222         }
1223         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1224             cfg.cls =  'container';
1225         }
1226         
1227         return cfg;
1228     },
1229     
1230     initEvents: function() 
1231     {
1232         if(this.expandable){
1233             var headerEl = this.headerEl();
1234         
1235             if(headerEl){
1236                 headerEl.on('click', this.onToggleClick, this);
1237             }
1238         }
1239         
1240         if(this.clickable){
1241             this.el.on('click', this.onClick, this);
1242         }
1243         
1244     },
1245     
1246     onToggleClick : function()
1247     {
1248         var headerEl = this.headerEl();
1249         
1250         if(!headerEl){
1251             return;
1252         }
1253         
1254         if(this.expanded){
1255             this.collapse();
1256             return;
1257         }
1258         
1259         this.expand();
1260     },
1261     
1262     expand : function()
1263     {
1264         if(this.fireEvent('expand', this)) {
1265             
1266             this.expanded = true;
1267             
1268             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1269             
1270             this.el.select('.panel-body',true).first().removeClass('hide');
1271             
1272             var toggleEl = this.toggleEl();
1273
1274             if(!toggleEl){
1275                 return;
1276             }
1277
1278             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1279         }
1280         
1281     },
1282     
1283     collapse : function()
1284     {
1285         if(this.fireEvent('collapse', this)) {
1286             
1287             this.expanded = false;
1288             
1289             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1290             this.el.select('.panel-body',true).first().addClass('hide');
1291         
1292             var toggleEl = this.toggleEl();
1293
1294             if(!toggleEl){
1295                 return;
1296             }
1297
1298             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1299         }
1300     },
1301     
1302     toggleEl : function()
1303     {
1304         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1305             return;
1306         }
1307         
1308         return this.el.select('.panel-heading .fa',true).first();
1309     },
1310     
1311     headerEl : function()
1312     {
1313         if(!this.el || !this.panel.length || !this.header.length){
1314             return;
1315         }
1316         
1317         return this.el.select('.panel-heading',true).first()
1318     },
1319     
1320     bodyEl : function()
1321     {
1322         if(!this.el || !this.panel.length){
1323             return;
1324         }
1325         
1326         return this.el.select('.panel-body',true).first()
1327     },
1328     
1329     titleEl : function()
1330     {
1331         if(!this.el || !this.panel.length || !this.header.length){
1332             return;
1333         }
1334         
1335         return this.el.select('.panel-title',true).first();
1336     },
1337     
1338     setTitle : function(v)
1339     {
1340         var titleEl = this.titleEl();
1341         
1342         if(!titleEl){
1343             return;
1344         }
1345         
1346         titleEl.dom.innerHTML = v;
1347     },
1348     
1349     getTitle : function()
1350     {
1351         
1352         var titleEl = this.titleEl();
1353         
1354         if(!titleEl){
1355             return '';
1356         }
1357         
1358         return titleEl.dom.innerHTML;
1359     },
1360     
1361     setRightTitle : function(v)
1362     {
1363         var t = this.el.select('.panel-header-right',true).first();
1364         
1365         if(!t){
1366             return;
1367         }
1368         
1369         t.dom.innerHTML = v;
1370     },
1371     
1372     onClick : function(e)
1373     {
1374         e.preventDefault();
1375         
1376         this.fireEvent('click', this, e);
1377     }
1378    
1379 });
1380
1381  /*
1382  * - LGPL
1383  *
1384  * image
1385  * 
1386  */
1387
1388
1389 /**
1390  * @class Roo.bootstrap.Img
1391  * @extends Roo.bootstrap.Component
1392  * Bootstrap Img class
1393  * @cfg {Boolean} imgResponsive false | true
1394  * @cfg {String} border rounded | circle | thumbnail
1395  * @cfg {String} src image source
1396  * @cfg {String} alt image alternative text
1397  * @cfg {String} href a tag href
1398  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
1399  * @cfg {String} xsUrl xs image source
1400  * @cfg {String} smUrl sm image source
1401  * @cfg {String} mdUrl md image source
1402  * @cfg {String} lgUrl lg image source
1403  * 
1404  * @constructor
1405  * Create a new Input
1406  * @param {Object} config The config object
1407  */
1408
1409 Roo.bootstrap.Img = function(config){
1410     Roo.bootstrap.Img.superclass.constructor.call(this, config);
1411     
1412     this.addEvents({
1413         // img events
1414         /**
1415          * @event click
1416          * The img click event for the img.
1417          * @param {Roo.EventObject} e
1418          */
1419         "click" : true
1420     });
1421 };
1422
1423 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
1424     
1425     imgResponsive: true,
1426     border: '',
1427     src: 'about:blank',
1428     href: false,
1429     target: false,
1430     xsUrl: '',
1431     smUrl: '',
1432     mdUrl: '',
1433     lgUrl: '',
1434
1435     getAutoCreate : function()
1436     {   
1437         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1438             return this.createSingleImg();
1439         }
1440         
1441         var cfg = {
1442             tag: 'div',
1443             cls: 'roo-image-responsive-group',
1444             cn: []
1445         };
1446         var _this = this;
1447         
1448         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
1449             
1450             if(!_this[size + 'Url']){
1451                 return;
1452             }
1453             
1454             var img = {
1455                 tag: 'img',
1456                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
1457                 html: _this.html || cfg.html,
1458                 src: _this[size + 'Url']
1459             };
1460             
1461             img.cls += ' roo-image-responsive-' + size;
1462             
1463             var s = ['xs', 'sm', 'md', 'lg'];
1464             
1465             s.splice(s.indexOf(size), 1);
1466             
1467             Roo.each(s, function(ss){
1468                 img.cls += ' hidden-' + ss;
1469             });
1470             
1471             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
1472                 cfg.cls += ' img-' + _this.border;
1473             }
1474             
1475             if(_this.alt){
1476                 cfg.alt = _this.alt;
1477             }
1478             
1479             if(_this.href){
1480                 var a = {
1481                     tag: 'a',
1482                     href: _this.href,
1483                     cn: [
1484                         img
1485                     ]
1486                 };
1487
1488                 if(this.target){
1489                     a.target = _this.target;
1490                 }
1491             }
1492             
1493             cfg.cn.push((_this.href) ? a : img);
1494             
1495         });
1496         
1497         return cfg;
1498     },
1499     
1500     createSingleImg : function()
1501     {
1502         var cfg = {
1503             tag: 'img',
1504             cls: (this.imgResponsive) ? 'img-responsive' : '',
1505             html : null,
1506             src : 'about:blank'  // just incase src get's set to undefined?!?
1507         };
1508         
1509         cfg.html = this.html || cfg.html;
1510         
1511         cfg.src = this.src || cfg.src;
1512         
1513         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
1514             cfg.cls += ' img-' + this.border;
1515         }
1516         
1517         if(this.alt){
1518             cfg.alt = this.alt;
1519         }
1520         
1521         if(this.href){
1522             var a = {
1523                 tag: 'a',
1524                 href: this.href,
1525                 cn: [
1526                     cfg
1527                 ]
1528             };
1529             
1530             if(this.target){
1531                 a.target = this.target;
1532             }
1533             
1534         }
1535         
1536         return (this.href) ? a : cfg;
1537     },
1538     
1539     initEvents: function() 
1540     {
1541         if(!this.href){
1542             this.el.on('click', this.onClick, this);
1543         }
1544         
1545     },
1546     
1547     onClick : function(e)
1548     {
1549         Roo.log('img onclick');
1550         this.fireEvent('click', this, e);
1551     },
1552     /**
1553      * Sets the url of the image - used to update it
1554      * @param {String} url the url of the image
1555      */
1556     
1557     setSrc : function(url)
1558     {
1559         this.src =  url;
1560         
1561         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
1562             this.el.dom.src =  url;
1563             return;
1564         }
1565         
1566         this.el.select('img', true).first().dom.src =  url;
1567     }
1568     
1569     
1570    
1571 });
1572
1573  /*
1574  * - LGPL
1575  *
1576  * image
1577  * 
1578  */
1579
1580
1581 /**
1582  * @class Roo.bootstrap.Link
1583  * @extends Roo.bootstrap.Component
1584  * Bootstrap Link Class
1585  * @cfg {String} alt image alternative text
1586  * @cfg {String} href a tag href
1587  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
1588  * @cfg {String} html the content of the link.
1589  * @cfg {String} anchor name for the anchor link
1590  * @cfg {String} fa - favicon
1591
1592  * @cfg {Boolean} preventDefault (true | false) default false
1593
1594  * 
1595  * @constructor
1596  * Create a new Input
1597  * @param {Object} config The config object
1598  */
1599
1600 Roo.bootstrap.Link = function(config){
1601     Roo.bootstrap.Link.superclass.constructor.call(this, config);
1602     
1603     this.addEvents({
1604         // img events
1605         /**
1606          * @event click
1607          * The img click event for the img.
1608          * @param {Roo.EventObject} e
1609          */
1610         "click" : true
1611     });
1612 };
1613
1614 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
1615     
1616     href: false,
1617     target: false,
1618     preventDefault: false,
1619     anchor : false,
1620     alt : false,
1621     fa: false,
1622
1623
1624     getAutoCreate : function()
1625     {
1626         var html = this.html || '';
1627         
1628         if (this.fa !== false) {
1629             html = '<i class="fa fa-' + this.fa + '"></i>';
1630         }
1631         var cfg = {
1632             tag: 'a'
1633         };
1634         // anchor's do not require html/href...
1635         if (this.anchor === false) {
1636             cfg.html = html;
1637             cfg.href = this.href || '#';
1638         } else {
1639             cfg.name = this.anchor;
1640             if (this.html !== false || this.fa !== false) {
1641                 cfg.html = html;
1642             }
1643             if (this.href !== false) {
1644                 cfg.href = this.href;
1645             }
1646         }
1647         
1648         if(this.alt !== false){
1649             cfg.alt = this.alt;
1650         }
1651         
1652         
1653         if(this.target !== false) {
1654             cfg.target = this.target;
1655         }
1656         
1657         return cfg;
1658     },
1659     
1660     initEvents: function() {
1661         
1662         if(!this.href || this.preventDefault){
1663             this.el.on('click', this.onClick, this);
1664         }
1665     },
1666     
1667     onClick : function(e)
1668     {
1669         if(this.preventDefault){
1670             e.preventDefault();
1671         }
1672         //Roo.log('img onclick');
1673         this.fireEvent('click', this, e);
1674     }
1675    
1676 });
1677
1678  /*
1679  * - LGPL
1680  *
1681  * header
1682  * 
1683  */
1684
1685 /**
1686  * @class Roo.bootstrap.Header
1687  * @extends Roo.bootstrap.Component
1688  * Bootstrap Header class
1689  * @cfg {String} html content of header
1690  * @cfg {Number} level (1|2|3|4|5|6) default 1
1691  * 
1692  * @constructor
1693  * Create a new Header
1694  * @param {Object} config The config object
1695  */
1696
1697
1698 Roo.bootstrap.Header  = function(config){
1699     Roo.bootstrap.Header.superclass.constructor.call(this, config);
1700 };
1701
1702 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
1703     
1704     //href : false,
1705     html : false,
1706     level : 1,
1707     
1708     
1709     
1710     getAutoCreate : function(){
1711         
1712         
1713         
1714         var cfg = {
1715             tag: 'h' + (1 *this.level),
1716             html: this.html || ''
1717         } ;
1718         
1719         return cfg;
1720     }
1721    
1722 });
1723
1724  
1725
1726  /*
1727  * Based on:
1728  * Ext JS Library 1.1.1
1729  * Copyright(c) 2006-2007, Ext JS, LLC.
1730  *
1731  * Originally Released Under LGPL - original licence link has changed is not relivant.
1732  *
1733  * Fork - LGPL
1734  * <script type="text/javascript">
1735  */
1736  
1737 /**
1738  * @class Roo.bootstrap.MenuMgr
1739  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
1740  * @singleton
1741  */
1742 Roo.bootstrap.MenuMgr = function(){
1743    var menus, active, groups = {}, attached = false, lastShow = new Date();
1744
1745    // private - called when first menu is created
1746    function init(){
1747        menus = {};
1748        active = new Roo.util.MixedCollection();
1749        Roo.get(document).addKeyListener(27, function(){
1750            if(active.length > 0){
1751                hideAll();
1752            }
1753        });
1754    }
1755
1756    // private
1757    function hideAll(){
1758        if(active && active.length > 0){
1759            var c = active.clone();
1760            c.each(function(m){
1761                m.hide();
1762            });
1763        }
1764    }
1765
1766    // private
1767    function onHide(m){
1768        active.remove(m);
1769        if(active.length < 1){
1770            Roo.get(document).un("mouseup", onMouseDown);
1771             
1772            attached = false;
1773        }
1774    }
1775
1776    // private
1777    function onShow(m){
1778        var last = active.last();
1779        lastShow = new Date();
1780        active.add(m);
1781        if(!attached){
1782           Roo.get(document).on("mouseup", onMouseDown);
1783            
1784            attached = true;
1785        }
1786        if(m.parentMenu){
1787           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
1788           m.parentMenu.activeChild = m;
1789        }else if(last && last.isVisible()){
1790           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
1791        }
1792    }
1793
1794    // private
1795    function onBeforeHide(m){
1796        if(m.activeChild){
1797            m.activeChild.hide();
1798        }
1799        if(m.autoHideTimer){
1800            clearTimeout(m.autoHideTimer);
1801            delete m.autoHideTimer;
1802        }
1803    }
1804
1805    // private
1806    function onBeforeShow(m){
1807        var pm = m.parentMenu;
1808        if(!pm && !m.allowOtherMenus){
1809            hideAll();
1810        }else if(pm && pm.activeChild && active != m){
1811            pm.activeChild.hide();
1812        }
1813    }
1814
1815    // private this should really trigger on mouseup..
1816    function onMouseDown(e){
1817         Roo.log("on Mouse Up");
1818         
1819         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
1820             Roo.log("MenuManager hideAll");
1821             hideAll();
1822             e.stopEvent();
1823         }
1824         
1825         
1826    }
1827
1828    // private
1829    function onBeforeCheck(mi, state){
1830        if(state){
1831            var g = groups[mi.group];
1832            for(var i = 0, l = g.length; i < l; i++){
1833                if(g[i] != mi){
1834                    g[i].setChecked(false);
1835                }
1836            }
1837        }
1838    }
1839
1840    return {
1841
1842        /**
1843         * Hides all menus that are currently visible
1844         */
1845        hideAll : function(){
1846             hideAll();  
1847        },
1848
1849        // private
1850        register : function(menu){
1851            if(!menus){
1852                init();
1853            }
1854            menus[menu.id] = menu;
1855            menu.on("beforehide", onBeforeHide);
1856            menu.on("hide", onHide);
1857            menu.on("beforeshow", onBeforeShow);
1858            menu.on("show", onShow);
1859            var g = menu.group;
1860            if(g && menu.events["checkchange"]){
1861                if(!groups[g]){
1862                    groups[g] = [];
1863                }
1864                groups[g].push(menu);
1865                menu.on("checkchange", onCheck);
1866            }
1867        },
1868
1869         /**
1870          * Returns a {@link Roo.menu.Menu} object
1871          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
1872          * be used to generate and return a new Menu instance.
1873          */
1874        get : function(menu){
1875            if(typeof menu == "string"){ // menu id
1876                return menus[menu];
1877            }else if(menu.events){  // menu instance
1878                return menu;
1879            }
1880            /*else if(typeof menu.length == 'number'){ // array of menu items?
1881                return new Roo.bootstrap.Menu({items:menu});
1882            }else{ // otherwise, must be a config
1883                return new Roo.bootstrap.Menu(menu);
1884            }
1885            */
1886            return false;
1887        },
1888
1889        // private
1890        unregister : function(menu){
1891            delete menus[menu.id];
1892            menu.un("beforehide", onBeforeHide);
1893            menu.un("hide", onHide);
1894            menu.un("beforeshow", onBeforeShow);
1895            menu.un("show", onShow);
1896            var g = menu.group;
1897            if(g && menu.events["checkchange"]){
1898                groups[g].remove(menu);
1899                menu.un("checkchange", onCheck);
1900            }
1901        },
1902
1903        // private
1904        registerCheckable : function(menuItem){
1905            var g = menuItem.group;
1906            if(g){
1907                if(!groups[g]){
1908                    groups[g] = [];
1909                }
1910                groups[g].push(menuItem);
1911                menuItem.on("beforecheckchange", onBeforeCheck);
1912            }
1913        },
1914
1915        // private
1916        unregisterCheckable : function(menuItem){
1917            var g = menuItem.group;
1918            if(g){
1919                groups[g].remove(menuItem);
1920                menuItem.un("beforecheckchange", onBeforeCheck);
1921            }
1922        }
1923    };
1924 }();/*
1925  * - LGPL
1926  *
1927  * menu
1928  * 
1929  */
1930
1931 /**
1932  * @class Roo.bootstrap.Menu
1933  * @extends Roo.bootstrap.Component
1934  * Bootstrap Menu class - container for MenuItems
1935  * @cfg {String} type (dropdown|treeview|submenu) type of menu
1936  * @cfg {bool} hidden  if the menu should be hidden when rendered.
1937  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
1938  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
1939  * 
1940  * @constructor
1941  * Create a new Menu
1942  * @param {Object} config The config object
1943  */
1944
1945
1946 Roo.bootstrap.Menu = function(config){
1947     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
1948     if (this.registerMenu && this.type != 'treeview')  {
1949         Roo.bootstrap.MenuMgr.register(this);
1950     }
1951     this.addEvents({
1952         /**
1953          * @event beforeshow
1954          * Fires before this menu is displayed
1955          * @param {Roo.menu.Menu} this
1956          */
1957         beforeshow : true,
1958         /**
1959          * @event beforehide
1960          * Fires before this menu is hidden
1961          * @param {Roo.menu.Menu} this
1962          */
1963         beforehide : true,
1964         /**
1965          * @event show
1966          * Fires after this menu is displayed
1967          * @param {Roo.menu.Menu} this
1968          */
1969         show : true,
1970         /**
1971          * @event hide
1972          * Fires after this menu is hidden
1973          * @param {Roo.menu.Menu} this
1974          */
1975         hide : true,
1976         /**
1977          * @event click
1978          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
1979          * @param {Roo.menu.Menu} this
1980          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1981          * @param {Roo.EventObject} e
1982          */
1983         click : true,
1984         /**
1985          * @event mouseover
1986          * Fires when the mouse is hovering over this menu
1987          * @param {Roo.menu.Menu} this
1988          * @param {Roo.EventObject} e
1989          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1990          */
1991         mouseover : true,
1992         /**
1993          * @event mouseout
1994          * Fires when the mouse exits this menu
1995          * @param {Roo.menu.Menu} this
1996          * @param {Roo.EventObject} e
1997          * @param {Roo.menu.Item} menuItem The menu item that was clicked
1998          */
1999         mouseout : true,
2000         /**
2001          * @event itemclick
2002          * Fires when a menu item contained in this menu is clicked
2003          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
2004          * @param {Roo.EventObject} e
2005          */
2006         itemclick: true
2007     });
2008     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
2009 };
2010
2011 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
2012     
2013    /// html : false,
2014     //align : '',
2015     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
2016     type: false,
2017     /**
2018      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
2019      */
2020     registerMenu : true,
2021     
2022     menuItems :false, // stores the menu items..
2023     
2024     hidden:true,
2025         
2026     parentMenu : false,
2027     
2028     stopEvent : true,
2029     
2030     isLink : false,
2031     
2032     getChildContainer : function() {
2033         return this.el;  
2034     },
2035     
2036     getAutoCreate : function(){
2037          
2038         //if (['right'].indexOf(this.align)!==-1) {
2039         //    cfg.cn[1].cls += ' pull-right'
2040         //}
2041         
2042         
2043         var cfg = {
2044             tag : 'ul',
2045             cls : 'dropdown-menu' ,
2046             style : 'z-index:1000'
2047             
2048         };
2049         
2050         if (this.type === 'submenu') {
2051             cfg.cls = 'submenu active';
2052         }
2053         if (this.type === 'treeview') {
2054             cfg.cls = 'treeview-menu';
2055         }
2056         
2057         return cfg;
2058     },
2059     initEvents : function() {
2060         
2061        // Roo.log("ADD event");
2062        // Roo.log(this.triggerEl.dom);
2063         
2064         this.triggerEl.on('click', this.onTriggerClick, this);
2065         
2066         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
2067         
2068         this.triggerEl.addClass('dropdown-toggle');
2069         
2070         if (Roo.isTouch) {
2071             this.el.on('touchstart'  , this.onTouch, this);
2072         }
2073         this.el.on('click' , this.onClick, this);
2074
2075         this.el.on("mouseover", this.onMouseOver, this);
2076         this.el.on("mouseout", this.onMouseOut, this);
2077         
2078     },
2079     
2080     findTargetItem : function(e)
2081     {
2082         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
2083         if(!t){
2084             return false;
2085         }
2086         //Roo.log(t);         Roo.log(t.id);
2087         if(t && t.id){
2088             //Roo.log(this.menuitems);
2089             return this.menuitems.get(t.id);
2090             
2091             //return this.items.get(t.menuItemId);
2092         }
2093         
2094         return false;
2095     },
2096     
2097     onTouch : function(e) 
2098     {
2099         Roo.log("menu.onTouch");
2100         //e.stopEvent(); this make the user popdown broken
2101         this.onClick(e);
2102     },
2103     
2104     onClick : function(e)
2105     {
2106         Roo.log("menu.onClick");
2107         
2108         var t = this.findTargetItem(e);
2109         if(!t || t.isContainer){
2110             return;
2111         }
2112         Roo.log(e);
2113         /*
2114         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
2115             if(t == this.activeItem && t.shouldDeactivate(e)){
2116                 this.activeItem.deactivate();
2117                 delete this.activeItem;
2118                 return;
2119             }
2120             if(t.canActivate){
2121                 this.setActiveItem(t, true);
2122             }
2123             return;
2124             
2125             
2126         }
2127         */
2128        
2129         Roo.log('pass click event');
2130         
2131         t.onClick(e);
2132         
2133         this.fireEvent("click", this, t, e);
2134         
2135         var _this = this;
2136         
2137         if(!t.href.length || t.href == '#'){
2138             (function() { _this.hide(); }).defer(100);
2139         }
2140         
2141     },
2142     
2143     onMouseOver : function(e){
2144         var t  = this.findTargetItem(e);
2145         //Roo.log(t);
2146         //if(t){
2147         //    if(t.canActivate && !t.disabled){
2148         //        this.setActiveItem(t, true);
2149         //    }
2150         //}
2151         
2152         this.fireEvent("mouseover", this, e, t);
2153     },
2154     isVisible : function(){
2155         return !this.hidden;
2156     },
2157      onMouseOut : function(e){
2158         var t  = this.findTargetItem(e);
2159         
2160         //if(t ){
2161         //    if(t == this.activeItem && t.shouldDeactivate(e)){
2162         //        this.activeItem.deactivate();
2163         //        delete this.activeItem;
2164         //    }
2165         //}
2166         this.fireEvent("mouseout", this, e, t);
2167     },
2168     
2169     
2170     /**
2171      * Displays this menu relative to another element
2172      * @param {String/HTMLElement/Roo.Element} element The element to align to
2173      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
2174      * the element (defaults to this.defaultAlign)
2175      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2176      */
2177     show : function(el, pos, parentMenu){
2178         this.parentMenu = parentMenu;
2179         if(!this.el){
2180             this.render();
2181         }
2182         this.fireEvent("beforeshow", this);
2183         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
2184     },
2185      /**
2186      * Displays this menu at a specific xy position
2187      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
2188      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
2189      */
2190     showAt : function(xy, parentMenu, /* private: */_e){
2191         this.parentMenu = parentMenu;
2192         if(!this.el){
2193             this.render();
2194         }
2195         if(_e !== false){
2196             this.fireEvent("beforeshow", this);
2197             //xy = this.el.adjustForConstraints(xy);
2198         }
2199         
2200         //this.el.show();
2201         this.hideMenuItems();
2202         this.hidden = false;
2203         this.triggerEl.addClass('open');
2204         
2205         if(this.el.getWidth() + xy[0] > Roo.lib.Dom.getViewWidth()){
2206             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
2207         }
2208         
2209         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
2210             this.el.setXY(xy);
2211         }
2212         
2213         this.focus();
2214         this.fireEvent("show", this);
2215     },
2216     
2217     focus : function(){
2218         return;
2219         if(!this.hidden){
2220             this.doFocus.defer(50, this);
2221         }
2222     },
2223
2224     doFocus : function(){
2225         if(!this.hidden){
2226             this.focusEl.focus();
2227         }
2228     },
2229
2230     /**
2231      * Hides this menu and optionally all parent menus
2232      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
2233      */
2234     hide : function(deep)
2235     {
2236         
2237         this.hideMenuItems();
2238         if(this.el && this.isVisible()){
2239             this.fireEvent("beforehide", this);
2240             if(this.activeItem){
2241                 this.activeItem.deactivate();
2242                 this.activeItem = null;
2243             }
2244             this.triggerEl.removeClass('open');;
2245             this.hidden = true;
2246             this.fireEvent("hide", this);
2247         }
2248         if(deep === true && this.parentMenu){
2249             this.parentMenu.hide(true);
2250         }
2251     },
2252     
2253     onTriggerClick : function(e)
2254     {
2255         Roo.log('trigger click');
2256         
2257         var target = e.getTarget();
2258         
2259         Roo.log(target.nodeName.toLowerCase());
2260         
2261         if(target.nodeName.toLowerCase() === 'i'){
2262             e.preventDefault();
2263         }
2264         
2265     },
2266     
2267     onTriggerPress  : function(e)
2268     {
2269         Roo.log('trigger press');
2270         //Roo.log(e.getTarget());
2271        // Roo.log(this.triggerEl.dom);
2272        
2273         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
2274         var pel = Roo.get(e.getTarget());
2275         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
2276             Roo.log('is treeview or dropdown?');
2277             return;
2278         }
2279         
2280         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
2281             return;
2282         }
2283         
2284         if (this.isVisible()) {
2285             Roo.log('hide');
2286             this.hide();
2287         } else {
2288             Roo.log('show');
2289             this.show(this.triggerEl, false, false);
2290         }
2291         
2292         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
2293             e.stopEvent();
2294         }
2295         
2296     },
2297        
2298     
2299     hideMenuItems : function()
2300     {
2301         Roo.log("hide Menu Items");
2302         if (!this.el) { 
2303             return;
2304         }
2305         //$(backdrop).remove()
2306         this.el.select('.open',true).each(function(aa) {
2307             
2308             aa.removeClass('open');
2309           //var parent = getParent($(this))
2310           //var relatedTarget = { relatedTarget: this }
2311           
2312            //$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget))
2313           //if (e.isDefaultPrevented()) return
2314            //$parent.removeClass('open').trigger('hidden.bs.dropdown', relatedTarget)
2315         });
2316     },
2317     addxtypeChild : function (tree, cntr) {
2318         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
2319           
2320         this.menuitems.add(comp);
2321         return comp;
2322
2323     },
2324     getEl : function()
2325     {
2326         Roo.log(this.el);
2327         return this.el;
2328     }
2329 });
2330
2331  
2332  /*
2333  * - LGPL
2334  *
2335  * menu item
2336  * 
2337  */
2338
2339
2340 /**
2341  * @class Roo.bootstrap.MenuItem
2342  * @extends Roo.bootstrap.Component
2343  * Bootstrap MenuItem class
2344  * @cfg {String} html the menu label
2345  * @cfg {String} href the link
2346  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
2347  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
2348  * @cfg {Boolean} active  used on sidebars to highlight active itesm
2349  * @cfg {String} fa favicon to show on left of menu item.
2350  * @cfg {Roo.bootsrap.Menu} menu the child menu.
2351  * 
2352  * 
2353  * @constructor
2354  * Create a new MenuItem
2355  * @param {Object} config The config object
2356  */
2357
2358
2359 Roo.bootstrap.MenuItem = function(config){
2360     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
2361     this.addEvents({
2362         // raw events
2363         /**
2364          * @event click
2365          * The raw click event for the entire grid.
2366          * @param {Roo.bootstrap.MenuItem} this
2367          * @param {Roo.EventObject} e
2368          */
2369         "click" : true
2370     });
2371 };
2372
2373 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
2374     
2375     href : false,
2376     html : false,
2377     preventDefault: false,
2378     isContainer : false,
2379     active : false,
2380     fa: false,
2381     
2382     getAutoCreate : function(){
2383         
2384         if(this.isContainer){
2385             return {
2386                 tag: 'li',
2387                 cls: 'dropdown-menu-item'
2388             };
2389         }
2390         var ctag = {
2391             tag: 'span',
2392             html: 'Link'
2393         };
2394         
2395         var anc = {
2396             tag : 'a',
2397             href : '#',
2398             cn : [  ]
2399         };
2400         
2401         if (this.fa !== false) {
2402             anc.cn.push({
2403                 tag : 'i',
2404                 cls : 'fa fa-' + this.fa
2405             });
2406         }
2407         
2408         anc.cn.push(ctag);
2409         
2410         
2411         var cfg= {
2412             tag: 'li',
2413             cls: 'dropdown-menu-item',
2414             cn: [ anc ]
2415         };
2416         if (this.parent().type == 'treeview') {
2417             cfg.cls = 'treeview-menu';
2418         }
2419         if (this.active) {
2420             cfg.cls += ' active';
2421         }
2422         
2423         
2424         
2425         anc.href = this.href || cfg.cn[0].href ;
2426         ctag.html = this.html || cfg.cn[0].html ;
2427         return cfg;
2428     },
2429     
2430     initEvents: function()
2431     {
2432         if (this.parent().type == 'treeview') {
2433             this.el.select('a').on('click', this.onClick, this);
2434         }
2435         
2436         if (this.menu) {
2437             this.menu.parentType = this.xtype;
2438             this.menu.triggerEl = this.el;
2439             this.menu = this.addxtype(Roo.apply({}, this.menu));
2440         }
2441         
2442     },
2443     onClick : function(e)
2444     {
2445         Roo.log('item on click ');
2446         
2447         if(this.preventDefault){
2448             e.preventDefault();
2449         }
2450         //this.parent().hideMenuItems();
2451         
2452         this.fireEvent('click', this, e);
2453     },
2454     getEl : function()
2455     {
2456         return this.el;
2457     } 
2458 });
2459
2460  
2461
2462  /*
2463  * - LGPL
2464  *
2465  * menu separator
2466  * 
2467  */
2468
2469
2470 /**
2471  * @class Roo.bootstrap.MenuSeparator
2472  * @extends Roo.bootstrap.Component
2473  * Bootstrap MenuSeparator class
2474  * 
2475  * @constructor
2476  * Create a new MenuItem
2477  * @param {Object} config The config object
2478  */
2479
2480
2481 Roo.bootstrap.MenuSeparator = function(config){
2482     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
2483 };
2484
2485 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
2486     
2487     getAutoCreate : function(){
2488         var cfg = {
2489             cls: 'divider',
2490             tag : 'li'
2491         };
2492         
2493         return cfg;
2494     }
2495    
2496 });
2497
2498  
2499
2500  
2501 /*
2502 * Licence: LGPL
2503 */
2504
2505 /**
2506  * @class Roo.bootstrap.Modal
2507  * @extends Roo.bootstrap.Component
2508  * Bootstrap Modal class
2509  * @cfg {String} title Title of dialog
2510  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
2511  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
2512  * @cfg {Boolean} specificTitle default false
2513  * @cfg {Array} buttons Array of buttons or standard button set..
2514  * @cfg {String} buttonPosition (left|right|center) default right
2515  * @cfg {Boolean} animate default true
2516  * @cfg {Boolean} allow_close default true
2517  * @cfg {Boolean} fitwindow default false
2518  * @cfg {String} size (sm|lg) default empty
2519  *
2520  *
2521  * @constructor
2522  * Create a new Modal Dialog
2523  * @param {Object} config The config object
2524  */
2525
2526 Roo.bootstrap.Modal = function(config){
2527     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
2528     this.addEvents({
2529         // raw events
2530         /**
2531          * @event btnclick
2532          * The raw btnclick event for the button
2533          * @param {Roo.EventObject} e
2534          */
2535         "btnclick" : true,
2536         /**
2537          * @event resize
2538          * Fire when dialog resize
2539          * @param {Roo.bootstrap.Modal} this
2540          * @param {Roo.EventObject} e
2541          */
2542         "resize" : true
2543     });
2544     this.buttons = this.buttons || [];
2545
2546     if (this.tmpl) {
2547         this.tmpl = Roo.factory(this.tmpl);
2548     }
2549
2550 };
2551
2552 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
2553
2554     title : 'test dialog',
2555
2556     buttons : false,
2557
2558     // set on load...
2559
2560     html: false,
2561
2562     tmp: false,
2563
2564     specificTitle: false,
2565
2566     buttonPosition: 'right',
2567
2568     allow_close : true,
2569
2570     animate : true,
2571
2572     fitwindow: false,
2573
2574
2575      // private
2576     dialogEl: false,
2577     bodyEl:  false,
2578     footerEl:  false,
2579     titleEl:  false,
2580     closeEl:  false,
2581
2582     size: '',
2583
2584
2585     onRender : function(ct, position)
2586     {
2587         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
2588
2589         if(!this.el){
2590             var cfg = Roo.apply({},  this.getAutoCreate());
2591             cfg.id = Roo.id();
2592             //if(!cfg.name){
2593             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
2594             //}
2595             //if (!cfg.name.length) {
2596             //    delete cfg.name;
2597            // }
2598             if (this.cls) {
2599                 cfg.cls += ' ' + this.cls;
2600             }
2601             if (this.style) {
2602                 cfg.style = this.style;
2603             }
2604             this.el = Roo.get(document.body).createChild(cfg, position);
2605         }
2606         //var type = this.el.dom.type;
2607
2608
2609         if(this.tabIndex !== undefined){
2610             this.el.dom.setAttribute('tabIndex', this.tabIndex);
2611         }
2612
2613         this.dialogEl = this.el.select('.modal-dialog',true).first();
2614         this.bodyEl = this.el.select('.modal-body',true).first();
2615         this.closeEl = this.el.select('.modal-header .close', true).first();
2616         this.headerEl = this.el.select('.modal-header',true).first();
2617         this.titleEl = this.el.select('.modal-title',true).first();
2618         this.footerEl = this.el.select('.modal-footer',true).first();
2619
2620         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
2621         this.maskEl.enableDisplayMode("block");
2622         this.maskEl.hide();
2623         //this.el.addClass("x-dlg-modal");
2624
2625         if (this.buttons.length) {
2626             Roo.each(this.buttons, function(bb) {
2627                 var b = Roo.apply({}, bb);
2628                 b.xns = b.xns || Roo.bootstrap;
2629                 b.xtype = b.xtype || 'Button';
2630                 if (typeof(b.listeners) == 'undefined') {
2631                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
2632                 }
2633
2634                 var btn = Roo.factory(b);
2635
2636                 btn.render(this.el.select('.modal-footer div').first());
2637
2638             },this);
2639         }
2640         // render the children.
2641         var nitems = [];
2642
2643         if(typeof(this.items) != 'undefined'){
2644             var items = this.items;
2645             delete this.items;
2646
2647             for(var i =0;i < items.length;i++) {
2648                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
2649             }
2650         }
2651
2652         this.items = nitems;
2653
2654         // where are these used - they used to be body/close/footer
2655
2656
2657         this.initEvents();
2658         //this.el.addClass([this.fieldClass, this.cls]);
2659
2660     },
2661
2662     getAutoCreate : function(){
2663
2664
2665         var bdy = {
2666                 cls : 'modal-body',
2667                 html : this.html || ''
2668         };
2669
2670         var title = {
2671             tag: 'h4',
2672             cls : 'modal-title',
2673             html : this.title
2674         };
2675
2676         if(this.specificTitle){
2677             title = this.title;
2678
2679         };
2680
2681         var header = [];
2682         if (this.allow_close) {
2683             header.push({
2684                 tag: 'button',
2685                 cls : 'close',
2686                 html : '&times'
2687             });
2688         }
2689
2690         header.push(title);
2691
2692         var size = '';
2693
2694         if(this.size.length){
2695             size = 'modal-' + this.size;
2696         }
2697
2698         var modal = {
2699             cls: "modal",
2700             style : 'display: none',
2701             cn : [
2702                 {
2703                     cls: "modal-dialog " + size,
2704                     cn : [
2705                         {
2706                             cls : "modal-content",
2707                             cn : [
2708                                 {
2709                                     cls : 'modal-header',
2710                                     cn : header
2711                                 },
2712                                 bdy,
2713                                 {
2714                                     cls : 'modal-footer',
2715                                     cn : [
2716                                         {
2717                                             tag: 'div',
2718                                             cls: 'btn-' + this.buttonPosition
2719                                         }
2720                                     ]
2721
2722                                 }
2723
2724
2725                             ]
2726
2727                         }
2728                     ]
2729
2730                 }
2731             ]
2732         };
2733
2734         if(this.animate){
2735             modal.cls += ' fade';
2736         }
2737
2738         return modal;
2739
2740     },
2741     getChildContainer : function() {
2742
2743          return this.bodyEl;
2744
2745     },
2746     getButtonContainer : function() {
2747          return this.el.select('.modal-footer div',true).first();
2748
2749     },
2750     initEvents : function()
2751     {
2752         if (this.allow_close) {
2753             this.closeEl.on('click', this.hide, this);
2754         }
2755         Roo.EventManager.onWindowResize(this.resize, this, true);
2756
2757
2758     },
2759
2760     resize : function()
2761     {
2762         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),  Roo.lib.Dom.getViewHeight(true));
2763         if (this.fitwindow) {
2764             var w = this.width || Roo.lib.Dom.getViewportWidth(true) - 30;
2765             var h = this.height || Roo.lib.Dom.getViewportHeight(true) - 60;
2766             this.setSize(w,h);
2767         }
2768     },
2769
2770     setSize : function(w,h)
2771     {
2772         if (!w && !h) {
2773             return;
2774         }
2775         this.resizeTo(w,h);
2776     },
2777
2778     show : function() {
2779
2780         if (!this.rendered) {
2781             this.render();
2782         }
2783
2784         this.el.setStyle('display', 'block');
2785
2786         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
2787             var _this = this;
2788             (function(){
2789                 this.el.addClass('in');
2790             }).defer(50, this);
2791         }else{
2792             this.el.addClass('in');
2793
2794         }
2795
2796         // not sure how we can show data in here..
2797         //if (this.tmpl) {
2798         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
2799         //}
2800
2801         Roo.get(document.body).addClass("x-body-masked");
2802         
2803         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
2804         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2805         this.maskEl.show();
2806         
2807         this.resize();
2808         
2809         this.fireEvent('show', this);
2810
2811         // set zindex here - otherwise it appears to be ignored...
2812         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
2813
2814         (function () {
2815             this.items.forEach( function(e) {
2816                 e.layout ? e.layout() : false;
2817
2818             });
2819         }).defer(100,this);
2820
2821     },
2822     hide : function()
2823     {
2824         if(this.fireEvent("beforehide", this) !== false){
2825             this.maskEl.hide();
2826             Roo.get(document.body).removeClass("x-body-masked");
2827             this.el.removeClass('in');
2828             this.el.select('.modal-dialog', true).first().setStyle('transform','');
2829
2830             if(this.animate){ // why
2831                 var _this = this;
2832                 (function(){ _this.el.setStyle('display', 'none'); }).defer(150);
2833             }else{
2834                 this.el.setStyle('display', 'none');
2835             }
2836             this.fireEvent('hide', this);
2837         }
2838     },
2839
2840     addButton : function(str, cb)
2841     {
2842
2843
2844         var b = Roo.apply({}, { html : str } );
2845         b.xns = b.xns || Roo.bootstrap;
2846         b.xtype = b.xtype || 'Button';
2847         if (typeof(b.listeners) == 'undefined') {
2848             b.listeners = { click : cb.createDelegate(this)  };
2849         }
2850
2851         var btn = Roo.factory(b);
2852
2853         btn.render(this.el.select('.modal-footer div').first());
2854
2855         return btn;
2856
2857     },
2858
2859     setDefaultButton : function(btn)
2860     {
2861         //this.el.select('.modal-footer').()
2862     },
2863     diff : false,
2864
2865     resizeTo: function(w,h)
2866     {
2867         // skip.. ?? why??
2868
2869         this.dialogEl.setWidth(w);
2870         if (this.diff === false) {
2871             this.diff = this.dialogEl.getHeight() - this.bodyEl.getHeight();
2872         }
2873
2874         this.bodyEl.setHeight(h-this.diff);
2875
2876         this.fireEvent('resize', this);
2877
2878     },
2879     setContentSize  : function(w, h)
2880     {
2881
2882     },
2883     onButtonClick: function(btn,e)
2884     {
2885         //Roo.log([a,b,c]);
2886         this.fireEvent('btnclick', btn.name, e);
2887     },
2888      /**
2889      * Set the title of the Dialog
2890      * @param {String} str new Title
2891      */
2892     setTitle: function(str) {
2893         this.titleEl.dom.innerHTML = str;
2894     },
2895     /**
2896      * Set the body of the Dialog
2897      * @param {String} str new Title
2898      */
2899     setBody: function(str) {
2900         this.bodyEl.dom.innerHTML = str;
2901     },
2902     /**
2903      * Set the body of the Dialog using the template
2904      * @param {Obj} data - apply this data to the template and replace the body contents.
2905      */
2906     applyBody: function(obj)
2907     {
2908         if (!this.tmpl) {
2909             Roo.log("Error - using apply Body without a template");
2910             //code
2911         }
2912         this.tmpl.overwrite(this.bodyEl, obj);
2913     }
2914
2915 });
2916
2917
2918 Roo.apply(Roo.bootstrap.Modal,  {
2919     /**
2920          * Button config that displays a single OK button
2921          * @type Object
2922          */
2923         OK :  [{
2924             name : 'ok',
2925             weight : 'primary',
2926             html : 'OK'
2927         }],
2928         /**
2929          * Button config that displays Yes and No buttons
2930          * @type Object
2931          */
2932         YESNO : [
2933             {
2934                 name  : 'no',
2935                 html : 'No'
2936             },
2937             {
2938                 name  :'yes',
2939                 weight : 'primary',
2940                 html : 'Yes'
2941             }
2942         ],
2943
2944         /**
2945          * Button config that displays OK and Cancel buttons
2946          * @type Object
2947          */
2948         OKCANCEL : [
2949             {
2950                name : 'cancel',
2951                 html : 'Cancel'
2952             },
2953             {
2954                 name : 'ok',
2955                 weight : 'primary',
2956                 html : 'OK'
2957             }
2958         ],
2959         /**
2960          * Button config that displays Yes, No and Cancel buttons
2961          * @type Object
2962          */
2963         YESNOCANCEL : [
2964             {
2965                 name : 'yes',
2966                 weight : 'primary',
2967                 html : 'Yes'
2968             },
2969             {
2970                 name : 'no',
2971                 html : 'No'
2972             },
2973             {
2974                 name : 'cancel',
2975                 html : 'Cancel'
2976             }
2977         ],
2978         
2979         zIndex : 10001
2980 });
2981 /*
2982  * - LGPL
2983  *
2984  * messagebox - can be used as a replace
2985  * 
2986  */
2987 /**
2988  * @class Roo.MessageBox
2989  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
2990  * Example usage:
2991  *<pre><code>
2992 // Basic alert:
2993 Roo.Msg.alert('Status', 'Changes saved successfully.');
2994
2995 // Prompt for user data:
2996 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
2997     if (btn == 'ok'){
2998         // process text value...
2999     }
3000 });
3001
3002 // Show a dialog using config options:
3003 Roo.Msg.show({
3004    title:'Save Changes?',
3005    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
3006    buttons: Roo.Msg.YESNOCANCEL,
3007    fn: processResult,
3008    animEl: 'elId'
3009 });
3010 </code></pre>
3011  * @singleton
3012  */
3013 Roo.bootstrap.MessageBox = function(){
3014     var dlg, opt, mask, waitTimer;
3015     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
3016     var buttons, activeTextEl, bwidth;
3017
3018     
3019     // private
3020     var handleButton = function(button){
3021         dlg.hide();
3022         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
3023     };
3024
3025     // private
3026     var handleHide = function(){
3027         if(opt && opt.cls){
3028             dlg.el.removeClass(opt.cls);
3029         }
3030         //if(waitTimer){
3031         //    Roo.TaskMgr.stop(waitTimer);
3032         //    waitTimer = null;
3033         //}
3034     };
3035
3036     // private
3037     var updateButtons = function(b){
3038         var width = 0;
3039         if(!b){
3040             buttons["ok"].hide();
3041             buttons["cancel"].hide();
3042             buttons["yes"].hide();
3043             buttons["no"].hide();
3044             //dlg.footer.dom.style.display = 'none';
3045             return width;
3046         }
3047         dlg.footerEl.dom.style.display = '';
3048         for(var k in buttons){
3049             if(typeof buttons[k] != "function"){
3050                 if(b[k]){
3051                     buttons[k].show();
3052                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
3053                     width += buttons[k].el.getWidth()+15;
3054                 }else{
3055                     buttons[k].hide();
3056                 }
3057             }
3058         }
3059         return width;
3060     };
3061
3062     // private
3063     var handleEsc = function(d, k, e){
3064         if(opt && opt.closable !== false){
3065             dlg.hide();
3066         }
3067         if(e){
3068             e.stopEvent();
3069         }
3070     };
3071
3072     return {
3073         /**
3074          * Returns a reference to the underlying {@link Roo.BasicDialog} element
3075          * @return {Roo.BasicDialog} The BasicDialog element
3076          */
3077         getDialog : function(){
3078            if(!dlg){
3079                 dlg = new Roo.bootstrap.Modal( {
3080                     //draggable: true,
3081                     //resizable:false,
3082                     //constraintoviewport:false,
3083                     //fixedcenter:true,
3084                     //collapsible : false,
3085                     //shim:true,
3086                     //modal: true,
3087                 //    width: 'auto',
3088                   //  height:100,
3089                     //buttonAlign:"center",
3090                     closeClick : function(){
3091                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
3092                             handleButton("no");
3093                         }else{
3094                             handleButton("cancel");
3095                         }
3096                     }
3097                 });
3098                 dlg.render();
3099                 dlg.on("hide", handleHide);
3100                 mask = dlg.mask;
3101                 //dlg.addKeyListener(27, handleEsc);
3102                 buttons = {};
3103                 this.buttons = buttons;
3104                 var bt = this.buttonText;
3105                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
3106                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
3107                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
3108                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
3109                 //Roo.log(buttons);
3110                 bodyEl = dlg.bodyEl.createChild({
3111
3112                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
3113                         '<textarea class="roo-mb-textarea"></textarea>' +
3114                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
3115                 });
3116                 msgEl = bodyEl.dom.firstChild;
3117                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
3118                 textboxEl.enableDisplayMode();
3119                 textboxEl.addKeyListener([10,13], function(){
3120                     if(dlg.isVisible() && opt && opt.buttons){
3121                         if(opt.buttons.ok){
3122                             handleButton("ok");
3123                         }else if(opt.buttons.yes){
3124                             handleButton("yes");
3125                         }
3126                     }
3127                 });
3128                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
3129                 textareaEl.enableDisplayMode();
3130                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
3131                 progressEl.enableDisplayMode();
3132                 
3133                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
3134                 var pf = progressEl.dom.firstChild;
3135                 if (pf) {
3136                     pp = Roo.get(pf.firstChild);
3137                     pp.setHeight(pf.offsetHeight);
3138                 }
3139                 
3140             }
3141             return dlg;
3142         },
3143
3144         /**
3145          * Updates the message box body text
3146          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
3147          * the XHTML-compliant non-breaking space character '&amp;#160;')
3148          * @return {Roo.MessageBox} This message box
3149          */
3150         updateText : function(text)
3151         {
3152             if(!dlg.isVisible() && !opt.width){
3153                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
3154                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
3155             }
3156             msgEl.innerHTML = text || '&#160;';
3157       
3158             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
3159             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
3160             var w = Math.max(
3161                     Math.min(opt.width || cw , this.maxWidth), 
3162                     Math.max(opt.minWidth || this.minWidth, bwidth)
3163             );
3164             if(opt.prompt){
3165                 activeTextEl.setWidth(w);
3166             }
3167             if(dlg.isVisible()){
3168                 dlg.fixedcenter = false;
3169             }
3170             // to big, make it scroll. = But as usual stupid IE does not support
3171             // !important..
3172             
3173             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
3174                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
3175                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
3176             } else {
3177                 bodyEl.dom.style.height = '';
3178                 bodyEl.dom.style.overflowY = '';
3179             }
3180             if (cw > w) {
3181                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
3182             } else {
3183                 bodyEl.dom.style.overflowX = '';
3184             }
3185             
3186             dlg.setContentSize(w, bodyEl.getHeight());
3187             if(dlg.isVisible()){
3188                 dlg.fixedcenter = true;
3189             }
3190             return this;
3191         },
3192
3193         /**
3194          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
3195          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
3196          * @param {Number} value Any number between 0 and 1 (e.g., .5)
3197          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
3198          * @return {Roo.MessageBox} This message box
3199          */
3200         updateProgress : function(value, text){
3201             if(text){
3202                 this.updateText(text);
3203             }
3204             
3205             if (pp) { // weird bug on my firefox - for some reason this is not defined
3206                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
3207                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
3208             }
3209             return this;
3210         },        
3211
3212         /**
3213          * Returns true if the message box is currently displayed
3214          * @return {Boolean} True if the message box is visible, else false
3215          */
3216         isVisible : function(){
3217             return dlg && dlg.isVisible();  
3218         },
3219
3220         /**
3221          * Hides the message box if it is displayed
3222          */
3223         hide : function(){
3224             if(this.isVisible()){
3225                 dlg.hide();
3226             }  
3227         },
3228
3229         /**
3230          * Displays a new message box, or reinitializes an existing message box, based on the config options
3231          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
3232          * The following config object properties are supported:
3233          * <pre>
3234 Property    Type             Description
3235 ----------  ---------------  ------------------------------------------------------------------------------------
3236 animEl            String/Element   An id or Element from which the message box should animate as it opens and
3237                                    closes (defaults to undefined)
3238 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
3239                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
3240 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
3241                                    progress and wait dialogs will ignore this property and always hide the
3242                                    close button as they can only be closed programmatically.
3243 cls               String           A custom CSS class to apply to the message box element
3244 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
3245                                    displayed (defaults to 75)
3246 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
3247                                    function will be btn (the name of the button that was clicked, if applicable,
3248                                    e.g. "ok"), and text (the value of the active text field, if applicable).
3249                                    Progress and wait dialogs will ignore this option since they do not respond to
3250                                    user actions and can only be closed programmatically, so any required function
3251                                    should be called by the same code after it closes the dialog.
3252 icon              String           A CSS class that provides a background image to be used as an icon for
3253                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
3254 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
3255 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
3256 modal             Boolean          False to allow user interaction with the page while the message box is
3257                                    displayed (defaults to true)
3258 msg               String           A string that will replace the existing message box body text (defaults
3259                                    to the XHTML-compliant non-breaking space character '&#160;')
3260 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
3261 progress          Boolean          True to display a progress bar (defaults to false)
3262 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
3263 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
3264 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
3265 title             String           The title text
3266 value             String           The string value to set into the active textbox element if displayed
3267 wait              Boolean          True to display a progress bar (defaults to false)
3268 width             Number           The width of the dialog in pixels
3269 </pre>
3270          *
3271          * Example usage:
3272          * <pre><code>
3273 Roo.Msg.show({
3274    title: 'Address',
3275    msg: 'Please enter your address:',
3276    width: 300,
3277    buttons: Roo.MessageBox.OKCANCEL,
3278    multiline: true,
3279    fn: saveAddress,
3280    animEl: 'addAddressBtn'
3281 });
3282 </code></pre>
3283          * @param {Object} config Configuration options
3284          * @return {Roo.MessageBox} This message box
3285          */
3286         show : function(options)
3287         {
3288             
3289             // this causes nightmares if you show one dialog after another
3290             // especially on callbacks..
3291              
3292             if(this.isVisible()){
3293                 
3294                 this.hide();
3295                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
3296                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
3297                 Roo.log("New Dialog Message:" +  options.msg )
3298                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
3299                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
3300                 
3301             }
3302             var d = this.getDialog();
3303             opt = options;
3304             d.setTitle(opt.title || "&#160;");
3305             d.closeEl.setDisplayed(opt.closable !== false);
3306             activeTextEl = textboxEl;
3307             opt.prompt = opt.prompt || (opt.multiline ? true : false);
3308             if(opt.prompt){
3309                 if(opt.multiline){
3310                     textboxEl.hide();
3311                     textareaEl.show();
3312                     textareaEl.setHeight(typeof opt.multiline == "number" ?
3313                         opt.multiline : this.defaultTextHeight);
3314                     activeTextEl = textareaEl;
3315                 }else{
3316                     textboxEl.show();
3317                     textareaEl.hide();
3318                 }
3319             }else{
3320                 textboxEl.hide();
3321                 textareaEl.hide();
3322             }
3323             progressEl.setDisplayed(opt.progress === true);
3324             this.updateProgress(0);
3325             activeTextEl.dom.value = opt.value || "";
3326             if(opt.prompt){
3327                 dlg.setDefaultButton(activeTextEl);
3328             }else{
3329                 var bs = opt.buttons;
3330                 var db = null;
3331                 if(bs && bs.ok){
3332                     db = buttons["ok"];
3333                 }else if(bs && bs.yes){
3334                     db = buttons["yes"];
3335                 }
3336                 dlg.setDefaultButton(db);
3337             }
3338             bwidth = updateButtons(opt.buttons);
3339             this.updateText(opt.msg);
3340             if(opt.cls){
3341                 d.el.addClass(opt.cls);
3342             }
3343             d.proxyDrag = opt.proxyDrag === true;
3344             d.modal = opt.modal !== false;
3345             d.mask = opt.modal !== false ? mask : false;
3346             if(!d.isVisible()){
3347                 // force it to the end of the z-index stack so it gets a cursor in FF
3348                 document.body.appendChild(dlg.el.dom);
3349                 d.animateTarget = null;
3350                 d.show(options.animEl);
3351             }
3352             return this;
3353         },
3354
3355         /**
3356          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
3357          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
3358          * and closing the message box when the process is complete.
3359          * @param {String} title The title bar text
3360          * @param {String} msg The message box body text
3361          * @return {Roo.MessageBox} This message box
3362          */
3363         progress : function(title, msg){
3364             this.show({
3365                 title : title,
3366                 msg : msg,
3367                 buttons: false,
3368                 progress:true,
3369                 closable:false,
3370                 minWidth: this.minProgressWidth,
3371                 modal : true
3372             });
3373             return this;
3374         },
3375
3376         /**
3377          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
3378          * If a callback function is passed it will be called after the user clicks the button, and the
3379          * id of the button that was clicked will be passed as the only parameter to the callback
3380          * (could also be the top-right close button).
3381          * @param {String} title The title bar text
3382          * @param {String} msg The message box body text
3383          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3384          * @param {Object} scope (optional) The scope of the callback function
3385          * @return {Roo.MessageBox} This message box
3386          */
3387         alert : function(title, msg, fn, scope)
3388         {
3389             this.show({
3390                 title : title,
3391                 msg : msg,
3392                 buttons: this.OK,
3393                 fn: fn,
3394                 closable : false,
3395                 scope : scope,
3396                 modal : true
3397             });
3398             return this;
3399         },
3400
3401         /**
3402          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
3403          * interaction while waiting for a long-running process to complete that does not have defined intervals.
3404          * You are responsible for closing the message box when the process is complete.
3405          * @param {String} msg The message box body text
3406          * @param {String} title (optional) The title bar text
3407          * @return {Roo.MessageBox} This message box
3408          */
3409         wait : function(msg, title){
3410             this.show({
3411                 title : title,
3412                 msg : msg,
3413                 buttons: false,
3414                 closable:false,
3415                 progress:true,
3416                 modal:true,
3417                 width:300,
3418                 wait:true
3419             });
3420             waitTimer = Roo.TaskMgr.start({
3421                 run: function(i){
3422                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
3423                 },
3424                 interval: 1000
3425             });
3426             return this;
3427         },
3428
3429         /**
3430          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
3431          * If a callback function is passed it will be called after the user clicks either button, and the id of the
3432          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
3433          * @param {String} title The title bar text
3434          * @param {String} msg The message box body text
3435          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3436          * @param {Object} scope (optional) The scope of the callback function
3437          * @return {Roo.MessageBox} This message box
3438          */
3439         confirm : function(title, msg, fn, scope){
3440             this.show({
3441                 title : title,
3442                 msg : msg,
3443                 buttons: this.YESNO,
3444                 fn: fn,
3445                 scope : scope,
3446                 modal : true
3447             });
3448             return this;
3449         },
3450
3451         /**
3452          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
3453          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
3454          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
3455          * (could also be the top-right close button) and the text that was entered will be passed as the two
3456          * parameters to the callback.
3457          * @param {String} title The title bar text
3458          * @param {String} msg The message box body text
3459          * @param {Function} fn (optional) The callback function invoked after the message box is closed
3460          * @param {Object} scope (optional) The scope of the callback function
3461          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
3462          * property, or the height in pixels to create the textbox (defaults to false / single-line)
3463          * @return {Roo.MessageBox} This message box
3464          */
3465         prompt : function(title, msg, fn, scope, multiline){
3466             this.show({
3467                 title : title,
3468                 msg : msg,
3469                 buttons: this.OKCANCEL,
3470                 fn: fn,
3471                 minWidth:250,
3472                 scope : scope,
3473                 prompt:true,
3474                 multiline: multiline,
3475                 modal : true
3476             });
3477             return this;
3478         },
3479
3480         /**
3481          * Button config that displays a single OK button
3482          * @type Object
3483          */
3484         OK : {ok:true},
3485         /**
3486          * Button config that displays Yes and No buttons
3487          * @type Object
3488          */
3489         YESNO : {yes:true, no:true},
3490         /**
3491          * Button config that displays OK and Cancel buttons
3492          * @type Object
3493          */
3494         OKCANCEL : {ok:true, cancel:true},
3495         /**
3496          * Button config that displays Yes, No and Cancel buttons
3497          * @type Object
3498          */
3499         YESNOCANCEL : {yes:true, no:true, cancel:true},
3500
3501         /**
3502          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
3503          * @type Number
3504          */
3505         defaultTextHeight : 75,
3506         /**
3507          * The maximum width in pixels of the message box (defaults to 600)
3508          * @type Number
3509          */
3510         maxWidth : 600,
3511         /**
3512          * The minimum width in pixels of the message box (defaults to 100)
3513          * @type Number
3514          */
3515         minWidth : 100,
3516         /**
3517          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
3518          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
3519          * @type Number
3520          */
3521         minProgressWidth : 250,
3522         /**
3523          * An object containing the default button text strings that can be overriden for localized language support.
3524          * Supported properties are: ok, cancel, yes and no.
3525          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
3526          * @type Object
3527          */
3528         buttonText : {
3529             ok : "OK",
3530             cancel : "Cancel",
3531             yes : "Yes",
3532             no : "No"
3533         }
3534     };
3535 }();
3536
3537 /**
3538  * Shorthand for {@link Roo.MessageBox}
3539  */
3540 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
3541 Roo.Msg = Roo.Msg || Roo.MessageBox;
3542 /*
3543  * - LGPL
3544  *
3545  * navbar
3546  * 
3547  */
3548
3549 /**
3550  * @class Roo.bootstrap.Navbar
3551  * @extends Roo.bootstrap.Component
3552  * Bootstrap Navbar class
3553
3554  * @constructor
3555  * Create a new Navbar
3556  * @param {Object} config The config object
3557  */
3558
3559
3560 Roo.bootstrap.Navbar = function(config){
3561     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
3562     this.addEvents({
3563         // raw events
3564         /**
3565          * @event beforetoggle
3566          * Fire before toggle the menu
3567          * @param {Roo.EventObject} e
3568          */
3569         "beforetoggle" : true
3570     });
3571 };
3572
3573 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
3574     
3575     
3576    
3577     // private
3578     navItems : false,
3579     loadMask : false,
3580     
3581     
3582     getAutoCreate : function(){
3583         
3584         
3585         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
3586         
3587     },
3588     
3589     initEvents :function ()
3590     {
3591         //Roo.log(this.el.select('.navbar-toggle',true));
3592         this.el.select('.navbar-toggle',true).on('click', function() {
3593             if(this.fireEvent('beforetoggle', this) !== false){
3594                this.el.select('.navbar-collapse',true).toggleClass('in');                                 
3595             }
3596             
3597         }, this);
3598         
3599         var mark = {
3600             tag: "div",
3601             cls:"x-dlg-mask"
3602         };
3603         
3604         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
3605         
3606         var size = this.el.getSize();
3607         this.maskEl.setSize(size.width, size.height);
3608         this.maskEl.enableDisplayMode("block");
3609         this.maskEl.hide();
3610         
3611         if(this.loadMask){
3612             this.maskEl.show();
3613         }
3614     },
3615     
3616     
3617     getChildContainer : function()
3618     {
3619         if (this.el.select('.collapse').getCount()) {
3620             return this.el.select('.collapse',true).first();
3621         }
3622         
3623         return this.el;
3624     },
3625     
3626     mask : function()
3627     {
3628         this.maskEl.show();
3629     },
3630     
3631     unmask : function()
3632     {
3633         this.maskEl.hide();
3634     } 
3635     
3636     
3637     
3638     
3639 });
3640
3641
3642
3643  
3644
3645  /*
3646  * - LGPL
3647  *
3648  * navbar
3649  * 
3650  */
3651
3652 /**
3653  * @class Roo.bootstrap.NavSimplebar
3654  * @extends Roo.bootstrap.Navbar
3655  * Bootstrap Sidebar class
3656  *
3657  * @cfg {Boolean} inverse is inverted color
3658  * 
3659  * @cfg {String} type (nav | pills | tabs)
3660  * @cfg {Boolean} arrangement stacked | justified
3661  * @cfg {String} align (left | right) alignment
3662  * 
3663  * @cfg {Boolean} main (true|false) main nav bar? default false
3664  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
3665  * 
3666  * @cfg {String} tag (header|footer|nav|div) default is nav 
3667
3668  * 
3669  * 
3670  * 
3671  * @constructor
3672  * Create a new Sidebar
3673  * @param {Object} config The config object
3674  */
3675
3676
3677 Roo.bootstrap.NavSimplebar = function(config){
3678     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
3679 };
3680
3681 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
3682     
3683     inverse: false,
3684     
3685     type: false,
3686     arrangement: '',
3687     align : false,
3688     
3689     
3690     
3691     main : false,
3692     
3693     
3694     tag : false,
3695     
3696     
3697     getAutoCreate : function(){
3698         
3699         
3700         var cfg = {
3701             tag : this.tag || 'div',
3702             cls : 'navbar'
3703         };
3704           
3705         
3706         cfg.cn = [
3707             {
3708                 cls: 'nav',
3709                 tag : 'ul'
3710             }
3711         ];
3712         
3713          
3714         this.type = this.type || 'nav';
3715         if (['tabs','pills'].indexOf(this.type)!==-1) {
3716             cfg.cn[0].cls += ' nav-' + this.type
3717         
3718         
3719         } else {
3720             if (this.type!=='nav') {
3721                 Roo.log('nav type must be nav/tabs/pills')
3722             }
3723             cfg.cn[0].cls += ' navbar-nav'
3724         }
3725         
3726         
3727         
3728         
3729         if (['stacked','justified'].indexOf(this.arrangement)!==-1) {
3730             cfg.cn[0].cls += ' nav-' + this.arrangement;
3731         }
3732         
3733         
3734         if (this.align === 'right') {
3735             cfg.cn[0].cls += ' navbar-right';
3736         }
3737         
3738         if (this.inverse) {
3739             cfg.cls += ' navbar-inverse';
3740             
3741         }
3742         
3743         
3744         return cfg;
3745     
3746         
3747     }
3748     
3749     
3750     
3751 });
3752
3753
3754
3755  
3756
3757  
3758        /*
3759  * - LGPL
3760  *
3761  * navbar
3762  * 
3763  */
3764
3765 /**
3766  * @class Roo.bootstrap.NavHeaderbar
3767  * @extends Roo.bootstrap.NavSimplebar
3768  * Bootstrap Sidebar class
3769  *
3770  * @cfg {String} brand what is brand
3771  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
3772  * @cfg {String} brand_href href of the brand
3773  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
3774  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
3775  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
3776  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
3777  * 
3778  * @constructor
3779  * Create a new Sidebar
3780  * @param {Object} config The config object
3781  */
3782
3783
3784 Roo.bootstrap.NavHeaderbar = function(config){
3785     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
3786       
3787 };
3788
3789 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
3790     
3791     position: '',
3792     brand: '',
3793     brand_href: false,
3794     srButton : true,
3795     autohide : false,
3796     desktopCenter : false,
3797    
3798     
3799     getAutoCreate : function(){
3800         
3801         var   cfg = {
3802             tag: this.nav || 'nav',
3803             cls: 'navbar',
3804             role: 'navigation',
3805             cn: []
3806         };
3807         
3808         var cn = cfg.cn;
3809         if (this.desktopCenter) {
3810             cn.push({cls : 'container', cn : []});
3811             cn = cn[0].cn;
3812         }
3813         
3814         if(this.srButton){
3815             cn.push({
3816                 tag: 'div',
3817                 cls: 'navbar-header',
3818                 cn: [
3819                     {
3820                         tag: 'button',
3821                         type: 'button',
3822                         cls: 'navbar-toggle',
3823                         'data-toggle': 'collapse',
3824                         cn: [
3825                             {
3826                                 tag: 'span',
3827                                 cls: 'sr-only',
3828                                 html: 'Toggle navigation'
3829                             },
3830                             {
3831                                 tag: 'span',
3832                                 cls: 'icon-bar'
3833                             },
3834                             {
3835                                 tag: 'span',
3836                                 cls: 'icon-bar'
3837                             },
3838                             {
3839                                 tag: 'span',
3840                                 cls: 'icon-bar'
3841                             }
3842                         ]
3843                     }
3844                 ]
3845             });
3846         }
3847         
3848         cn.push({
3849             tag: 'div',
3850             cls: 'collapse navbar-collapse',
3851             cn : []
3852         });
3853         
3854         cfg.cls += this.inverse ? ' navbar-inverse' : ' navbar-default';
3855         
3856         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
3857             cfg.cls += ' navbar-' + this.position;
3858             
3859             // tag can override this..
3860             
3861             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
3862         }
3863         
3864         if (this.brand !== '') {
3865             cn[0].cn.push({
3866                 tag: 'a',
3867                 href: this.brand_href ? this.brand_href : '#',
3868                 cls: 'navbar-brand',
3869                 cn: [
3870                 this.brand
3871                 ]
3872             });
3873         }
3874         
3875         if(this.main){
3876             cfg.cls += ' main-nav';
3877         }
3878         
3879         
3880         return cfg;
3881
3882         
3883     },
3884     getHeaderChildContainer : function()
3885     {
3886         if (this.srButton && this.el.select('.navbar-header').getCount()) {
3887             return this.el.select('.navbar-header',true).first();
3888         }
3889         
3890         return this.getChildContainer();
3891     },
3892     
3893     
3894     initEvents : function()
3895     {
3896         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
3897         
3898         if (this.autohide) {
3899             
3900             var prevScroll = 0;
3901             var ft = this.el;
3902             
3903             Roo.get(document).on('scroll',function(e) {
3904                 var ns = Roo.get(document).getScroll().top;
3905                 var os = prevScroll;
3906                 prevScroll = ns;
3907                 
3908                 if(ns > os){
3909                     ft.removeClass('slideDown');
3910                     ft.addClass('slideUp');
3911                     return;
3912                 }
3913                 ft.removeClass('slideUp');
3914                 ft.addClass('slideDown');
3915                  
3916               
3917           },this);
3918         }
3919     }    
3920     
3921 });
3922
3923
3924
3925  
3926
3927  /*
3928  * - LGPL
3929  *
3930  * navbar
3931  * 
3932  */
3933
3934 /**
3935  * @class Roo.bootstrap.NavSidebar
3936  * @extends Roo.bootstrap.Navbar
3937  * Bootstrap Sidebar class
3938  * 
3939  * @constructor
3940  * Create a new Sidebar
3941  * @param {Object} config The config object
3942  */
3943
3944
3945 Roo.bootstrap.NavSidebar = function(config){
3946     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
3947 };
3948
3949 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
3950     
3951     sidebar : true, // used by Navbar Item and NavbarGroup at present...
3952     
3953     getAutoCreate : function(){
3954         
3955         
3956         return  {
3957             tag: 'div',
3958             cls: 'sidebar sidebar-nav'
3959         };
3960     
3961         
3962     }
3963     
3964     
3965     
3966 });
3967
3968
3969
3970  
3971
3972  /*
3973  * - LGPL
3974  *
3975  * nav group
3976  * 
3977  */
3978
3979 /**
3980  * @class Roo.bootstrap.NavGroup
3981  * @extends Roo.bootstrap.Component
3982  * Bootstrap NavGroup class
3983  * @cfg {String} align (left|right)
3984  * @cfg {Boolean} inverse
3985  * @cfg {String} type (nav|pills|tab) default nav
3986  * @cfg {String} navId - reference Id for navbar.
3987
3988  * 
3989  * @constructor
3990  * Create a new nav group
3991  * @param {Object} config The config object
3992  */
3993
3994 Roo.bootstrap.NavGroup = function(config){
3995     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
3996     this.navItems = [];
3997    
3998     Roo.bootstrap.NavGroup.register(this);
3999      this.addEvents({
4000         /**
4001              * @event changed
4002              * Fires when the active item changes
4003              * @param {Roo.bootstrap.NavGroup} this
4004              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
4005              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
4006          */
4007         'changed': true
4008      });
4009     
4010 };
4011
4012 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
4013     
4014     align: '',
4015     inverse: false,
4016     form: false,
4017     type: 'nav',
4018     navId : '',
4019     // private
4020     
4021     navItems : false, 
4022     
4023     getAutoCreate : function()
4024     {
4025         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
4026         
4027         cfg = {
4028             tag : 'ul',
4029             cls: 'nav' 
4030         };
4031         
4032         if (['tabs','pills'].indexOf(this.type)!==-1) {
4033             cfg.cls += ' nav-' + this.type
4034         } else {
4035             if (this.type!=='nav') {
4036                 Roo.log('nav type must be nav/tabs/pills')
4037             }
4038             cfg.cls += ' navbar-nav'
4039         }
4040         
4041         if (this.parent() && this.parent().sidebar) {
4042             cfg = {
4043                 tag: 'ul',
4044                 cls: 'dashboard-menu sidebar-menu'
4045             };
4046             
4047             return cfg;
4048         }
4049         
4050         if (this.form === true) {
4051             cfg = {
4052                 tag: 'form',
4053                 cls: 'navbar-form'
4054             };
4055             
4056             if (this.align === 'right') {
4057                 cfg.cls += ' navbar-right';
4058             } else {
4059                 cfg.cls += ' navbar-left';
4060             }
4061         }
4062         
4063         if (this.align === 'right') {
4064             cfg.cls += ' navbar-right';
4065         }
4066         
4067         if (this.inverse) {
4068             cfg.cls += ' navbar-inverse';
4069             
4070         }
4071         
4072         
4073         return cfg;
4074     },
4075     /**
4076     * sets the active Navigation item
4077     * @param {Roo.bootstrap.NavItem} the new current navitem
4078     */
4079     setActiveItem : function(item)
4080     {
4081         var prev = false;
4082         Roo.each(this.navItems, function(v){
4083             if (v == item) {
4084                 return ;
4085             }
4086             if (v.isActive()) {
4087                 v.setActive(false, true);
4088                 prev = v;
4089                 
4090             }
4091             
4092         });
4093
4094         item.setActive(true, true);
4095         this.fireEvent('changed', this, item, prev);
4096         
4097         
4098     },
4099     /**
4100     * gets the active Navigation item
4101     * @return {Roo.bootstrap.NavItem} the current navitem
4102     */
4103     getActive : function()
4104     {
4105         
4106         var prev = false;
4107         Roo.each(this.navItems, function(v){
4108             
4109             if (v.isActive()) {
4110                 prev = v;
4111                 
4112             }
4113             
4114         });
4115         return prev;
4116     },
4117     
4118     indexOfNav : function()
4119     {
4120         
4121         var prev = false;
4122         Roo.each(this.navItems, function(v,i){
4123             
4124             if (v.isActive()) {
4125                 prev = i;
4126                 
4127             }
4128             
4129         });
4130         return prev;
4131     },
4132     /**
4133     * adds a Navigation item
4134     * @param {Roo.bootstrap.NavItem} the navitem to add
4135     */
4136     addItem : function(cfg)
4137     {
4138         var cn = new Roo.bootstrap.NavItem(cfg);
4139         this.register(cn);
4140         cn.parentId = this.id;
4141         cn.onRender(this.el, null);
4142         return cn;
4143     },
4144     /**
4145     * register a Navigation item
4146     * @param {Roo.bootstrap.NavItem} the navitem to add
4147     */
4148     register : function(item)
4149     {
4150         this.navItems.push( item);
4151         item.navId = this.navId;
4152     
4153     },
4154     
4155     /**
4156     * clear all the Navigation item
4157     */
4158    
4159     clearAll : function()
4160     {
4161         this.navItems = [];
4162         this.el.dom.innerHTML = '';
4163     },
4164     
4165     getNavItem: function(tabId)
4166     {
4167         var ret = false;
4168         Roo.each(this.navItems, function(e) {
4169             if (e.tabId == tabId) {
4170                ret =  e;
4171                return false;
4172             }
4173             return true;
4174             
4175         });
4176         return ret;
4177     },
4178     
4179     setActiveNext : function()
4180     {
4181         var i = this.indexOfNav(this.getActive());
4182         if (i > this.navItems.length) {
4183             return;
4184         }
4185         this.setActiveItem(this.navItems[i+1]);
4186     },
4187     setActivePrev : function()
4188     {
4189         var i = this.indexOfNav(this.getActive());
4190         if (i  < 1) {
4191             return;
4192         }
4193         this.setActiveItem(this.navItems[i-1]);
4194     },
4195     clearWasActive : function(except) {
4196         Roo.each(this.navItems, function(e) {
4197             if (e.tabId != except.tabId && e.was_active) {
4198                e.was_active = false;
4199                return false;
4200             }
4201             return true;
4202             
4203         });
4204     },
4205     getWasActive : function ()
4206     {
4207         var r = false;
4208         Roo.each(this.navItems, function(e) {
4209             if (e.was_active) {
4210                r = e;
4211                return false;
4212             }
4213             return true;
4214             
4215         });
4216         return r;
4217     }
4218     
4219     
4220 });
4221
4222  
4223 Roo.apply(Roo.bootstrap.NavGroup, {
4224     
4225     groups: {},
4226      /**
4227     * register a Navigation Group
4228     * @param {Roo.bootstrap.NavGroup} the navgroup to add
4229     */
4230     register : function(navgrp)
4231     {
4232         this.groups[navgrp.navId] = navgrp;
4233         
4234     },
4235     /**
4236     * fetch a Navigation Group based on the navigation ID
4237     * @param {string} the navgroup to add
4238     * @returns {Roo.bootstrap.NavGroup} the navgroup 
4239     */
4240     get: function(navId) {
4241         if (typeof(this.groups[navId]) == 'undefined') {
4242             return false;
4243             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
4244         }
4245         return this.groups[navId] ;
4246     }
4247     
4248     
4249     
4250 });
4251
4252  /*
4253  * - LGPL
4254  *
4255  * row
4256  * 
4257  */
4258
4259 /**
4260  * @class Roo.bootstrap.NavItem
4261  * @extends Roo.bootstrap.Component
4262  * Bootstrap Navbar.NavItem class
4263  * @cfg {String} href  link to
4264  * @cfg {String} html content of button
4265  * @cfg {String} badge text inside badge
4266  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
4267  * @cfg {String} glyphicon name of glyphicon
4268  * @cfg {String} icon name of font awesome icon
4269  * @cfg {Boolean} active Is item active
4270  * @cfg {Boolean} disabled Is item disabled
4271  
4272  * @cfg {Boolean} preventDefault (true | false) default false
4273  * @cfg {String} tabId the tab that this item activates.
4274  * @cfg {String} tagtype (a|span) render as a href or span?
4275  * @cfg {Boolean} animateRef (true|false) link to element default false  
4276   
4277  * @constructor
4278  * Create a new Navbar Item
4279  * @param {Object} config The config object
4280  */
4281 Roo.bootstrap.NavItem = function(config){
4282     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
4283     this.addEvents({
4284         // raw events
4285         /**
4286          * @event click
4287          * The raw click event for the entire grid.
4288          * @param {Roo.EventObject} e
4289          */
4290         "click" : true,
4291          /**
4292             * @event changed
4293             * Fires when the active item active state changes
4294             * @param {Roo.bootstrap.NavItem} this
4295             * @param {boolean} state the new state
4296              
4297          */
4298         'changed': true,
4299         /**
4300             * @event scrollto
4301             * Fires when scroll to element
4302             * @param {Roo.bootstrap.NavItem} this
4303             * @param {Object} options
4304             * @param {Roo.EventObject} e
4305              
4306          */
4307         'scrollto': true
4308     });
4309    
4310 };
4311
4312 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
4313     
4314     href: false,
4315     html: '',
4316     badge: '',
4317     icon: false,
4318     glyphicon: false,
4319     active: false,
4320     preventDefault : false,
4321     tabId : false,
4322     tagtype : 'a',
4323     disabled : false,
4324     animateRef : false,
4325     was_active : false,
4326     
4327     getAutoCreate : function(){
4328          
4329         var cfg = {
4330             tag: 'li',
4331             cls: 'nav-item'
4332             
4333         };
4334         
4335         if (this.active) {
4336             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
4337         }
4338         if (this.disabled) {
4339             cfg.cls += ' disabled';
4340         }
4341         
4342         if (this.href || this.html || this.glyphicon || this.icon) {
4343             cfg.cn = [
4344                 {
4345                     tag: this.tagtype,
4346                     href : this.href || "#",
4347                     html: this.html || ''
4348                 }
4349             ];
4350             
4351             if (this.icon) {
4352                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>'
4353             }
4354
4355             if(this.glyphicon) {
4356                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
4357             }
4358             
4359             if (this.menu) {
4360                 
4361                 cfg.cn[0].html += " <span class='caret'></span>";
4362              
4363             }
4364             
4365             if (this.badge !== '') {
4366                  
4367                 cfg.cn[0].html += ' <span class="badge">' + this.badge + '</span>';
4368             }
4369         }
4370         
4371         
4372         
4373         return cfg;
4374     },
4375     initEvents: function() 
4376     {
4377         if (typeof (this.menu) != 'undefined') {
4378             this.menu.parentType = this.xtype;
4379             this.menu.triggerEl = this.el;
4380             this.menu = this.addxtype(Roo.apply({}, this.menu));
4381         }
4382         
4383         this.el.select('a',true).on('click', this.onClick, this);
4384         
4385         if(this.tagtype == 'span'){
4386             this.el.select('span',true).on('click', this.onClick, this);
4387         }
4388        
4389         // at this point parent should be available..
4390         this.parent().register(this);
4391     },
4392     
4393     onClick : function(e)
4394     {
4395         if (e.getTarget('.dropdown-menu-item')) {
4396             // did you click on a menu itemm.... - then don't trigger onclick..
4397             return;
4398         }
4399         
4400         if(
4401                 this.preventDefault || 
4402                 this.href == '#' 
4403         ){
4404             Roo.log("NavItem - prevent Default?");
4405             e.preventDefault();
4406         }
4407         
4408         if (this.disabled) {
4409             return;
4410         }
4411         
4412         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4413         if (tg && tg.transition) {
4414             Roo.log("waiting for the transitionend");
4415             return;
4416         }
4417         
4418         
4419         
4420         //Roo.log("fire event clicked");
4421         if(this.fireEvent('click', this, e) === false){
4422             return;
4423         };
4424         
4425         if(this.tagtype == 'span'){
4426             return;
4427         }
4428         
4429         //Roo.log(this.href);
4430         var ael = this.el.select('a',true).first();
4431         //Roo.log(ael);
4432         
4433         if(ael && this.animateRef && this.href.indexOf('#') > -1){
4434             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
4435             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
4436                 return; // ignore... - it's a 'hash' to another page.
4437             }
4438             Roo.log("NavItem - prevent Default?");
4439             e.preventDefault();
4440             this.scrollToElement(e);
4441         }
4442         
4443         
4444         var p =  this.parent();
4445    
4446         if (['tabs','pills'].indexOf(p.type)!==-1) {
4447             if (typeof(p.setActiveItem) !== 'undefined') {
4448                 p.setActiveItem(this);
4449             }
4450         }
4451         
4452         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
4453         if (p.parentType == 'NavHeaderbar' && !this.menu) {
4454             // remove the collapsed menu expand...
4455             p.parent().el.select('.navbar-collapse',true).removeClass('in');  
4456         }
4457     },
4458     
4459     isActive: function () {
4460         return this.active
4461     },
4462     setActive : function(state, fire, is_was_active)
4463     {
4464         if (this.active && !state && this.navId) {
4465             this.was_active = true;
4466             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4467             if (nv) {
4468                 nv.clearWasActive(this);
4469             }
4470             
4471         }
4472         this.active = state;
4473         
4474         if (!state ) {
4475             this.el.removeClass('active');
4476         } else if (!this.el.hasClass('active')) {
4477             this.el.addClass('active');
4478         }
4479         if (fire) {
4480             this.fireEvent('changed', this, state);
4481         }
4482         
4483         // show a panel if it's registered and related..
4484         
4485         if (!this.navId || !this.tabId || !state || is_was_active) {
4486             return;
4487         }
4488         
4489         var tg = Roo.bootstrap.TabGroup.get(this.navId);
4490         if (!tg) {
4491             return;
4492         }
4493         var pan = tg.getPanelByName(this.tabId);
4494         if (!pan) {
4495             return;
4496         }
4497         // if we can not flip to new panel - go back to old nav highlight..
4498         if (false == tg.showPanel(pan)) {
4499             var nv = Roo.bootstrap.NavGroup.get(this.navId);
4500             if (nv) {
4501                 var onav = nv.getWasActive();
4502                 if (onav) {
4503                     onav.setActive(true, false, true);
4504                 }
4505             }
4506             
4507         }
4508         
4509         
4510         
4511     },
4512      // this should not be here...
4513     setDisabled : function(state)
4514     {
4515         this.disabled = state;
4516         if (!state ) {
4517             this.el.removeClass('disabled');
4518         } else if (!this.el.hasClass('disabled')) {
4519             this.el.addClass('disabled');
4520         }
4521         
4522     },
4523     
4524     /**
4525      * Fetch the element to display the tooltip on.
4526      * @return {Roo.Element} defaults to this.el
4527      */
4528     tooltipEl : function()
4529     {
4530         return this.el.select('' + this.tagtype + '', true).first();
4531     },
4532     
4533     scrollToElement : function(e)
4534     {
4535         var c = document.body;
4536         
4537         /*
4538          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
4539          */
4540         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
4541             c = document.documentElement;
4542         }
4543         
4544         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
4545         
4546         if(!target){
4547             return;
4548         }
4549
4550         var o = target.calcOffsetsTo(c);
4551         
4552         var options = {
4553             target : target,
4554             value : o[1]
4555         };
4556         
4557         this.fireEvent('scrollto', this, options, e);
4558         
4559         Roo.get(c).scrollTo('top', options.value, true);
4560         
4561         return;
4562     }
4563 });
4564  
4565
4566  /*
4567  * - LGPL
4568  *
4569  * sidebar item
4570  *
4571  *  li
4572  *    <span> icon </span>
4573  *    <span> text </span>
4574  *    <span>badge </span>
4575  */
4576
4577 /**
4578  * @class Roo.bootstrap.NavSidebarItem
4579  * @extends Roo.bootstrap.NavItem
4580  * Bootstrap Navbar.NavSidebarItem class
4581  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
4582  * {Boolean} open is the menu open
4583  * {Boolean} buttonView use button as the tigger el rather that a (default false)
4584  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
4585  * {String} buttonSize (sm|md|lg)the extra classes for the button
4586  * {Boolean} showArrow show arrow next to the text (default true)
4587  * @constructor
4588  * Create a new Navbar Button
4589  * @param {Object} config The config object
4590  */
4591 Roo.bootstrap.NavSidebarItem = function(config){
4592     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
4593     this.addEvents({
4594         // raw events
4595         /**
4596          * @event click
4597          * The raw click event for the entire grid.
4598          * @param {Roo.EventObject} e
4599          */
4600         "click" : true,
4601          /**
4602             * @event changed
4603             * Fires when the active item active state changes
4604             * @param {Roo.bootstrap.NavSidebarItem} this
4605             * @param {boolean} state the new state
4606              
4607          */
4608         'changed': true
4609     });
4610    
4611 };
4612
4613 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
4614     
4615     badgeWeight : 'default',
4616     
4617     open: false,
4618     
4619     buttonView : false,
4620     
4621     buttonWeight : 'default',
4622     
4623     buttonSize : 'md',
4624     
4625     showArrow : true,
4626     
4627     getAutoCreate : function(){
4628         
4629         
4630         var a = {
4631                 tag: 'a',
4632                 href : this.href || '#',
4633                 cls: '',
4634                 html : '',
4635                 cn : []
4636         };
4637         
4638         if(this.buttonView){
4639             a = {
4640                 tag: 'button',
4641                 href : this.href || '#',
4642                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
4643                 html : this.html,
4644                 cn : []
4645             };
4646         }
4647         
4648         var cfg = {
4649             tag: 'li',
4650             cls: '',
4651             cn: [ a ]
4652         };
4653         
4654         if (this.active) {
4655             cfg.cls += ' active';
4656         }
4657         
4658         if (this.disabled) {
4659             cfg.cls += ' disabled';
4660         }
4661         if (this.open) {
4662             cfg.cls += ' open x-open';
4663         }
4664         // left icon..
4665         if (this.glyphicon || this.icon) {
4666             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
4667             a.cn.push({ tag : 'i', cls : c }) ;
4668         }
4669         
4670         if(!this.buttonView){
4671             var span = {
4672                 tag: 'span',
4673                 html : this.html || ''
4674             };
4675
4676             a.cn.push(span);
4677             
4678         }
4679         
4680         if (this.badge !== '') {
4681             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
4682         }
4683         
4684         if (this.menu) {
4685             
4686             if(this.showArrow){
4687                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
4688             }
4689             
4690             a.cls += ' dropdown-toggle treeview' ;
4691         }
4692         
4693         return cfg;
4694     },
4695     
4696     initEvents : function()
4697     { 
4698         if (typeof (this.menu) != 'undefined') {
4699             this.menu.parentType = this.xtype;
4700             this.menu.triggerEl = this.el;
4701             this.menu = this.addxtype(Roo.apply({}, this.menu));
4702         }
4703         
4704         this.el.on('click', this.onClick, this);
4705         
4706         if(this.badge !== ''){
4707             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
4708         }
4709         
4710     },
4711     
4712     onClick : function(e)
4713     {
4714         if(this.disabled){
4715             e.preventDefault();
4716             return;
4717         }
4718         
4719         if(this.preventDefault){
4720             e.preventDefault();
4721         }
4722         
4723         this.fireEvent('click', this);
4724     },
4725     
4726     disable : function()
4727     {
4728         this.setDisabled(true);
4729     },
4730     
4731     enable : function()
4732     {
4733         this.setDisabled(false);
4734     },
4735     
4736     setDisabled : function(state)
4737     {
4738         if(this.disabled == state){
4739             return;
4740         }
4741         
4742         this.disabled = state;
4743         
4744         if (state) {
4745             this.el.addClass('disabled');
4746             return;
4747         }
4748         
4749         this.el.removeClass('disabled');
4750         
4751         return;
4752     },
4753     
4754     setActive : function(state)
4755     {
4756         if(this.active == state){
4757             return;
4758         }
4759         
4760         this.active = state;
4761         
4762         if (state) {
4763             this.el.addClass('active');
4764             return;
4765         }
4766         
4767         this.el.removeClass('active');
4768         
4769         return;
4770     },
4771     
4772     isActive: function () 
4773     {
4774         return this.active;
4775     },
4776     
4777     setBadge : function(str)
4778     {
4779         if(!this.badgeEl){
4780             return;
4781         }
4782         
4783         this.badgeEl.dom.innerHTML = str;
4784     }
4785     
4786    
4787      
4788  
4789 });
4790  
4791
4792  /*
4793  * - LGPL
4794  *
4795  * row
4796  * 
4797  */
4798
4799 /**
4800  * @class Roo.bootstrap.Row
4801  * @extends Roo.bootstrap.Component
4802  * Bootstrap Row class (contains columns...)
4803  * 
4804  * @constructor
4805  * Create a new Row
4806  * @param {Object} config The config object
4807  */
4808
4809 Roo.bootstrap.Row = function(config){
4810     Roo.bootstrap.Row.superclass.constructor.call(this, config);
4811 };
4812
4813 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
4814     
4815     getAutoCreate : function(){
4816        return {
4817             cls: 'row clearfix'
4818        };
4819     }
4820     
4821     
4822 });
4823
4824  
4825
4826  /*
4827  * - LGPL
4828  *
4829  * element
4830  * 
4831  */
4832
4833 /**
4834  * @class Roo.bootstrap.Element
4835  * @extends Roo.bootstrap.Component
4836  * Bootstrap Element class
4837  * @cfg {String} html contents of the element
4838  * @cfg {String} tag tag of the element
4839  * @cfg {String} cls class of the element
4840  * @cfg {Boolean} preventDefault (true|false) default false
4841  * @cfg {Boolean} clickable (true|false) default false
4842  * 
4843  * @constructor
4844  * Create a new Element
4845  * @param {Object} config The config object
4846  */
4847
4848 Roo.bootstrap.Element = function(config){
4849     Roo.bootstrap.Element.superclass.constructor.call(this, config);
4850     
4851     this.addEvents({
4852         // raw events
4853         /**
4854          * @event click
4855          * When a element is chick
4856          * @param {Roo.bootstrap.Element} this
4857          * @param {Roo.EventObject} e
4858          */
4859         "click" : true
4860     });
4861 };
4862
4863 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
4864     
4865     tag: 'div',
4866     cls: '',
4867     html: '',
4868     preventDefault: false, 
4869     clickable: false,
4870     
4871     getAutoCreate : function(){
4872         
4873         var cfg = {
4874             tag: this.tag,
4875             cls: this.cls,
4876             html: this.html
4877         };
4878         
4879         return cfg;
4880     },
4881     
4882     initEvents: function() 
4883     {
4884         Roo.bootstrap.Element.superclass.initEvents.call(this);
4885         
4886         if(this.clickable){
4887             this.el.on('click', this.onClick, this);
4888         }
4889         
4890     },
4891     
4892     onClick : function(e)
4893     {
4894         if(this.preventDefault){
4895             e.preventDefault();
4896         }
4897         
4898         this.fireEvent('click', this, e);
4899     },
4900     
4901     getValue : function()
4902     {
4903         return this.el.dom.innerHTML;
4904     },
4905     
4906     setValue : function(value)
4907     {
4908         this.el.dom.innerHTML = value;
4909     }
4910    
4911 });
4912
4913  
4914
4915  /*
4916  * - LGPL
4917  *
4918  * pagination
4919  * 
4920  */
4921
4922 /**
4923  * @class Roo.bootstrap.Pagination
4924  * @extends Roo.bootstrap.Component
4925  * Bootstrap Pagination class
4926  * @cfg {String} size xs | sm | md | lg
4927  * @cfg {Boolean} inverse false | true
4928  * 
4929  * @constructor
4930  * Create a new Pagination
4931  * @param {Object} config The config object
4932  */
4933
4934 Roo.bootstrap.Pagination = function(config){
4935     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
4936 };
4937
4938 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
4939     
4940     cls: false,
4941     size: false,
4942     inverse: false,
4943     
4944     getAutoCreate : function(){
4945         var cfg = {
4946             tag: 'ul',
4947                 cls: 'pagination'
4948         };
4949         if (this.inverse) {
4950             cfg.cls += ' inverse';
4951         }
4952         if (this.html) {
4953             cfg.html=this.html;
4954         }
4955         if (this.cls) {
4956             cfg.cls += " " + this.cls;
4957         }
4958         return cfg;
4959     }
4960    
4961 });
4962
4963  
4964
4965  /*
4966  * - LGPL
4967  *
4968  * Pagination item
4969  * 
4970  */
4971
4972
4973 /**
4974  * @class Roo.bootstrap.PaginationItem
4975  * @extends Roo.bootstrap.Component
4976  * Bootstrap PaginationItem class
4977  * @cfg {String} html text
4978  * @cfg {String} href the link
4979  * @cfg {Boolean} preventDefault (true | false) default true
4980  * @cfg {Boolean} active (true | false) default false
4981  * @cfg {Boolean} disabled default false
4982  * 
4983  * 
4984  * @constructor
4985  * Create a new PaginationItem
4986  * @param {Object} config The config object
4987  */
4988
4989
4990 Roo.bootstrap.PaginationItem = function(config){
4991     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
4992     this.addEvents({
4993         // raw events
4994         /**
4995          * @event click
4996          * The raw click event for the entire grid.
4997          * @param {Roo.EventObject} e
4998          */
4999         "click" : true
5000     });
5001 };
5002
5003 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
5004     
5005     href : false,
5006     html : false,
5007     preventDefault: true,
5008     active : false,
5009     cls : false,
5010     disabled: false,
5011     
5012     getAutoCreate : function(){
5013         var cfg= {
5014             tag: 'li',
5015             cn: [
5016                 {
5017                     tag : 'a',
5018                     href : this.href ? this.href : '#',
5019                     html : this.html ? this.html : ''
5020                 }
5021             ]
5022         };
5023         
5024         if(this.cls){
5025             cfg.cls = this.cls;
5026         }
5027         
5028         if(this.disabled){
5029             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
5030         }
5031         
5032         if(this.active){
5033             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
5034         }
5035         
5036         return cfg;
5037     },
5038     
5039     initEvents: function() {
5040         
5041         this.el.on('click', this.onClick, this);
5042         
5043     },
5044     onClick : function(e)
5045     {
5046         Roo.log('PaginationItem on click ');
5047         if(this.preventDefault){
5048             e.preventDefault();
5049         }
5050         
5051         if(this.disabled){
5052             return;
5053         }
5054         
5055         this.fireEvent('click', this, e);
5056     }
5057    
5058 });
5059
5060  
5061
5062  /*
5063  * - LGPL
5064  *
5065  * slider
5066  * 
5067  */
5068
5069
5070 /**
5071  * @class Roo.bootstrap.Slider
5072  * @extends Roo.bootstrap.Component
5073  * Bootstrap Slider class
5074  *    
5075  * @constructor
5076  * Create a new Slider
5077  * @param {Object} config The config object
5078  */
5079
5080 Roo.bootstrap.Slider = function(config){
5081     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
5082 };
5083
5084 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
5085     
5086     getAutoCreate : function(){
5087         
5088         var cfg = {
5089             tag: 'div',
5090             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
5091             cn: [
5092                 {
5093                     tag: 'a',
5094                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
5095                 }
5096             ]
5097         };
5098         
5099         return cfg;
5100     }
5101    
5102 });
5103
5104  /*
5105  * Based on:
5106  * Ext JS Library 1.1.1
5107  * Copyright(c) 2006-2007, Ext JS, LLC.
5108  *
5109  * Originally Released Under LGPL - original licence link has changed is not relivant.
5110  *
5111  * Fork - LGPL
5112  * <script type="text/javascript">
5113  */
5114  
5115
5116 /**
5117  * @class Roo.grid.ColumnModel
5118  * @extends Roo.util.Observable
5119  * This is the default implementation of a ColumnModel used by the Grid. It defines
5120  * the columns in the grid.
5121  * <br>Usage:<br>
5122  <pre><code>
5123  var colModel = new Roo.grid.ColumnModel([
5124         {header: "Ticker", width: 60, sortable: true, locked: true},
5125         {header: "Company Name", width: 150, sortable: true},
5126         {header: "Market Cap.", width: 100, sortable: true},
5127         {header: "$ Sales", width: 100, sortable: true, renderer: money},
5128         {header: "Employees", width: 100, sortable: true, resizable: false}
5129  ]);
5130  </code></pre>
5131  * <p>
5132  
5133  * The config options listed for this class are options which may appear in each
5134  * individual column definition.
5135  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
5136  * @constructor
5137  * @param {Object} config An Array of column config objects. See this class's
5138  * config objects for details.
5139 */
5140 Roo.grid.ColumnModel = function(config){
5141         /**
5142      * The config passed into the constructor
5143      */
5144     this.config = config;
5145     this.lookup = {};
5146
5147     // if no id, create one
5148     // if the column does not have a dataIndex mapping,
5149     // map it to the order it is in the config
5150     for(var i = 0, len = config.length; i < len; i++){
5151         var c = config[i];
5152         if(typeof c.dataIndex == "undefined"){
5153             c.dataIndex = i;
5154         }
5155         if(typeof c.renderer == "string"){
5156             c.renderer = Roo.util.Format[c.renderer];
5157         }
5158         if(typeof c.id == "undefined"){
5159             c.id = Roo.id();
5160         }
5161         if(c.editor && c.editor.xtype){
5162             c.editor  = Roo.factory(c.editor, Roo.grid);
5163         }
5164         if(c.editor && c.editor.isFormField){
5165             c.editor = new Roo.grid.GridEditor(c.editor);
5166         }
5167         this.lookup[c.id] = c;
5168     }
5169
5170     /**
5171      * The width of columns which have no width specified (defaults to 100)
5172      * @type Number
5173      */
5174     this.defaultWidth = 100;
5175
5176     /**
5177      * Default sortable of columns which have no sortable specified (defaults to false)
5178      * @type Boolean
5179      */
5180     this.defaultSortable = false;
5181
5182     this.addEvents({
5183         /**
5184              * @event widthchange
5185              * Fires when the width of a column changes.
5186              * @param {ColumnModel} this
5187              * @param {Number} columnIndex The column index
5188              * @param {Number} newWidth The new width
5189              */
5190             "widthchange": true,
5191         /**
5192              * @event headerchange
5193              * Fires when the text of a header changes.
5194              * @param {ColumnModel} this
5195              * @param {Number} columnIndex The column index
5196              * @param {Number} newText The new header text
5197              */
5198             "headerchange": true,
5199         /**
5200              * @event hiddenchange
5201              * Fires when a column is hidden or "unhidden".
5202              * @param {ColumnModel} this
5203              * @param {Number} columnIndex The column index
5204              * @param {Boolean} hidden true if hidden, false otherwise
5205              */
5206             "hiddenchange": true,
5207             /**
5208          * @event columnmoved
5209          * Fires when a column is moved.
5210          * @param {ColumnModel} this
5211          * @param {Number} oldIndex
5212          * @param {Number} newIndex
5213          */
5214         "columnmoved" : true,
5215         /**
5216          * @event columlockchange
5217          * Fires when a column's locked state is changed
5218          * @param {ColumnModel} this
5219          * @param {Number} colIndex
5220          * @param {Boolean} locked true if locked
5221          */
5222         "columnlockchange" : true
5223     });
5224     Roo.grid.ColumnModel.superclass.constructor.call(this);
5225 };
5226 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
5227     /**
5228      * @cfg {String} header The header text to display in the Grid view.
5229      */
5230     /**
5231      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
5232      * {@link Roo.data.Record} definition from which to draw the column's value. If not
5233      * specified, the column's index is used as an index into the Record's data Array.
5234      */
5235     /**
5236      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
5237      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
5238      */
5239     /**
5240      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
5241      * Defaults to the value of the {@link #defaultSortable} property.
5242      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
5243      */
5244     /**
5245      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
5246      */
5247     /**
5248      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
5249      */
5250     /**
5251      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
5252      */
5253     /**
5254      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
5255      */
5256     /**
5257      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
5258      * given the cell's data value. See {@link #setRenderer}. If not specified, the
5259      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
5260      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
5261      */
5262        /**
5263      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
5264      */
5265     /**
5266      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
5267      */
5268     /**
5269      * @cfg {String} cursor (Optional)
5270      */
5271     /**
5272      * @cfg {String} tooltip (Optional)
5273      */
5274     /**
5275      * @cfg {Number} xs (Optional)
5276      */
5277     /**
5278      * @cfg {Number} sm (Optional)
5279      */
5280     /**
5281      * @cfg {Number} md (Optional)
5282      */
5283     /**
5284      * @cfg {Number} lg (Optional)
5285      */
5286     /**
5287      * Returns the id of the column at the specified index.
5288      * @param {Number} index The column index
5289      * @return {String} the id
5290      */
5291     getColumnId : function(index){
5292         return this.config[index].id;
5293     },
5294
5295     /**
5296      * Returns the column for a specified id.
5297      * @param {String} id The column id
5298      * @return {Object} the column
5299      */
5300     getColumnById : function(id){
5301         return this.lookup[id];
5302     },
5303
5304     
5305     /**
5306      * Returns the column for a specified dataIndex.
5307      * @param {String} dataIndex The column dataIndex
5308      * @return {Object|Boolean} the column or false if not found
5309      */
5310     getColumnByDataIndex: function(dataIndex){
5311         var index = this.findColumnIndex(dataIndex);
5312         return index > -1 ? this.config[index] : false;
5313     },
5314     
5315     /**
5316      * Returns the index for a specified column id.
5317      * @param {String} id The column id
5318      * @return {Number} the index, or -1 if not found
5319      */
5320     getIndexById : function(id){
5321         for(var i = 0, len = this.config.length; i < len; i++){
5322             if(this.config[i].id == id){
5323                 return i;
5324             }
5325         }
5326         return -1;
5327     },
5328     
5329     /**
5330      * Returns the index for a specified column dataIndex.
5331      * @param {String} dataIndex The column dataIndex
5332      * @return {Number} the index, or -1 if not found
5333      */
5334     
5335     findColumnIndex : function(dataIndex){
5336         for(var i = 0, len = this.config.length; i < len; i++){
5337             if(this.config[i].dataIndex == dataIndex){
5338                 return i;
5339             }
5340         }
5341         return -1;
5342     },
5343     
5344     
5345     moveColumn : function(oldIndex, newIndex){
5346         var c = this.config[oldIndex];
5347         this.config.splice(oldIndex, 1);
5348         this.config.splice(newIndex, 0, c);
5349         this.dataMap = null;
5350         this.fireEvent("columnmoved", this, oldIndex, newIndex);
5351     },
5352
5353     isLocked : function(colIndex){
5354         return this.config[colIndex].locked === true;
5355     },
5356
5357     setLocked : function(colIndex, value, suppressEvent){
5358         if(this.isLocked(colIndex) == value){
5359             return;
5360         }
5361         this.config[colIndex].locked = value;
5362         if(!suppressEvent){
5363             this.fireEvent("columnlockchange", this, colIndex, value);
5364         }
5365     },
5366
5367     getTotalLockedWidth : function(){
5368         var totalWidth = 0;
5369         for(var i = 0; i < this.config.length; i++){
5370             if(this.isLocked(i) && !this.isHidden(i)){
5371                 this.totalWidth += this.getColumnWidth(i);
5372             }
5373         }
5374         return totalWidth;
5375     },
5376
5377     getLockedCount : function(){
5378         for(var i = 0, len = this.config.length; i < len; i++){
5379             if(!this.isLocked(i)){
5380                 return i;
5381             }
5382         }
5383         
5384         return this.config.length;
5385     },
5386
5387     /**
5388      * Returns the number of columns.
5389      * @return {Number}
5390      */
5391     getColumnCount : function(visibleOnly){
5392         if(visibleOnly === true){
5393             var c = 0;
5394             for(var i = 0, len = this.config.length; i < len; i++){
5395                 if(!this.isHidden(i)){
5396                     c++;
5397                 }
5398             }
5399             return c;
5400         }
5401         return this.config.length;
5402     },
5403
5404     /**
5405      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
5406      * @param {Function} fn
5407      * @param {Object} scope (optional)
5408      * @return {Array} result
5409      */
5410     getColumnsBy : function(fn, scope){
5411         var r = [];
5412         for(var i = 0, len = this.config.length; i < len; i++){
5413             var c = this.config[i];
5414             if(fn.call(scope||this, c, i) === true){
5415                 r[r.length] = c;
5416             }
5417         }
5418         return r;
5419     },
5420
5421     /**
5422      * Returns true if the specified column is sortable.
5423      * @param {Number} col The column index
5424      * @return {Boolean}
5425      */
5426     isSortable : function(col){
5427         if(typeof this.config[col].sortable == "undefined"){
5428             return this.defaultSortable;
5429         }
5430         return this.config[col].sortable;
5431     },
5432
5433     /**
5434      * Returns the rendering (formatting) function defined for the column.
5435      * @param {Number} col The column index.
5436      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
5437      */
5438     getRenderer : function(col){
5439         if(!this.config[col].renderer){
5440             return Roo.grid.ColumnModel.defaultRenderer;
5441         }
5442         return this.config[col].renderer;
5443     },
5444
5445     /**
5446      * Sets the rendering (formatting) function for a column.
5447      * @param {Number} col The column index
5448      * @param {Function} fn The function to use to process the cell's raw data
5449      * to return HTML markup for the grid view. The render function is called with
5450      * the following parameters:<ul>
5451      * <li>Data value.</li>
5452      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
5453      * <li>css A CSS style string to apply to the table cell.</li>
5454      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
5455      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
5456      * <li>Row index</li>
5457      * <li>Column index</li>
5458      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
5459      */
5460     setRenderer : function(col, fn){
5461         this.config[col].renderer = fn;
5462     },
5463
5464     /**
5465      * Returns the width for the specified column.
5466      * @param {Number} col The column index
5467      * @return {Number}
5468      */
5469     getColumnWidth : function(col){
5470         return this.config[col].width * 1 || this.defaultWidth;
5471     },
5472
5473     /**
5474      * Sets the width for a column.
5475      * @param {Number} col The column index
5476      * @param {Number} width The new width
5477      */
5478     setColumnWidth : function(col, width, suppressEvent){
5479         this.config[col].width = width;
5480         this.totalWidth = null;
5481         if(!suppressEvent){
5482              this.fireEvent("widthchange", this, col, width);
5483         }
5484     },
5485
5486     /**
5487      * Returns the total width of all columns.
5488      * @param {Boolean} includeHidden True to include hidden column widths
5489      * @return {Number}
5490      */
5491     getTotalWidth : function(includeHidden){
5492         if(!this.totalWidth){
5493             this.totalWidth = 0;
5494             for(var i = 0, len = this.config.length; i < len; i++){
5495                 if(includeHidden || !this.isHidden(i)){
5496                     this.totalWidth += this.getColumnWidth(i);
5497                 }
5498             }
5499         }
5500         return this.totalWidth;
5501     },
5502
5503     /**
5504      * Returns the header for the specified column.
5505      * @param {Number} col The column index
5506      * @return {String}
5507      */
5508     getColumnHeader : function(col){
5509         return this.config[col].header;
5510     },
5511
5512     /**
5513      * Sets the header for a column.
5514      * @param {Number} col The column index
5515      * @param {String} header The new header
5516      */
5517     setColumnHeader : function(col, header){
5518         this.config[col].header = header;
5519         this.fireEvent("headerchange", this, col, header);
5520     },
5521
5522     /**
5523      * Returns the tooltip for the specified column.
5524      * @param {Number} col The column index
5525      * @return {String}
5526      */
5527     getColumnTooltip : function(col){
5528             return this.config[col].tooltip;
5529     },
5530     /**
5531      * Sets the tooltip for a column.
5532      * @param {Number} col The column index
5533      * @param {String} tooltip The new tooltip
5534      */
5535     setColumnTooltip : function(col, tooltip){
5536             this.config[col].tooltip = tooltip;
5537     },
5538
5539     /**
5540      * Returns the dataIndex for the specified column.
5541      * @param {Number} col The column index
5542      * @return {Number}
5543      */
5544     getDataIndex : function(col){
5545         return this.config[col].dataIndex;
5546     },
5547
5548     /**
5549      * Sets the dataIndex for a column.
5550      * @param {Number} col The column index
5551      * @param {Number} dataIndex The new dataIndex
5552      */
5553     setDataIndex : function(col, dataIndex){
5554         this.config[col].dataIndex = dataIndex;
5555     },
5556
5557     
5558     
5559     /**
5560      * Returns true if the cell is editable.
5561      * @param {Number} colIndex The column index
5562      * @param {Number} rowIndex The row index - this is nto actually used..?
5563      * @return {Boolean}
5564      */
5565     isCellEditable : function(colIndex, rowIndex){
5566         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
5567     },
5568
5569     /**
5570      * Returns the editor defined for the cell/column.
5571      * return false or null to disable editing.
5572      * @param {Number} colIndex The column index
5573      * @param {Number} rowIndex The row index
5574      * @return {Object}
5575      */
5576     getCellEditor : function(colIndex, rowIndex){
5577         return this.config[colIndex].editor;
5578     },
5579
5580     /**
5581      * Sets if a column is editable.
5582      * @param {Number} col The column index
5583      * @param {Boolean} editable True if the column is editable
5584      */
5585     setEditable : function(col, editable){
5586         this.config[col].editable = editable;
5587     },
5588
5589
5590     /**
5591      * Returns true if the column is hidden.
5592      * @param {Number} colIndex The column index
5593      * @return {Boolean}
5594      */
5595     isHidden : function(colIndex){
5596         return this.config[colIndex].hidden;
5597     },
5598
5599
5600     /**
5601      * Returns true if the column width cannot be changed
5602      */
5603     isFixed : function(colIndex){
5604         return this.config[colIndex].fixed;
5605     },
5606
5607     /**
5608      * Returns true if the column can be resized
5609      * @return {Boolean}
5610      */
5611     isResizable : function(colIndex){
5612         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
5613     },
5614     /**
5615      * Sets if a column is hidden.
5616      * @param {Number} colIndex The column index
5617      * @param {Boolean} hidden True if the column is hidden
5618      */
5619     setHidden : function(colIndex, hidden){
5620         this.config[colIndex].hidden = hidden;
5621         this.totalWidth = null;
5622         this.fireEvent("hiddenchange", this, colIndex, hidden);
5623     },
5624
5625     /**
5626      * Sets the editor for a column.
5627      * @param {Number} col The column index
5628      * @param {Object} editor The editor object
5629      */
5630     setEditor : function(col, editor){
5631         this.config[col].editor = editor;
5632     }
5633 });
5634
5635 Roo.grid.ColumnModel.defaultRenderer = function(value)
5636 {
5637     if(typeof value == "object") {
5638         return value;
5639     }
5640         if(typeof value == "string" && value.length < 1){
5641             return "&#160;";
5642         }
5643     
5644         return String.format("{0}", value);
5645 };
5646
5647 // Alias for backwards compatibility
5648 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
5649 /*
5650  * Based on:
5651  * Ext JS Library 1.1.1
5652  * Copyright(c) 2006-2007, Ext JS, LLC.
5653  *
5654  * Originally Released Under LGPL - original licence link has changed is not relivant.
5655  *
5656  * Fork - LGPL
5657  * <script type="text/javascript">
5658  */
5659  
5660 /**
5661  * @class Roo.LoadMask
5662  * A simple utility class for generically masking elements while loading data.  If the element being masked has
5663  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
5664  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
5665  * element's UpdateManager load indicator and will be destroyed after the initial load.
5666  * @constructor
5667  * Create a new LoadMask
5668  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
5669  * @param {Object} config The config object
5670  */
5671 Roo.LoadMask = function(el, config){
5672     this.el = Roo.get(el);
5673     Roo.apply(this, config);
5674     if(this.store){
5675         this.store.on('beforeload', this.onBeforeLoad, this);
5676         this.store.on('load', this.onLoad, this);
5677         this.store.on('loadexception', this.onLoadException, this);
5678         this.removeMask = false;
5679     }else{
5680         var um = this.el.getUpdateManager();
5681         um.showLoadIndicator = false; // disable the default indicator
5682         um.on('beforeupdate', this.onBeforeLoad, this);
5683         um.on('update', this.onLoad, this);
5684         um.on('failure', this.onLoad, this);
5685         this.removeMask = true;
5686     }
5687 };
5688
5689 Roo.LoadMask.prototype = {
5690     /**
5691      * @cfg {Boolean} removeMask
5692      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
5693      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
5694      */
5695     /**
5696      * @cfg {String} msg
5697      * The text to display in a centered loading message box (defaults to 'Loading...')
5698      */
5699     msg : 'Loading...',
5700     /**
5701      * @cfg {String} msgCls
5702      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
5703      */
5704     msgCls : 'x-mask-loading',
5705
5706     /**
5707      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
5708      * @type Boolean
5709      */
5710     disabled: false,
5711
5712     /**
5713      * Disables the mask to prevent it from being displayed
5714      */
5715     disable : function(){
5716        this.disabled = true;
5717     },
5718
5719     /**
5720      * Enables the mask so that it can be displayed
5721      */
5722     enable : function(){
5723         this.disabled = false;
5724     },
5725     
5726     onLoadException : function()
5727     {
5728         Roo.log(arguments);
5729         
5730         if (typeof(arguments[3]) != 'undefined') {
5731             Roo.MessageBox.alert("Error loading",arguments[3]);
5732         } 
5733         /*
5734         try {
5735             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
5736                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
5737             }   
5738         } catch(e) {
5739             
5740         }
5741         */
5742     
5743         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5744     },
5745     // private
5746     onLoad : function()
5747     {
5748         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
5749     },
5750
5751     // private
5752     onBeforeLoad : function(){
5753         if(!this.disabled){
5754             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
5755         }
5756     },
5757
5758     // private
5759     destroy : function(){
5760         if(this.store){
5761             this.store.un('beforeload', this.onBeforeLoad, this);
5762             this.store.un('load', this.onLoad, this);
5763             this.store.un('loadexception', this.onLoadException, this);
5764         }else{
5765             var um = this.el.getUpdateManager();
5766             um.un('beforeupdate', this.onBeforeLoad, this);
5767             um.un('update', this.onLoad, this);
5768             um.un('failure', this.onLoad, this);
5769         }
5770     }
5771 };/*
5772  * - LGPL
5773  *
5774  * table
5775  * 
5776  */
5777
5778 /**
5779  * @class Roo.bootstrap.Table
5780  * @extends Roo.bootstrap.Component
5781  * Bootstrap Table class
5782  * @cfg {String} cls table class
5783  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
5784  * @cfg {String} bgcolor Specifies the background color for a table
5785  * @cfg {Number} border Specifies whether the table cells should have borders or not
5786  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
5787  * @cfg {Number} cellspacing Specifies the space between cells
5788  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
5789  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
5790  * @cfg {String} sortable Specifies that the table should be sortable
5791  * @cfg {String} summary Specifies a summary of the content of a table
5792  * @cfg {Number} width Specifies the width of a table
5793  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
5794  * 
5795  * @cfg {boolean} striped Should the rows be alternative striped
5796  * @cfg {boolean} bordered Add borders to the table
5797  * @cfg {boolean} hover Add hover highlighting
5798  * @cfg {boolean} condensed Format condensed
5799  * @cfg {boolean} responsive Format condensed
5800  * @cfg {Boolean} loadMask (true|false) default false
5801  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
5802  * @cfg {Boolean} headerShow (true|false) generate thead, default true
5803  * @cfg {Boolean} rowSelection (true|false) default false
5804  * @cfg {Boolean} cellSelection (true|false) default false
5805  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
5806  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
5807  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
5808  
5809  * 
5810  * @constructor
5811  * Create a new Table
5812  * @param {Object} config The config object
5813  */
5814
5815 Roo.bootstrap.Table = function(config){
5816     Roo.bootstrap.Table.superclass.constructor.call(this, config);
5817     
5818   
5819     
5820     // BC...
5821     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
5822     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
5823     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
5824     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
5825     
5826     this.sm = this.sm || {xtype: 'RowSelectionModel'};
5827     if (this.sm) {
5828         this.sm.grid = this;
5829         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
5830         this.sm = this.selModel;
5831         this.sm.xmodule = this.xmodule || false;
5832     }
5833     
5834     if (this.cm && typeof(this.cm.config) == 'undefined') {
5835         this.colModel = new Roo.grid.ColumnModel(this.cm);
5836         this.cm = this.colModel;
5837         this.cm.xmodule = this.xmodule || false;
5838     }
5839     if (this.store) {
5840         this.store= Roo.factory(this.store, Roo.data);
5841         this.ds = this.store;
5842         this.ds.xmodule = this.xmodule || false;
5843          
5844     }
5845     if (this.footer && this.store) {
5846         this.footer.dataSource = this.ds;
5847         this.footer = Roo.factory(this.footer);
5848     }
5849     
5850     /** @private */
5851     this.addEvents({
5852         /**
5853          * @event cellclick
5854          * Fires when a cell is clicked
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         "cellclick" : true,
5862         /**
5863          * @event celldblclick
5864          * Fires when a cell is double clicked
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         "celldblclick" : true,
5872         /**
5873          * @event rowclick
5874          * Fires when a row is clicked
5875          * @param {Roo.bootstrap.Table} this
5876          * @param {Roo.Element} el
5877          * @param {Number} rowIndex
5878          * @param {Roo.EventObject} e
5879          */
5880         "rowclick" : true,
5881         /**
5882          * @event rowdblclick
5883          * Fires when a row is double clicked
5884          * @param {Roo.bootstrap.Table} this
5885          * @param {Roo.Element} el
5886          * @param {Number} rowIndex
5887          * @param {Roo.EventObject} e
5888          */
5889         "rowdblclick" : true,
5890         /**
5891          * @event mouseover
5892          * Fires when a mouseover occur
5893          * @param {Roo.bootstrap.Table} this
5894          * @param {Roo.Element} el
5895          * @param {Number} rowIndex
5896          * @param {Number} columnIndex
5897          * @param {Roo.EventObject} e
5898          */
5899         "mouseover" : true,
5900         /**
5901          * @event mouseout
5902          * Fires when a mouseout occur
5903          * @param {Roo.bootstrap.Table} this
5904          * @param {Roo.Element} el
5905          * @param {Number} rowIndex
5906          * @param {Number} columnIndex
5907          * @param {Roo.EventObject} e
5908          */
5909         "mouseout" : true,
5910         /**
5911          * @event rowclass
5912          * Fires when a row is rendered, so you can change add a style to it.
5913          * @param {Roo.bootstrap.Table} this
5914          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
5915          */
5916         'rowclass' : true,
5917           /**
5918          * @event rowsrendered
5919          * Fires when all the  rows have been rendered
5920          * @param {Roo.bootstrap.Table} this
5921          */
5922         'rowsrendered' : true,
5923         /**
5924          * @event contextmenu
5925          * The raw contextmenu event for the entire grid.
5926          * @param {Roo.EventObject} e
5927          */
5928         "contextmenu" : true,
5929         /**
5930          * @event rowcontextmenu
5931          * Fires when a row is right clicked
5932          * @param {Roo.bootstrap.Table} this
5933          * @param {Number} rowIndex
5934          * @param {Roo.EventObject} e
5935          */
5936         "rowcontextmenu" : true,
5937         /**
5938          * @event cellcontextmenu
5939          * Fires when a cell is right clicked
5940          * @param {Roo.bootstrap.Table} this
5941          * @param {Number} rowIndex
5942          * @param {Number} cellIndex
5943          * @param {Roo.EventObject} e
5944          */
5945          "cellcontextmenu" : true,
5946          /**
5947          * @event headercontextmenu
5948          * Fires when a header is right clicked
5949          * @param {Roo.bootstrap.Table} this
5950          * @param {Number} columnIndex
5951          * @param {Roo.EventObject} e
5952          */
5953         "headercontextmenu" : true
5954     });
5955 };
5956
5957 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
5958     
5959     cls: false,
5960     align: false,
5961     bgcolor: false,
5962     border: false,
5963     cellpadding: false,
5964     cellspacing: false,
5965     frame: false,
5966     rules: false,
5967     sortable: false,
5968     summary: false,
5969     width: false,
5970     striped : false,
5971     scrollBody : false,
5972     bordered: false,
5973     hover:  false,
5974     condensed : false,
5975     responsive : false,
5976     sm : false,
5977     cm : false,
5978     store : false,
5979     loadMask : false,
5980     footerShow : true,
5981     headerShow : true,
5982   
5983     rowSelection : false,
5984     cellSelection : false,
5985     layout : false,
5986     
5987     // Roo.Element - the tbody
5988     mainBody: false,
5989     // Roo.Element - thead element
5990     mainHead: false,
5991     
5992     container: false, // used by gridpanel...
5993     
5994     lazyLoad : false,
5995     
5996     getAutoCreate : function()
5997     {
5998         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
5999         
6000         cfg = {
6001             tag: 'table',
6002             cls : 'table',
6003             cn : []
6004         };
6005         if (this.scrollBody) {
6006             cfg.cls += ' table-body-fixed';
6007         }    
6008         if (this.striped) {
6009             cfg.cls += ' table-striped';
6010         }
6011         
6012         if (this.hover) {
6013             cfg.cls += ' table-hover';
6014         }
6015         if (this.bordered) {
6016             cfg.cls += ' table-bordered';
6017         }
6018         if (this.condensed) {
6019             cfg.cls += ' table-condensed';
6020         }
6021         if (this.responsive) {
6022             cfg.cls += ' table-responsive';
6023         }
6024         
6025         if (this.cls) {
6026             cfg.cls+=  ' ' +this.cls;
6027         }
6028         
6029         // this lot should be simplifed...
6030         
6031         if (this.align) {
6032             cfg.align=this.align;
6033         }
6034         if (this.bgcolor) {
6035             cfg.bgcolor=this.bgcolor;
6036         }
6037         if (this.border) {
6038             cfg.border=this.border;
6039         }
6040         if (this.cellpadding) {
6041             cfg.cellpadding=this.cellpadding;
6042         }
6043         if (this.cellspacing) {
6044             cfg.cellspacing=this.cellspacing;
6045         }
6046         if (this.frame) {
6047             cfg.frame=this.frame;
6048         }
6049         if (this.rules) {
6050             cfg.rules=this.rules;
6051         }
6052         if (this.sortable) {
6053             cfg.sortable=this.sortable;
6054         }
6055         if (this.summary) {
6056             cfg.summary=this.summary;
6057         }
6058         if (this.width) {
6059             cfg.width=this.width;
6060         }
6061         if (this.layout) {
6062             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
6063         }
6064         
6065         if(this.store || this.cm){
6066             if(this.headerShow){
6067                 cfg.cn.push(this.renderHeader());
6068             }
6069             
6070             cfg.cn.push(this.renderBody());
6071             
6072             if(this.footerShow){
6073                 cfg.cn.push(this.renderFooter());
6074             }
6075             // where does this come from?
6076             //cfg.cls+=  ' TableGrid';
6077         }
6078         
6079         return { cn : [ cfg ] };
6080     },
6081     
6082     initEvents : function()
6083     {   
6084         if(!this.store || !this.cm){
6085             return;
6086         }
6087         if (this.selModel) {
6088             this.selModel.initEvents();
6089         }
6090         
6091         
6092         //Roo.log('initEvents with ds!!!!');
6093         
6094         this.mainBody = this.el.select('tbody', true).first();
6095         this.mainHead = this.el.select('thead', true).first();
6096         
6097         
6098         
6099         
6100         var _this = this;
6101         
6102         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6103             e.on('click', _this.sort, _this);
6104         });
6105         
6106         this.mainBody.on("click", this.onClick, this);
6107         this.mainBody.on("dblclick", this.onDblClick, this);
6108         
6109         // why is this done????? = it breaks dialogs??
6110         //this.parent().el.setStyle('position', 'relative');
6111         
6112         
6113         if (this.footer) {
6114             this.footer.parentId = this.id;
6115             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
6116             
6117             if(this.lazyLoad){
6118                 this.el.select('tfoot tr td').first().addClass('hide');
6119             }
6120         } 
6121         
6122         this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
6123         
6124         this.store.on('load', this.onLoad, this);
6125         this.store.on('beforeload', this.onBeforeLoad, this);
6126         this.store.on('update', this.onUpdate, this);
6127         this.store.on('add', this.onAdd, this);
6128         this.store.on("clear", this.clear, this);
6129         
6130         this.el.on("contextmenu", this.onContextMenu, this);
6131         
6132         this.mainBody.on('scroll', this.onBodyScroll, this);
6133         
6134         this.cm.on("headerchange", this.onHeaderChange, this);
6135         
6136     },
6137     
6138     onContextMenu : function(e, t)
6139     {
6140         this.processEvent("contextmenu", e);
6141     },
6142     
6143     processEvent : function(name, e)
6144     {
6145         if (name != 'touchstart' ) {
6146             this.fireEvent(name, e);    
6147         }
6148         
6149         var t = e.getTarget();
6150         
6151         var cell = Roo.get(t);
6152         
6153         if(!cell){
6154             return;
6155         }
6156         
6157         if(cell.findParent('tfoot', false, true)){
6158             return;
6159         }
6160         
6161         if(cell.findParent('thead', false, true)){
6162             
6163             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6164                 cell = Roo.get(t).findParent('th', false, true);
6165                 if (!cell) {
6166                     Roo.log("failed to find th in thead?");
6167                     Roo.log(e.getTarget());
6168                     return;
6169                 }
6170             }
6171             
6172             var cellIndex = cell.dom.cellIndex;
6173             
6174             var ename = name == 'touchstart' ? 'click' : name;
6175             this.fireEvent("header" + ename, this, cellIndex, e);
6176             
6177             return;
6178         }
6179         
6180         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6181             cell = Roo.get(t).findParent('td', false, true);
6182             if (!cell) {
6183                 Roo.log("failed to find th in tbody?");
6184                 Roo.log(e.getTarget());
6185                 return;
6186             }
6187         }
6188         
6189         var row = cell.findParent('tr', false, true);
6190         var cellIndex = cell.dom.cellIndex;
6191         var rowIndex = row.dom.rowIndex - 1;
6192         
6193         if(row !== false){
6194             
6195             this.fireEvent("row" + name, this, rowIndex, e);
6196             
6197             if(cell !== false){
6198             
6199                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6200             }
6201         }
6202         
6203     },
6204     
6205     onMouseover : function(e, el)
6206     {
6207         var cell = Roo.get(el);
6208         
6209         if(!cell){
6210             return;
6211         }
6212         
6213         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6214             cell = cell.findParent('td', false, true);
6215         }
6216         
6217         var row = cell.findParent('tr', false, true);
6218         var cellIndex = cell.dom.cellIndex;
6219         var rowIndex = row.dom.rowIndex - 1; // start from 0
6220         
6221         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6222         
6223     },
6224     
6225     onMouseout : function(e, el)
6226     {
6227         var cell = Roo.get(el);
6228         
6229         if(!cell){
6230             return;
6231         }
6232         
6233         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6234             cell = cell.findParent('td', false, true);
6235         }
6236         
6237         var row = cell.findParent('tr', false, true);
6238         var cellIndex = cell.dom.cellIndex;
6239         var rowIndex = row.dom.rowIndex - 1; // start from 0
6240         
6241         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6242         
6243     },
6244     
6245     onClick : function(e, el)
6246     {
6247         var cell = Roo.get(el);
6248         
6249         if(!cell || (!this.cellSelection && !this.rowSelection)){
6250             return;
6251         }
6252         
6253         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6254             cell = cell.findParent('td', false, true);
6255         }
6256         
6257         if(!cell || typeof(cell) == 'undefined'){
6258             return;
6259         }
6260         
6261         var row = cell.findParent('tr', false, true);
6262         
6263         if(!row || typeof(row) == 'undefined'){
6264             return;
6265         }
6266         
6267         var cellIndex = cell.dom.cellIndex;
6268         var rowIndex = this.getRowIndex(row);
6269         
6270         // why??? - should these not be based on SelectionModel?
6271         if(this.cellSelection){
6272             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6273         }
6274         
6275         if(this.rowSelection){
6276             this.fireEvent('rowclick', this, row, rowIndex, e);
6277         }
6278         
6279         
6280     },
6281         
6282     onDblClick : function(e,el)
6283     {
6284         var cell = Roo.get(el);
6285         
6286         if(!cell || (!this.cellSelection && !this.rowSelection)){
6287             return;
6288         }
6289         
6290         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6291             cell = cell.findParent('td', false, true);
6292         }
6293         
6294         if(!cell || typeof(cell) == 'undefined'){
6295             return;
6296         }
6297         
6298         var row = cell.findParent('tr', false, true);
6299         
6300         if(!row || typeof(row) == 'undefined'){
6301             return;
6302         }
6303         
6304         var cellIndex = cell.dom.cellIndex;
6305         var rowIndex = this.getRowIndex(row);
6306         
6307         if(this.cellSelection){
6308             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6309         }
6310         
6311         if(this.rowSelection){
6312             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6313         }
6314     },
6315     
6316     sort : function(e,el)
6317     {
6318         var col = Roo.get(el);
6319         
6320         if(!col.hasClass('sortable')){
6321             return;
6322         }
6323         
6324         var sort = col.attr('sort');
6325         var dir = 'ASC';
6326         
6327         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6328             dir = 'DESC';
6329         }
6330         
6331         this.store.sortInfo = {field : sort, direction : dir};
6332         
6333         if (this.footer) {
6334             Roo.log("calling footer first");
6335             this.footer.onClick('first');
6336         } else {
6337         
6338             this.store.load({ params : { start : 0 } });
6339         }
6340     },
6341     
6342     renderHeader : function()
6343     {
6344         var header = {
6345             tag: 'thead',
6346             cn : []
6347         };
6348         
6349         var cm = this.cm;
6350         this.totalWidth = 0;
6351         
6352         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6353             
6354             var config = cm.config[i];
6355             
6356             var c = {
6357                 tag: 'th',
6358                 style : '',
6359                 html: cm.getColumnHeader(i)
6360             };
6361             
6362             var hh = '';
6363             
6364             if(typeof(config.sortable) != 'undefined' && config.sortable){
6365                 c.cls = 'sortable';
6366                 c.html = '<i class="glyphicon"></i>' + c.html;
6367             }
6368             
6369             if(typeof(config.lgHeader) != 'undefined'){
6370                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6371             }
6372             
6373             if(typeof(config.mdHeader) != 'undefined'){
6374                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6375             }
6376             
6377             if(typeof(config.smHeader) != 'undefined'){
6378                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6379             }
6380             
6381             if(typeof(config.xsHeader) != 'undefined'){
6382                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6383             }
6384             
6385             if(hh.length){
6386                 c.html = hh;
6387             }
6388             
6389             if(typeof(config.tooltip) != 'undefined'){
6390                 c.tooltip = config.tooltip;
6391             }
6392             
6393             if(typeof(config.colspan) != 'undefined'){
6394                 c.colspan = config.colspan;
6395             }
6396             
6397             if(typeof(config.hidden) != 'undefined' && config.hidden){
6398                 c.style += ' display:none;';
6399             }
6400             
6401             if(typeof(config.dataIndex) != 'undefined'){
6402                 c.sort = config.dataIndex;
6403             }
6404             
6405            
6406             
6407             if(typeof(config.align) != 'undefined' && config.align.length){
6408                 c.style += ' text-align:' + config.align + ';';
6409             }
6410             
6411             if(typeof(config.width) != 'undefined'){
6412                 c.style += ' width:' + config.width + 'px;';
6413                 this.totalWidth += config.width;
6414             } else {
6415                 this.totalWidth += 100; // assume minimum of 100 per column?
6416             }
6417             
6418             if(typeof(config.cls) != 'undefined'){
6419                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6420             }
6421             
6422             ['xs','sm','md','lg'].map(function(size){
6423                 
6424                 if(typeof(config[size]) == 'undefined'){
6425                     return;
6426                 }
6427                 
6428                 if (!config[size]) { // 0 = hidden
6429                     c.cls += ' hidden-' + size;
6430                     return;
6431                 }
6432                 
6433                 c.cls += ' col-' + size + '-' + config[size];
6434
6435             });
6436             
6437             header.cn.push(c)
6438         }
6439         
6440         return header;
6441     },
6442     
6443     renderBody : function()
6444     {
6445         var body = {
6446             tag: 'tbody',
6447             cn : [
6448                 {
6449                     tag: 'tr',
6450                     cn : [
6451                         {
6452                             tag : 'td',
6453                             colspan :  this.cm.getColumnCount()
6454                         }
6455                     ]
6456                 }
6457             ]
6458         };
6459         
6460         return body;
6461     },
6462     
6463     renderFooter : function()
6464     {
6465         var footer = {
6466             tag: 'tfoot',
6467             cn : [
6468                 {
6469                     tag: 'tr',
6470                     cn : [
6471                         {
6472                             tag : 'td',
6473                             colspan :  this.cm.getColumnCount()
6474                         }
6475                     ]
6476                 }
6477             ]
6478         };
6479         
6480         return footer;
6481     },
6482     
6483     
6484     
6485     onLoad : function()
6486     {
6487 //        Roo.log('ds onload');
6488         this.clear();
6489         
6490         var _this = this;
6491         var cm = this.cm;
6492         var ds = this.store;
6493         
6494         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6495             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6496             if (_this.store.sortInfo) {
6497                     
6498                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6499                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6500                 }
6501                 
6502                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6503                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6504                 }
6505             }
6506         });
6507         
6508         var tbody =  this.mainBody;
6509               
6510         if(ds.getCount() > 0){
6511             ds.data.each(function(d,rowIndex){
6512                 var row =  this.renderRow(cm, ds, rowIndex);
6513                 
6514                 tbody.createChild(row);
6515                 
6516                 var _this = this;
6517                 
6518                 if(row.cellObjects.length){
6519                     Roo.each(row.cellObjects, function(r){
6520                         _this.renderCellObject(r);
6521                     })
6522                 }
6523                 
6524             }, this);
6525         }
6526         
6527         Roo.each(this.el.select('tbody td', true).elements, function(e){
6528             e.on('mouseover', _this.onMouseover, _this);
6529         });
6530         
6531         Roo.each(this.el.select('tbody td', true).elements, function(e){
6532             e.on('mouseout', _this.onMouseout, _this);
6533         });
6534         this.fireEvent('rowsrendered', this);
6535         //if(this.loadMask){
6536         //    this.maskEl.hide();
6537         //}
6538         
6539         this.autoSize();
6540     },
6541     
6542     
6543     onUpdate : function(ds,record)
6544     {
6545         this.refreshRow(record);
6546         this.autoSize();
6547     },
6548     
6549     onRemove : function(ds, record, index, isUpdate){
6550         if(isUpdate !== true){
6551             this.fireEvent("beforerowremoved", this, index, record);
6552         }
6553         var bt = this.mainBody.dom;
6554         
6555         var rows = this.el.select('tbody > tr', true).elements;
6556         
6557         if(typeof(rows[index]) != 'undefined'){
6558             bt.removeChild(rows[index].dom);
6559         }
6560         
6561 //        if(bt.rows[index]){
6562 //            bt.removeChild(bt.rows[index]);
6563 //        }
6564         
6565         if(isUpdate !== true){
6566             //this.stripeRows(index);
6567             //this.syncRowHeights(index, index);
6568             //this.layout();
6569             this.fireEvent("rowremoved", this, index, record);
6570         }
6571     },
6572     
6573     onAdd : function(ds, records, rowIndex)
6574     {
6575         //Roo.log('on Add called');
6576         // - note this does not handle multiple adding very well..
6577         var bt = this.mainBody.dom;
6578         for (var i =0 ; i < records.length;i++) {
6579             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6580             //Roo.log(records[i]);
6581             //Roo.log(this.store.getAt(rowIndex+i));
6582             this.insertRow(this.store, rowIndex + i, false);
6583             return;
6584         }
6585         
6586     },
6587     
6588     
6589     refreshRow : function(record){
6590         var ds = this.store, index;
6591         if(typeof record == 'number'){
6592             index = record;
6593             record = ds.getAt(index);
6594         }else{
6595             index = ds.indexOf(record);
6596         }
6597         this.insertRow(ds, index, true);
6598         this.autoSize();
6599         this.onRemove(ds, record, index+1, true);
6600         this.autoSize();
6601         //this.syncRowHeights(index, index);
6602         //this.layout();
6603         this.fireEvent("rowupdated", this, index, record);
6604     },
6605     
6606     insertRow : function(dm, rowIndex, isUpdate){
6607         
6608         if(!isUpdate){
6609             this.fireEvent("beforerowsinserted", this, rowIndex);
6610         }
6611             //var s = this.getScrollState();
6612         var row = this.renderRow(this.cm, this.store, rowIndex);
6613         // insert before rowIndex..
6614         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6615         
6616         var _this = this;
6617                 
6618         if(row.cellObjects.length){
6619             Roo.each(row.cellObjects, function(r){
6620                 _this.renderCellObject(r);
6621             })
6622         }
6623             
6624         if(!isUpdate){
6625             this.fireEvent("rowsinserted", this, rowIndex);
6626             //this.syncRowHeights(firstRow, lastRow);
6627             //this.stripeRows(firstRow);
6628             //this.layout();
6629         }
6630         
6631     },
6632     
6633     
6634     getRowDom : function(rowIndex)
6635     {
6636         var rows = this.el.select('tbody > tr', true).elements;
6637         
6638         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6639         
6640     },
6641     // returns the object tree for a tr..
6642   
6643     
6644     renderRow : function(cm, ds, rowIndex) 
6645     {
6646         
6647         var d = ds.getAt(rowIndex);
6648         
6649         var row = {
6650             tag : 'tr',
6651             cn : []
6652         };
6653             
6654         var cellObjects = [];
6655         
6656         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6657             var config = cm.config[i];
6658             
6659             var renderer = cm.getRenderer(i);
6660             var value = '';
6661             var id = false;
6662             
6663             if(typeof(renderer) !== 'undefined'){
6664                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6665             }
6666             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6667             // and are rendered into the cells after the row is rendered - using the id for the element.
6668             
6669             if(typeof(value) === 'object'){
6670                 id = Roo.id();
6671                 cellObjects.push({
6672                     container : id,
6673                     cfg : value 
6674                 })
6675             }
6676             
6677             var rowcfg = {
6678                 record: d,
6679                 rowIndex : rowIndex,
6680                 colIndex : i,
6681                 rowClass : ''
6682             };
6683
6684             this.fireEvent('rowclass', this, rowcfg);
6685             
6686             var td = {
6687                 tag: 'td',
6688                 cls : rowcfg.rowClass,
6689                 style: '',
6690                 html: (typeof(value) === 'object') ? '' : value
6691             };
6692             
6693             if (id) {
6694                 td.id = id;
6695             }
6696             
6697             if(typeof(config.colspan) != 'undefined'){
6698                 td.colspan = config.colspan;
6699             }
6700             
6701             if(typeof(config.hidden) != 'undefined' && config.hidden){
6702                 td.style += ' display:none;';
6703             }
6704             
6705             if(typeof(config.align) != 'undefined' && config.align.length){
6706                 td.style += ' text-align:' + config.align + ';';
6707             }
6708             
6709             if(typeof(config.width) != 'undefined'){
6710                 td.style += ' width:' +  config.width + 'px;';
6711             }
6712             
6713             if(typeof(config.cursor) != 'undefined'){
6714                 td.style += ' cursor:' +  config.cursor + ';';
6715             }
6716             
6717             if(typeof(config.cls) != 'undefined'){
6718                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6719             }
6720             
6721             ['xs','sm','md','lg'].map(function(size){
6722                 
6723                 if(typeof(config[size]) == 'undefined'){
6724                     return;
6725                 }
6726                 
6727                 if (!config[size]) { // 0 = hidden
6728                     td.cls += ' hidden-' + size;
6729                     return;
6730                 }
6731                 
6732                 td.cls += ' col-' + size + '-' + config[size];
6733
6734             });
6735              
6736             row.cn.push(td);
6737            
6738         }
6739         
6740         row.cellObjects = cellObjects;
6741         
6742         return row;
6743           
6744     },
6745     
6746     
6747     
6748     onBeforeLoad : function()
6749     {
6750         //Roo.log('ds onBeforeLoad');
6751         
6752         //this.clear();
6753         
6754         //if(this.loadMask){
6755         //    this.maskEl.show();
6756         //}
6757     },
6758      /**
6759      * Remove all rows
6760      */
6761     clear : function()
6762     {
6763         this.el.select('tbody', true).first().dom.innerHTML = '';
6764     },
6765     /**
6766      * Show or hide a row.
6767      * @param {Number} rowIndex to show or hide
6768      * @param {Boolean} state hide
6769      */
6770     setRowVisibility : function(rowIndex, state)
6771     {
6772         var bt = this.mainBody.dom;
6773         
6774         var rows = this.el.select('tbody > tr', true).elements;
6775         
6776         if(typeof(rows[rowIndex]) == 'undefined'){
6777             return;
6778         }
6779         rows[rowIndex].dom.style.display = state ? '' : 'none';
6780     },
6781     
6782     
6783     getSelectionModel : function(){
6784         if(!this.selModel){
6785             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6786         }
6787         return this.selModel;
6788     },
6789     /*
6790      * Render the Roo.bootstrap object from renderder
6791      */
6792     renderCellObject : function(r)
6793     {
6794         var _this = this;
6795         
6796         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6797         
6798         var t = r.cfg.render(r.container);
6799         
6800         if(r.cfg.cn){
6801             Roo.each(r.cfg.cn, function(c){
6802                 var child = {
6803                     container: t.getChildContainer(),
6804                     cfg: c
6805                 };
6806                 _this.renderCellObject(child);
6807             })
6808         }
6809     },
6810     
6811     getRowIndex : function(row)
6812     {
6813         var rowIndex = -1;
6814         
6815         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6816             if(el != row){
6817                 return;
6818             }
6819             
6820             rowIndex = index;
6821         });
6822         
6823         return rowIndex;
6824     },
6825      /**
6826      * Returns the grid's underlying element = used by panel.Grid
6827      * @return {Element} The element
6828      */
6829     getGridEl : function(){
6830         return this.el;
6831     },
6832      /**
6833      * Forces a resize - used by panel.Grid
6834      * @return {Element} The element
6835      */
6836     autoSize : function()
6837     {
6838         //var ctr = Roo.get(this.container.dom.parentElement);
6839         var ctr = Roo.get(this.el.dom);
6840         
6841         var thd = this.getGridEl().select('thead',true).first();
6842         var tbd = this.getGridEl().select('tbody', true).first();
6843         var tfd = this.getGridEl().select('tfoot', true).first();
6844         
6845         var cw = ctr.getWidth();
6846         
6847         if (tbd) {
6848             
6849             tbd.setSize(ctr.getWidth(),
6850                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
6851             );
6852             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6853             cw -= barsize;
6854         }
6855         cw = Math.max(cw, this.totalWidth);
6856         this.getGridEl().select('tr',true).setWidth(cw);
6857         // resize 'expandable coloumn?
6858         
6859         return; // we doe not have a view in this design..
6860         
6861     },
6862     onBodyScroll: function()
6863     {
6864         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6865         if(this.mainHead){
6866             this.mainHead.setStyle({
6867                 'position' : 'relative',
6868                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6869             });
6870         }
6871         
6872         if(this.lazyLoad){
6873             
6874             var scrollHeight = this.mainBody.dom.scrollHeight;
6875             
6876             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
6877             
6878             var height = this.mainBody.getHeight();
6879             
6880             if(scrollHeight - height == scrollTop) {
6881                 
6882                 var total = this.ds.getTotalCount();
6883                 
6884                 if(this.footer.cursor + this.footer.pageSize < total){
6885                     
6886                     this.footer.ds.load({
6887                         params : {
6888                             start : this.footer.cursor + this.footer.pageSize,
6889                             limit : this.footer.pageSize
6890                         },
6891                         add : true
6892                     });
6893                 }
6894             }
6895             
6896         }
6897     },
6898     
6899     onHeaderChange : function()
6900     {
6901         
6902         var header = this.renderHeader();
6903         var table = this.el.select('table', true).first();
6904         
6905         this.mainHead.remove();
6906         this.mainHead = table.createChild(header, this.mainBody, false);
6907     }
6908     
6909 });
6910
6911  
6912
6913  /*
6914  * - LGPL
6915  *
6916  * table cell
6917  * 
6918  */
6919
6920 /**
6921  * @class Roo.bootstrap.TableCell
6922  * @extends Roo.bootstrap.Component
6923  * Bootstrap TableCell class
6924  * @cfg {String} html cell contain text
6925  * @cfg {String} cls cell class
6926  * @cfg {String} tag cell tag (td|th) default td
6927  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6928  * @cfg {String} align Aligns the content in a cell
6929  * @cfg {String} axis Categorizes cells
6930  * @cfg {String} bgcolor Specifies the background color of a cell
6931  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6932  * @cfg {Number} colspan Specifies the number of columns a cell should span
6933  * @cfg {String} headers Specifies one or more header cells a cell is related to
6934  * @cfg {Number} height Sets the height of a cell
6935  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6936  * @cfg {Number} rowspan Sets the number of rows a cell should span
6937  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6938  * @cfg {String} valign Vertical aligns the content in a cell
6939  * @cfg {Number} width Specifies the width of a cell
6940  * 
6941  * @constructor
6942  * Create a new TableCell
6943  * @param {Object} config The config object
6944  */
6945
6946 Roo.bootstrap.TableCell = function(config){
6947     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6948 };
6949
6950 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6951     
6952     html: false,
6953     cls: false,
6954     tag: false,
6955     abbr: false,
6956     align: false,
6957     axis: false,
6958     bgcolor: false,
6959     charoff: false,
6960     colspan: false,
6961     headers: false,
6962     height: false,
6963     nowrap: false,
6964     rowspan: false,
6965     scope: false,
6966     valign: false,
6967     width: false,
6968     
6969     
6970     getAutoCreate : function(){
6971         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6972         
6973         cfg = {
6974             tag: 'td'
6975         };
6976         
6977         if(this.tag){
6978             cfg.tag = this.tag;
6979         }
6980         
6981         if (this.html) {
6982             cfg.html=this.html
6983         }
6984         if (this.cls) {
6985             cfg.cls=this.cls
6986         }
6987         if (this.abbr) {
6988             cfg.abbr=this.abbr
6989         }
6990         if (this.align) {
6991             cfg.align=this.align
6992         }
6993         if (this.axis) {
6994             cfg.axis=this.axis
6995         }
6996         if (this.bgcolor) {
6997             cfg.bgcolor=this.bgcolor
6998         }
6999         if (this.charoff) {
7000             cfg.charoff=this.charoff
7001         }
7002         if (this.colspan) {
7003             cfg.colspan=this.colspan
7004         }
7005         if (this.headers) {
7006             cfg.headers=this.headers
7007         }
7008         if (this.height) {
7009             cfg.height=this.height
7010         }
7011         if (this.nowrap) {
7012             cfg.nowrap=this.nowrap
7013         }
7014         if (this.rowspan) {
7015             cfg.rowspan=this.rowspan
7016         }
7017         if (this.scope) {
7018             cfg.scope=this.scope
7019         }
7020         if (this.valign) {
7021             cfg.valign=this.valign
7022         }
7023         if (this.width) {
7024             cfg.width=this.width
7025         }
7026         
7027         
7028         return cfg;
7029     }
7030    
7031 });
7032
7033  
7034
7035  /*
7036  * - LGPL
7037  *
7038  * table row
7039  * 
7040  */
7041
7042 /**
7043  * @class Roo.bootstrap.TableRow
7044  * @extends Roo.bootstrap.Component
7045  * Bootstrap TableRow class
7046  * @cfg {String} cls row class
7047  * @cfg {String} align Aligns the content in a table row
7048  * @cfg {String} bgcolor Specifies a background color for a table row
7049  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7050  * @cfg {String} valign Vertical aligns the content in a table row
7051  * 
7052  * @constructor
7053  * Create a new TableRow
7054  * @param {Object} config The config object
7055  */
7056
7057 Roo.bootstrap.TableRow = function(config){
7058     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7059 };
7060
7061 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7062     
7063     cls: false,
7064     align: false,
7065     bgcolor: false,
7066     charoff: false,
7067     valign: false,
7068     
7069     getAutoCreate : function(){
7070         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7071         
7072         cfg = {
7073             tag: 'tr'
7074         };
7075             
7076         if(this.cls){
7077             cfg.cls = this.cls;
7078         }
7079         if(this.align){
7080             cfg.align = this.align;
7081         }
7082         if(this.bgcolor){
7083             cfg.bgcolor = this.bgcolor;
7084         }
7085         if(this.charoff){
7086             cfg.charoff = this.charoff;
7087         }
7088         if(this.valign){
7089             cfg.valign = this.valign;
7090         }
7091         
7092         return cfg;
7093     }
7094    
7095 });
7096
7097  
7098
7099  /*
7100  * - LGPL
7101  *
7102  * table body
7103  * 
7104  */
7105
7106 /**
7107  * @class Roo.bootstrap.TableBody
7108  * @extends Roo.bootstrap.Component
7109  * Bootstrap TableBody class
7110  * @cfg {String} cls element class
7111  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7112  * @cfg {String} align Aligns the content inside the element
7113  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7114  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7115  * 
7116  * @constructor
7117  * Create a new TableBody
7118  * @param {Object} config The config object
7119  */
7120
7121 Roo.bootstrap.TableBody = function(config){
7122     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7123 };
7124
7125 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7126     
7127     cls: false,
7128     tag: false,
7129     align: false,
7130     charoff: false,
7131     valign: false,
7132     
7133     getAutoCreate : function(){
7134         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7135         
7136         cfg = {
7137             tag: 'tbody'
7138         };
7139             
7140         if (this.cls) {
7141             cfg.cls=this.cls
7142         }
7143         if(this.tag){
7144             cfg.tag = this.tag;
7145         }
7146         
7147         if(this.align){
7148             cfg.align = this.align;
7149         }
7150         if(this.charoff){
7151             cfg.charoff = this.charoff;
7152         }
7153         if(this.valign){
7154             cfg.valign = this.valign;
7155         }
7156         
7157         return cfg;
7158     }
7159     
7160     
7161 //    initEvents : function()
7162 //    {
7163 //        
7164 //        if(!this.store){
7165 //            return;
7166 //        }
7167 //        
7168 //        this.store = Roo.factory(this.store, Roo.data);
7169 //        this.store.on('load', this.onLoad, this);
7170 //        
7171 //        this.store.load();
7172 //        
7173 //    },
7174 //    
7175 //    onLoad: function () 
7176 //    {   
7177 //        this.fireEvent('load', this);
7178 //    }
7179 //    
7180 //   
7181 });
7182
7183  
7184
7185  /*
7186  * Based on:
7187  * Ext JS Library 1.1.1
7188  * Copyright(c) 2006-2007, Ext JS, LLC.
7189  *
7190  * Originally Released Under LGPL - original licence link has changed is not relivant.
7191  *
7192  * Fork - LGPL
7193  * <script type="text/javascript">
7194  */
7195
7196 // as we use this in bootstrap.
7197 Roo.namespace('Roo.form');
7198  /**
7199  * @class Roo.form.Action
7200  * Internal Class used to handle form actions
7201  * @constructor
7202  * @param {Roo.form.BasicForm} el The form element or its id
7203  * @param {Object} config Configuration options
7204  */
7205
7206  
7207  
7208 // define the action interface
7209 Roo.form.Action = function(form, options){
7210     this.form = form;
7211     this.options = options || {};
7212 };
7213 /**
7214  * Client Validation Failed
7215  * @const 
7216  */
7217 Roo.form.Action.CLIENT_INVALID = 'client';
7218 /**
7219  * Server Validation Failed
7220  * @const 
7221  */
7222 Roo.form.Action.SERVER_INVALID = 'server';
7223  /**
7224  * Connect to Server Failed
7225  * @const 
7226  */
7227 Roo.form.Action.CONNECT_FAILURE = 'connect';
7228 /**
7229  * Reading Data from Server Failed
7230  * @const 
7231  */
7232 Roo.form.Action.LOAD_FAILURE = 'load';
7233
7234 Roo.form.Action.prototype = {
7235     type : 'default',
7236     failureType : undefined,
7237     response : undefined,
7238     result : undefined,
7239
7240     // interface method
7241     run : function(options){
7242
7243     },
7244
7245     // interface method
7246     success : function(response){
7247
7248     },
7249
7250     // interface method
7251     handleResponse : function(response){
7252
7253     },
7254
7255     // default connection failure
7256     failure : function(response){
7257         
7258         this.response = response;
7259         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7260         this.form.afterAction(this, false);
7261     },
7262
7263     processResponse : function(response){
7264         this.response = response;
7265         if(!response.responseText){
7266             return true;
7267         }
7268         this.result = this.handleResponse(response);
7269         return this.result;
7270     },
7271
7272     // utility functions used internally
7273     getUrl : function(appendParams){
7274         var url = this.options.url || this.form.url || this.form.el.dom.action;
7275         if(appendParams){
7276             var p = this.getParams();
7277             if(p){
7278                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7279             }
7280         }
7281         return url;
7282     },
7283
7284     getMethod : function(){
7285         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7286     },
7287
7288     getParams : function(){
7289         var bp = this.form.baseParams;
7290         var p = this.options.params;
7291         if(p){
7292             if(typeof p == "object"){
7293                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7294             }else if(typeof p == 'string' && bp){
7295                 p += '&' + Roo.urlEncode(bp);
7296             }
7297         }else if(bp){
7298             p = Roo.urlEncode(bp);
7299         }
7300         return p;
7301     },
7302
7303     createCallback : function(){
7304         return {
7305             success: this.success,
7306             failure: this.failure,
7307             scope: this,
7308             timeout: (this.form.timeout*1000),
7309             upload: this.form.fileUpload ? this.success : undefined
7310         };
7311     }
7312 };
7313
7314 Roo.form.Action.Submit = function(form, options){
7315     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7316 };
7317
7318 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7319     type : 'submit',
7320
7321     haveProgress : false,
7322     uploadComplete : false,
7323     
7324     // uploadProgress indicator.
7325     uploadProgress : function()
7326     {
7327         if (!this.form.progressUrl) {
7328             return;
7329         }
7330         
7331         if (!this.haveProgress) {
7332             Roo.MessageBox.progress("Uploading", "Uploading");
7333         }
7334         if (this.uploadComplete) {
7335            Roo.MessageBox.hide();
7336            return;
7337         }
7338         
7339         this.haveProgress = true;
7340    
7341         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7342         
7343         var c = new Roo.data.Connection();
7344         c.request({
7345             url : this.form.progressUrl,
7346             params: {
7347                 id : uid
7348             },
7349             method: 'GET',
7350             success : function(req){
7351                //console.log(data);
7352                 var rdata = false;
7353                 var edata;
7354                 try  {
7355                    rdata = Roo.decode(req.responseText)
7356                 } catch (e) {
7357                     Roo.log("Invalid data from server..");
7358                     Roo.log(edata);
7359                     return;
7360                 }
7361                 if (!rdata || !rdata.success) {
7362                     Roo.log(rdata);
7363                     Roo.MessageBox.alert(Roo.encode(rdata));
7364                     return;
7365                 }
7366                 var data = rdata.data;
7367                 
7368                 if (this.uploadComplete) {
7369                    Roo.MessageBox.hide();
7370                    return;
7371                 }
7372                    
7373                 if (data){
7374                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7375                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7376                     );
7377                 }
7378                 this.uploadProgress.defer(2000,this);
7379             },
7380        
7381             failure: function(data) {
7382                 Roo.log('progress url failed ');
7383                 Roo.log(data);
7384             },
7385             scope : this
7386         });
7387            
7388     },
7389     
7390     
7391     run : function()
7392     {
7393         // run get Values on the form, so it syncs any secondary forms.
7394         this.form.getValues();
7395         
7396         var o = this.options;
7397         var method = this.getMethod();
7398         var isPost = method == 'POST';
7399         if(o.clientValidation === false || this.form.isValid()){
7400             
7401             if (this.form.progressUrl) {
7402                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7403                     (new Date() * 1) + '' + Math.random());
7404                     
7405             } 
7406             
7407             
7408             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7409                 form:this.form.el.dom,
7410                 url:this.getUrl(!isPost),
7411                 method: method,
7412                 params:isPost ? this.getParams() : null,
7413                 isUpload: this.form.fileUpload
7414             }));
7415             
7416             this.uploadProgress();
7417
7418         }else if (o.clientValidation !== false){ // client validation failed
7419             this.failureType = Roo.form.Action.CLIENT_INVALID;
7420             this.form.afterAction(this, false);
7421         }
7422     },
7423
7424     success : function(response)
7425     {
7426         this.uploadComplete= true;
7427         if (this.haveProgress) {
7428             Roo.MessageBox.hide();
7429         }
7430         
7431         
7432         var result = this.processResponse(response);
7433         if(result === true || result.success){
7434             this.form.afterAction(this, true);
7435             return;
7436         }
7437         if(result.errors){
7438             this.form.markInvalid(result.errors);
7439             this.failureType = Roo.form.Action.SERVER_INVALID;
7440         }
7441         this.form.afterAction(this, false);
7442     },
7443     failure : function(response)
7444     {
7445         this.uploadComplete= true;
7446         if (this.haveProgress) {
7447             Roo.MessageBox.hide();
7448         }
7449         
7450         this.response = response;
7451         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7452         this.form.afterAction(this, false);
7453     },
7454     
7455     handleResponse : function(response){
7456         if(this.form.errorReader){
7457             var rs = this.form.errorReader.read(response);
7458             var errors = [];
7459             if(rs.records){
7460                 for(var i = 0, len = rs.records.length; i < len; i++) {
7461                     var r = rs.records[i];
7462                     errors[i] = r.data;
7463                 }
7464             }
7465             if(errors.length < 1){
7466                 errors = null;
7467             }
7468             return {
7469                 success : rs.success,
7470                 errors : errors
7471             };
7472         }
7473         var ret = false;
7474         try {
7475             ret = Roo.decode(response.responseText);
7476         } catch (e) {
7477             ret = {
7478                 success: false,
7479                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7480                 errors : []
7481             };
7482         }
7483         return ret;
7484         
7485     }
7486 });
7487
7488
7489 Roo.form.Action.Load = function(form, options){
7490     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7491     this.reader = this.form.reader;
7492 };
7493
7494 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7495     type : 'load',
7496
7497     run : function(){
7498         
7499         Roo.Ajax.request(Roo.apply(
7500                 this.createCallback(), {
7501                     method:this.getMethod(),
7502                     url:this.getUrl(false),
7503                     params:this.getParams()
7504         }));
7505     },
7506
7507     success : function(response){
7508         
7509         var result = this.processResponse(response);
7510         if(result === true || !result.success || !result.data){
7511             this.failureType = Roo.form.Action.LOAD_FAILURE;
7512             this.form.afterAction(this, false);
7513             return;
7514         }
7515         this.form.clearInvalid();
7516         this.form.setValues(result.data);
7517         this.form.afterAction(this, true);
7518     },
7519
7520     handleResponse : function(response){
7521         if(this.form.reader){
7522             var rs = this.form.reader.read(response);
7523             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7524             return {
7525                 success : rs.success,
7526                 data : data
7527             };
7528         }
7529         return Roo.decode(response.responseText);
7530     }
7531 });
7532
7533 Roo.form.Action.ACTION_TYPES = {
7534     'load' : Roo.form.Action.Load,
7535     'submit' : Roo.form.Action.Submit
7536 };/*
7537  * - LGPL
7538  *
7539  * form
7540  *
7541  */
7542
7543 /**
7544  * @class Roo.bootstrap.Form
7545  * @extends Roo.bootstrap.Component
7546  * Bootstrap Form class
7547  * @cfg {String} method  GET | POST (default POST)
7548  * @cfg {String} labelAlign top | left (default top)
7549  * @cfg {String} align left  | right - for navbars
7550  * @cfg {Boolean} loadMask load mask when submit (default true)
7551
7552  *
7553  * @constructor
7554  * Create a new Form
7555  * @param {Object} config The config object
7556  */
7557
7558
7559 Roo.bootstrap.Form = function(config){
7560     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7561     
7562     Roo.bootstrap.Form.popover.apply();
7563     
7564     this.addEvents({
7565         /**
7566          * @event clientvalidation
7567          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7568          * @param {Form} this
7569          * @param {Boolean} valid true if the form has passed client-side validation
7570          */
7571         clientvalidation: true,
7572         /**
7573          * @event beforeaction
7574          * Fires before any action is performed. Return false to cancel the action.
7575          * @param {Form} this
7576          * @param {Action} action The action to be performed
7577          */
7578         beforeaction: true,
7579         /**
7580          * @event actionfailed
7581          * Fires when an action fails.
7582          * @param {Form} this
7583          * @param {Action} action The action that failed
7584          */
7585         actionfailed : true,
7586         /**
7587          * @event actioncomplete
7588          * Fires when an action is completed.
7589          * @param {Form} this
7590          * @param {Action} action The action that completed
7591          */
7592         actioncomplete : true
7593     });
7594
7595 };
7596
7597 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7598
7599      /**
7600      * @cfg {String} method
7601      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7602      */
7603     method : 'POST',
7604     /**
7605      * @cfg {String} url
7606      * The URL to use for form actions if one isn't supplied in the action options.
7607      */
7608     /**
7609      * @cfg {Boolean} fileUpload
7610      * Set to true if this form is a file upload.
7611      */
7612
7613     /**
7614      * @cfg {Object} baseParams
7615      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7616      */
7617
7618     /**
7619      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7620      */
7621     timeout: 30,
7622     /**
7623      * @cfg {Sting} align (left|right) for navbar forms
7624      */
7625     align : 'left',
7626
7627     // private
7628     activeAction : null,
7629
7630     /**
7631      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7632      * element by passing it or its id or mask the form itself by passing in true.
7633      * @type Mixed
7634      */
7635     waitMsgTarget : false,
7636
7637     loadMask : true,
7638     
7639     /**
7640      * @cfg {Boolean} errorMask (true|false) default false
7641      */
7642     errorMask : false,
7643     
7644     /**
7645      * @cfg {Number} maskOffset Default 100
7646      */
7647     maskOffset : 100,
7648     
7649     /**
7650      * @cfg {Boolean} maskBody
7651      */
7652     maskBody : false,
7653
7654     getAutoCreate : function(){
7655
7656         var cfg = {
7657             tag: 'form',
7658             method : this.method || 'POST',
7659             id : this.id || Roo.id(),
7660             cls : ''
7661         };
7662         if (this.parent().xtype.match(/^Nav/)) {
7663             cfg.cls = 'navbar-form navbar-' + this.align;
7664
7665         }
7666
7667         if (this.labelAlign == 'left' ) {
7668             cfg.cls += ' form-horizontal';
7669         }
7670
7671
7672         return cfg;
7673     },
7674     initEvents : function()
7675     {
7676         this.el.on('submit', this.onSubmit, this);
7677         // this was added as random key presses on the form where triggering form submit.
7678         this.el.on('keypress', function(e) {
7679             if (e.getCharCode() != 13) {
7680                 return true;
7681             }
7682             // we might need to allow it for textareas.. and some other items.
7683             // check e.getTarget().
7684
7685             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7686                 return true;
7687             }
7688
7689             Roo.log("keypress blocked");
7690
7691             e.preventDefault();
7692             return false;
7693         });
7694         
7695     },
7696     // private
7697     onSubmit : function(e){
7698         e.stopEvent();
7699     },
7700
7701      /**
7702      * Returns true if client-side validation on the form is successful.
7703      * @return Boolean
7704      */
7705     isValid : function(){
7706         var items = this.getItems();
7707         var valid = true;
7708         var target = false;
7709         
7710         items.each(function(f){
7711             if(f.validate()){
7712                 return;
7713             }
7714             valid = false;
7715
7716             if(!target && f.el.isVisible(true)){
7717                 target = f;
7718             }
7719            
7720         });
7721         
7722         if(this.errorMask && !valid){
7723             Roo.bootstrap.Form.popover.mask(this, target);
7724         }
7725         
7726         return valid;
7727     },
7728     
7729     /**
7730      * Returns true if any fields in this form have changed since their original load.
7731      * @return Boolean
7732      */
7733     isDirty : function(){
7734         var dirty = false;
7735         var items = this.getItems();
7736         items.each(function(f){
7737            if(f.isDirty()){
7738                dirty = true;
7739                return false;
7740            }
7741            return true;
7742         });
7743         return dirty;
7744     },
7745      /**
7746      * Performs a predefined action (submit or load) or custom actions you define on this form.
7747      * @param {String} actionName The name of the action type
7748      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7749      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7750      * accept other config options):
7751      * <pre>
7752 Property          Type             Description
7753 ----------------  ---------------  ----------------------------------------------------------------------------------
7754 url               String           The url for the action (defaults to the form's url)
7755 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7756 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7757 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7758                                    validate the form on the client (defaults to false)
7759      * </pre>
7760      * @return {BasicForm} this
7761      */
7762     doAction : function(action, options){
7763         if(typeof action == 'string'){
7764             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7765         }
7766         if(this.fireEvent('beforeaction', this, action) !== false){
7767             this.beforeAction(action);
7768             action.run.defer(100, action);
7769         }
7770         return this;
7771     },
7772
7773     // private
7774     beforeAction : function(action){
7775         var o = action.options;
7776         
7777         if(this.loadMask){
7778             
7779             if(this.maskBody){
7780                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
7781             } else {
7782                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7783             }
7784         }
7785         // not really supported yet.. ??
7786
7787         //if(this.waitMsgTarget === true){
7788         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7789         //}else if(this.waitMsgTarget){
7790         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7791         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7792         //}else {
7793         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7794        // }
7795
7796     },
7797
7798     // private
7799     afterAction : function(action, success){
7800         this.activeAction = null;
7801         var o = action.options;
7802
7803         if(this.loadMask){
7804             
7805             if(this.maskBody){
7806                 Roo.get(document.body).unmask();
7807             } else {
7808                 this.el.unmask();
7809             }
7810         }
7811         
7812         //if(this.waitMsgTarget === true){
7813 //            this.el.unmask();
7814         //}else if(this.waitMsgTarget){
7815         //    this.waitMsgTarget.unmask();
7816         //}else{
7817         //    Roo.MessageBox.updateProgress(1);
7818         //    Roo.MessageBox.hide();
7819        // }
7820         //
7821         if(success){
7822             if(o.reset){
7823                 this.reset();
7824             }
7825             Roo.callback(o.success, o.scope, [this, action]);
7826             this.fireEvent('actioncomplete', this, action);
7827
7828         }else{
7829
7830             // failure condition..
7831             // we have a scenario where updates need confirming.
7832             // eg. if a locking scenario exists..
7833             // we look for { errors : { needs_confirm : true }} in the response.
7834             if (
7835                 (typeof(action.result) != 'undefined')  &&
7836                 (typeof(action.result.errors) != 'undefined')  &&
7837                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7838            ){
7839                 var _t = this;
7840                 Roo.log("not supported yet");
7841                  /*
7842
7843                 Roo.MessageBox.confirm(
7844                     "Change requires confirmation",
7845                     action.result.errorMsg,
7846                     function(r) {
7847                         if (r != 'yes') {
7848                             return;
7849                         }
7850                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7851                     }
7852
7853                 );
7854                 */
7855
7856
7857                 return;
7858             }
7859
7860             Roo.callback(o.failure, o.scope, [this, action]);
7861             // show an error message if no failed handler is set..
7862             if (!this.hasListener('actionfailed')) {
7863                 Roo.log("need to add dialog support");
7864                 /*
7865                 Roo.MessageBox.alert("Error",
7866                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7867                         action.result.errorMsg :
7868                         "Saving Failed, please check your entries or try again"
7869                 );
7870                 */
7871             }
7872
7873             this.fireEvent('actionfailed', this, action);
7874         }
7875
7876     },
7877     /**
7878      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7879      * @param {String} id The value to search for
7880      * @return Field
7881      */
7882     findField : function(id){
7883         var items = this.getItems();
7884         var field = items.get(id);
7885         if(!field){
7886              items.each(function(f){
7887                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7888                     field = f;
7889                     return false;
7890                 }
7891                 return true;
7892             });
7893         }
7894         return field || null;
7895     },
7896      /**
7897      * Mark fields in this form invalid in bulk.
7898      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7899      * @return {BasicForm} this
7900      */
7901     markInvalid : function(errors){
7902         if(errors instanceof Array){
7903             for(var i = 0, len = errors.length; i < len; i++){
7904                 var fieldError = errors[i];
7905                 var f = this.findField(fieldError.id);
7906                 if(f){
7907                     f.markInvalid(fieldError.msg);
7908                 }
7909             }
7910         }else{
7911             var field, id;
7912             for(id in errors){
7913                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7914                     field.markInvalid(errors[id]);
7915                 }
7916             }
7917         }
7918         //Roo.each(this.childForms || [], function (f) {
7919         //    f.markInvalid(errors);
7920         //});
7921
7922         return this;
7923     },
7924
7925     /**
7926      * Set values for fields in this form in bulk.
7927      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7928      * @return {BasicForm} this
7929      */
7930     setValues : function(values){
7931         if(values instanceof Array){ // array of objects
7932             for(var i = 0, len = values.length; i < len; i++){
7933                 var v = values[i];
7934                 var f = this.findField(v.id);
7935                 if(f){
7936                     f.setValue(v.value);
7937                     if(this.trackResetOnLoad){
7938                         f.originalValue = f.getValue();
7939                     }
7940                 }
7941             }
7942         }else{ // object hash
7943             var field, id;
7944             for(id in values){
7945                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7946
7947                     if (field.setFromData &&
7948                         field.valueField &&
7949                         field.displayField &&
7950                         // combos' with local stores can
7951                         // be queried via setValue()
7952                         // to set their value..
7953                         (field.store && !field.store.isLocal)
7954                         ) {
7955                         // it's a combo
7956                         var sd = { };
7957                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7958                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7959                         field.setFromData(sd);
7960
7961                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
7962                         
7963                         field.setFromData(values);
7964                         
7965                     } else {
7966                         field.setValue(values[id]);
7967                     }
7968
7969
7970                     if(this.trackResetOnLoad){
7971                         field.originalValue = field.getValue();
7972                     }
7973                 }
7974             }
7975         }
7976
7977         //Roo.each(this.childForms || [], function (f) {
7978         //    f.setValues(values);
7979         //});
7980
7981         return this;
7982     },
7983
7984     /**
7985      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7986      * they are returned as an array.
7987      * @param {Boolean} asString
7988      * @return {Object}
7989      */
7990     getValues : function(asString){
7991         //if (this.childForms) {
7992             // copy values from the child forms
7993         //    Roo.each(this.childForms, function (f) {
7994         //        this.setValues(f.getValues());
7995         //    }, this);
7996         //}
7997
7998
7999
8000         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
8001         if(asString === true){
8002             return fs;
8003         }
8004         return Roo.urlDecode(fs);
8005     },
8006
8007     /**
8008      * Returns the fields in this form as an object with key/value pairs.
8009      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
8010      * @return {Object}
8011      */
8012     getFieldValues : function(with_hidden)
8013     {
8014         var items = this.getItems();
8015         var ret = {};
8016         items.each(function(f){
8017             
8018             if (!f.getName()) {
8019                 return;
8020             }
8021             
8022             var v = f.getValue();
8023             
8024             if (f.inputType =='radio') {
8025                 if (typeof(ret[f.getName()]) == 'undefined') {
8026                     ret[f.getName()] = ''; // empty..
8027                 }
8028
8029                 if (!f.el.dom.checked) {
8030                     return;
8031
8032                 }
8033                 v = f.el.dom.value;
8034
8035             }
8036             
8037             if(f.xtype == 'MoneyField'){
8038                 ret[f.currencyName] = f.getCurrency();
8039             }
8040
8041             // not sure if this supported any more..
8042             if ((typeof(v) == 'object') && f.getRawValue) {
8043                 v = f.getRawValue() ; // dates..
8044             }
8045             // combo boxes where name != hiddenName...
8046             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8047                 ret[f.name] = f.getRawValue();
8048             }
8049             ret[f.getName()] = v;
8050         });
8051
8052         return ret;
8053     },
8054
8055     /**
8056      * Clears all invalid messages in this form.
8057      * @return {BasicForm} this
8058      */
8059     clearInvalid : function(){
8060         var items = this.getItems();
8061
8062         items.each(function(f){
8063            f.clearInvalid();
8064         });
8065
8066         return this;
8067     },
8068
8069     /**
8070      * Resets this form.
8071      * @return {BasicForm} this
8072      */
8073     reset : function(){
8074         var items = this.getItems();
8075         items.each(function(f){
8076             f.reset();
8077         });
8078
8079         Roo.each(this.childForms || [], function (f) {
8080             f.reset();
8081         });
8082
8083
8084         return this;
8085     },
8086     
8087     getItems : function()
8088     {
8089         var r=new Roo.util.MixedCollection(false, function(o){
8090             return o.id || (o.id = Roo.id());
8091         });
8092         var iter = function(el) {
8093             if (el.inputEl) {
8094                 r.add(el);
8095             }
8096             if (!el.items) {
8097                 return;
8098             }
8099             Roo.each(el.items,function(e) {
8100                 iter(e);
8101             });
8102
8103
8104         };
8105
8106         iter(this);
8107         return r;
8108         
8109     }
8110
8111 });
8112
8113 Roo.apply(Roo.bootstrap.Form, {
8114     
8115     popover : {
8116         
8117         padding : 5,
8118         
8119         isApplied : false,
8120         
8121         isMasked : false,
8122         
8123         form : false,
8124         
8125         target : false,
8126         
8127         toolTip : false,
8128         
8129         intervalID : false,
8130         
8131         maskEl : false,
8132         
8133         apply : function()
8134         {
8135             if(this.isApplied){
8136                 return;
8137             }
8138             
8139             this.maskEl = {
8140                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8141                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8142                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8143                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8144             };
8145             
8146             this.maskEl.top.enableDisplayMode("block");
8147             this.maskEl.left.enableDisplayMode("block");
8148             this.maskEl.bottom.enableDisplayMode("block");
8149             this.maskEl.right.enableDisplayMode("block");
8150             
8151             this.toolTip = new Roo.bootstrap.Tooltip({
8152                 cls : 'roo-form-error-popover',
8153                 alignment : {
8154                     'left' : ['r-l', [-2,0], 'right'],
8155                     'right' : ['l-r', [2,0], 'left'],
8156                     'bottom' : ['tl-bl', [0,2], 'top'],
8157                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8158                 }
8159             });
8160             
8161             this.toolTip.render(Roo.get(document.body));
8162
8163             this.toolTip.el.enableDisplayMode("block");
8164             
8165             Roo.get(document.body).on('click', function(){
8166                 this.unmask();
8167             }, this);
8168             
8169             Roo.get(document.body).on('touchstart', function(){
8170                 this.unmask();
8171             }, this);
8172             
8173             this.isApplied = true
8174         },
8175         
8176         mask : function(form, target)
8177         {
8178             this.form = form;
8179             
8180             this.target = target;
8181             
8182             if(!this.form.errorMask || !target.el){
8183                 return;
8184             }
8185             
8186             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8187             
8188             Roo.log(scrollable);
8189             
8190             var ot = this.target.el.calcOffsetsTo(scrollable);
8191             
8192             var scrollTo = ot[1] - this.form.maskOffset;
8193             
8194             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8195             
8196             scrollable.scrollTo('top', scrollTo);
8197             
8198             var box = this.target.el.getBox();
8199             Roo.log(box);
8200             var zIndex = Roo.bootstrap.Modal.zIndex++;
8201
8202             
8203             this.maskEl.top.setStyle('position', 'absolute');
8204             this.maskEl.top.setStyle('z-index', zIndex);
8205             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8206             this.maskEl.top.setLeft(0);
8207             this.maskEl.top.setTop(0);
8208             this.maskEl.top.show();
8209             
8210             this.maskEl.left.setStyle('position', 'absolute');
8211             this.maskEl.left.setStyle('z-index', zIndex);
8212             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8213             this.maskEl.left.setLeft(0);
8214             this.maskEl.left.setTop(box.y - this.padding);
8215             this.maskEl.left.show();
8216
8217             this.maskEl.bottom.setStyle('position', 'absolute');
8218             this.maskEl.bottom.setStyle('z-index', zIndex);
8219             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8220             this.maskEl.bottom.setLeft(0);
8221             this.maskEl.bottom.setTop(box.bottom + this.padding);
8222             this.maskEl.bottom.show();
8223
8224             this.maskEl.right.setStyle('position', 'absolute');
8225             this.maskEl.right.setStyle('z-index', zIndex);
8226             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8227             this.maskEl.right.setLeft(box.right + this.padding);
8228             this.maskEl.right.setTop(box.y - this.padding);
8229             this.maskEl.right.show();
8230
8231             this.toolTip.bindEl = this.target.el;
8232
8233             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8234
8235             var tip = this.target.blankText;
8236
8237             if(this.target.getValue() !== '' ) {
8238                 
8239                 if (this.target.invalidText.length) {
8240                     tip = this.target.invalidText;
8241                 } else if (this.target.regexText.length){
8242                     tip = this.target.regexText;
8243                 }
8244             }
8245
8246             this.toolTip.show(tip);
8247
8248             this.intervalID = window.setInterval(function() {
8249                 Roo.bootstrap.Form.popover.unmask();
8250             }, 10000);
8251
8252             window.onwheel = function(){ return false;};
8253             
8254             (function(){ this.isMasked = true; }).defer(500, this);
8255             
8256         },
8257         
8258         unmask : function()
8259         {
8260             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8261                 return;
8262             }
8263             
8264             this.maskEl.top.setStyle('position', 'absolute');
8265             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8266             this.maskEl.top.hide();
8267
8268             this.maskEl.left.setStyle('position', 'absolute');
8269             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8270             this.maskEl.left.hide();
8271
8272             this.maskEl.bottom.setStyle('position', 'absolute');
8273             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8274             this.maskEl.bottom.hide();
8275
8276             this.maskEl.right.setStyle('position', 'absolute');
8277             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8278             this.maskEl.right.hide();
8279             
8280             this.toolTip.hide();
8281             
8282             this.toolTip.el.hide();
8283             
8284             window.onwheel = function(){ return true;};
8285             
8286             if(this.intervalID){
8287                 window.clearInterval(this.intervalID);
8288                 this.intervalID = false;
8289             }
8290             
8291             this.isMasked = false;
8292             
8293         }
8294         
8295     }
8296     
8297 });
8298
8299 /*
8300  * Based on:
8301  * Ext JS Library 1.1.1
8302  * Copyright(c) 2006-2007, Ext JS, LLC.
8303  *
8304  * Originally Released Under LGPL - original licence link has changed is not relivant.
8305  *
8306  * Fork - LGPL
8307  * <script type="text/javascript">
8308  */
8309 /**
8310  * @class Roo.form.VTypes
8311  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8312  * @singleton
8313  */
8314 Roo.form.VTypes = function(){
8315     // closure these in so they are only created once.
8316     var alpha = /^[a-zA-Z_]+$/;
8317     var alphanum = /^[a-zA-Z0-9_]+$/;
8318     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8319     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8320
8321     // All these messages and functions are configurable
8322     return {
8323         /**
8324          * The function used to validate email addresses
8325          * @param {String} value The email address
8326          */
8327         'email' : function(v){
8328             return email.test(v);
8329         },
8330         /**
8331          * The error text to display when the email validation function returns false
8332          * @type String
8333          */
8334         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8335         /**
8336          * The keystroke filter mask to be applied on email input
8337          * @type RegExp
8338          */
8339         'emailMask' : /[a-z0-9_\.\-@]/i,
8340
8341         /**
8342          * The function used to validate URLs
8343          * @param {String} value The URL
8344          */
8345         'url' : function(v){
8346             return url.test(v);
8347         },
8348         /**
8349          * The error text to display when the url validation function returns false
8350          * @type String
8351          */
8352         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8353         
8354         /**
8355          * The function used to validate alpha values
8356          * @param {String} value The value
8357          */
8358         'alpha' : function(v){
8359             return alpha.test(v);
8360         },
8361         /**
8362          * The error text to display when the alpha validation function returns false
8363          * @type String
8364          */
8365         'alphaText' : 'This field should only contain letters and _',
8366         /**
8367          * The keystroke filter mask to be applied on alpha input
8368          * @type RegExp
8369          */
8370         'alphaMask' : /[a-z_]/i,
8371
8372         /**
8373          * The function used to validate alphanumeric values
8374          * @param {String} value The value
8375          */
8376         'alphanum' : function(v){
8377             return alphanum.test(v);
8378         },
8379         /**
8380          * The error text to display when the alphanumeric validation function returns false
8381          * @type String
8382          */
8383         'alphanumText' : 'This field should only contain letters, numbers and _',
8384         /**
8385          * The keystroke filter mask to be applied on alphanumeric input
8386          * @type RegExp
8387          */
8388         'alphanumMask' : /[a-z0-9_]/i
8389     };
8390 }();/*
8391  * - LGPL
8392  *
8393  * Input
8394  * 
8395  */
8396
8397 /**
8398  * @class Roo.bootstrap.Input
8399  * @extends Roo.bootstrap.Component
8400  * Bootstrap Input class
8401  * @cfg {Boolean} disabled is it disabled
8402  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8403  * @cfg {String} name name of the input
8404  * @cfg {string} fieldLabel - the label associated
8405  * @cfg {string} placeholder - placeholder to put in text.
8406  * @cfg {string}  before - input group add on before
8407  * @cfg {string} after - input group add on after
8408  * @cfg {string} size - (lg|sm) or leave empty..
8409  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8410  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8411  * @cfg {Number} md colspan out of 12 for computer-sized screens
8412  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8413  * @cfg {string} value default value of the input
8414  * @cfg {Number} labelWidth set the width of label 
8415  * @cfg {Number} labellg set the width of label (1-12)
8416  * @cfg {Number} labelmd set the width of label (1-12)
8417  * @cfg {Number} labelsm set the width of label (1-12)
8418  * @cfg {Number} labelxs set the width of label (1-12)
8419  * @cfg {String} labelAlign (top|left)
8420  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8421  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8422  * @cfg {String} indicatorpos (left|right) default left
8423
8424  * @cfg {String} align (left|center|right) Default left
8425  * @cfg {Boolean} forceFeedback (true|false) Default false
8426  * 
8427  * 
8428  * 
8429  * 
8430  * @constructor
8431  * Create a new Input
8432  * @param {Object} config The config object
8433  */
8434
8435 Roo.bootstrap.Input = function(config){
8436     
8437     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8438     
8439     this.addEvents({
8440         /**
8441          * @event focus
8442          * Fires when this field receives input focus.
8443          * @param {Roo.form.Field} this
8444          */
8445         focus : true,
8446         /**
8447          * @event blur
8448          * Fires when this field loses input focus.
8449          * @param {Roo.form.Field} this
8450          */
8451         blur : true,
8452         /**
8453          * @event specialkey
8454          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8455          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8456          * @param {Roo.form.Field} this
8457          * @param {Roo.EventObject} e The event object
8458          */
8459         specialkey : true,
8460         /**
8461          * @event change
8462          * Fires just before the field blurs if the field value has changed.
8463          * @param {Roo.form.Field} this
8464          * @param {Mixed} newValue The new value
8465          * @param {Mixed} oldValue The original value
8466          */
8467         change : true,
8468         /**
8469          * @event invalid
8470          * Fires after the field has been marked as invalid.
8471          * @param {Roo.form.Field} this
8472          * @param {String} msg The validation message
8473          */
8474         invalid : true,
8475         /**
8476          * @event valid
8477          * Fires after the field has been validated with no errors.
8478          * @param {Roo.form.Field} this
8479          */
8480         valid : true,
8481          /**
8482          * @event keyup
8483          * Fires after the key up
8484          * @param {Roo.form.Field} this
8485          * @param {Roo.EventObject}  e The event Object
8486          */
8487         keyup : true
8488     });
8489 };
8490
8491 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8492      /**
8493      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8494       automatic validation (defaults to "keyup").
8495      */
8496     validationEvent : "keyup",
8497      /**
8498      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8499      */
8500     validateOnBlur : true,
8501     /**
8502      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8503      */
8504     validationDelay : 250,
8505      /**
8506      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8507      */
8508     focusClass : "x-form-focus",  // not needed???
8509     
8510        
8511     /**
8512      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8513      */
8514     invalidClass : "has-warning",
8515     
8516     /**
8517      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8518      */
8519     validClass : "has-success",
8520     
8521     /**
8522      * @cfg {Boolean} hasFeedback (true|false) default true
8523      */
8524     hasFeedback : true,
8525     
8526     /**
8527      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8528      */
8529     invalidFeedbackClass : "glyphicon-warning-sign",
8530     
8531     /**
8532      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8533      */
8534     validFeedbackClass : "glyphicon-ok",
8535     
8536     /**
8537      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8538      */
8539     selectOnFocus : false,
8540     
8541      /**
8542      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8543      */
8544     maskRe : null,
8545        /**
8546      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8547      */
8548     vtype : null,
8549     
8550       /**
8551      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8552      */
8553     disableKeyFilter : false,
8554     
8555        /**
8556      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8557      */
8558     disabled : false,
8559      /**
8560      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8561      */
8562     allowBlank : true,
8563     /**
8564      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8565      */
8566     blankText : "Please complete this mandatory field",
8567     
8568      /**
8569      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8570      */
8571     minLength : 0,
8572     /**
8573      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8574      */
8575     maxLength : Number.MAX_VALUE,
8576     /**
8577      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8578      */
8579     minLengthText : "The minimum length for this field is {0}",
8580     /**
8581      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8582      */
8583     maxLengthText : "The maximum length for this field is {0}",
8584   
8585     
8586     /**
8587      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8588      * If available, this function will be called only after the basic validators all return true, and will be passed the
8589      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8590      */
8591     validator : null,
8592     /**
8593      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8594      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8595      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8596      */
8597     regex : null,
8598     /**
8599      * @cfg {String} regexText -- Depricated - use Invalid Text
8600      */
8601     regexText : "",
8602     
8603     /**
8604      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8605      */
8606     invalidText : "",
8607     
8608     
8609     
8610     autocomplete: false,
8611     
8612     
8613     fieldLabel : '',
8614     inputType : 'text',
8615     
8616     name : false,
8617     placeholder: false,
8618     before : false,
8619     after : false,
8620     size : false,
8621     hasFocus : false,
8622     preventMark: false,
8623     isFormField : true,
8624     value : '',
8625     labelWidth : 2,
8626     labelAlign : false,
8627     readOnly : false,
8628     align : false,
8629     formatedValue : false,
8630     forceFeedback : false,
8631     
8632     indicatorpos : 'left',
8633     
8634     labellg : 0,
8635     labelmd : 0,
8636     labelsm : 0,
8637     labelxs : 0,
8638     
8639     parentLabelAlign : function()
8640     {
8641         var parent = this;
8642         while (parent.parent()) {
8643             parent = parent.parent();
8644             if (typeof(parent.labelAlign) !='undefined') {
8645                 return parent.labelAlign;
8646             }
8647         }
8648         return 'left';
8649         
8650     },
8651     
8652     getAutoCreate : function()
8653     {
8654         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8655         
8656         var id = Roo.id();
8657         
8658         var cfg = {};
8659         
8660         if(this.inputType != 'hidden'){
8661             cfg.cls = 'form-group' //input-group
8662         }
8663         
8664         var input =  {
8665             tag: 'input',
8666             id : id,
8667             type : this.inputType,
8668             value : this.value,
8669             cls : 'form-control',
8670             placeholder : this.placeholder || '',
8671             autocomplete : this.autocomplete || 'new-password'
8672         };
8673         
8674         if(this.align){
8675             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8676         }
8677         
8678         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8679             input.maxLength = this.maxLength;
8680         }
8681         
8682         if (this.disabled) {
8683             input.disabled=true;
8684         }
8685         
8686         if (this.readOnly) {
8687             input.readonly=true;
8688         }
8689         
8690         if (this.name) {
8691             input.name = this.name;
8692         }
8693         
8694         if (this.size) {
8695             input.cls += ' input-' + this.size;
8696         }
8697         
8698         var settings=this;
8699         ['xs','sm','md','lg'].map(function(size){
8700             if (settings[size]) {
8701                 cfg.cls += ' col-' + size + '-' + settings[size];
8702             }
8703         });
8704         
8705         var inputblock = input;
8706         
8707         var feedback = {
8708             tag: 'span',
8709             cls: 'glyphicon form-control-feedback'
8710         };
8711             
8712         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8713             
8714             inputblock = {
8715                 cls : 'has-feedback',
8716                 cn :  [
8717                     input,
8718                     feedback
8719                 ] 
8720             };  
8721         }
8722         
8723         if (this.before || this.after) {
8724             
8725             inputblock = {
8726                 cls : 'input-group',
8727                 cn :  [] 
8728             };
8729             
8730             if (this.before && typeof(this.before) == 'string') {
8731                 
8732                 inputblock.cn.push({
8733                     tag :'span',
8734                     cls : 'roo-input-before input-group-addon',
8735                     html : this.before
8736                 });
8737             }
8738             if (this.before && typeof(this.before) == 'object') {
8739                 this.before = Roo.factory(this.before);
8740                 
8741                 inputblock.cn.push({
8742                     tag :'span',
8743                     cls : 'roo-input-before input-group-' +
8744                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8745                 });
8746             }
8747             
8748             inputblock.cn.push(input);
8749             
8750             if (this.after && typeof(this.after) == 'string') {
8751                 inputblock.cn.push({
8752                     tag :'span',
8753                     cls : 'roo-input-after input-group-addon',
8754                     html : this.after
8755                 });
8756             }
8757             if (this.after && typeof(this.after) == 'object') {
8758                 this.after = Roo.factory(this.after);
8759                 
8760                 inputblock.cn.push({
8761                     tag :'span',
8762                     cls : 'roo-input-after input-group-' +
8763                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8764                 });
8765             }
8766             
8767             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8768                 inputblock.cls += ' has-feedback';
8769                 inputblock.cn.push(feedback);
8770             }
8771         };
8772         
8773         if (align ==='left' && this.fieldLabel.length) {
8774             
8775             cfg.cls += ' roo-form-group-label-left';
8776             
8777             cfg.cn = [
8778                 {
8779                     tag : 'i',
8780                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8781                     tooltip : 'This field is required'
8782                 },
8783                 {
8784                     tag: 'label',
8785                     'for' :  id,
8786                     cls : 'control-label',
8787                     html : this.fieldLabel
8788
8789                 },
8790                 {
8791                     cls : "", 
8792                     cn: [
8793                         inputblock
8794                     ]
8795                 }
8796             ];
8797             
8798             var labelCfg = cfg.cn[1];
8799             var contentCfg = cfg.cn[2];
8800             
8801             if(this.indicatorpos == 'right'){
8802                 cfg.cn = [
8803                     {
8804                         tag: 'label',
8805                         'for' :  id,
8806                         cls : 'control-label',
8807                         cn : [
8808                             {
8809                                 tag : 'span',
8810                                 html : this.fieldLabel
8811                             },
8812                             {
8813                                 tag : 'i',
8814                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8815                                 tooltip : 'This field is required'
8816                             }
8817                         ]
8818                     },
8819                     {
8820                         cls : "",
8821                         cn: [
8822                             inputblock
8823                         ]
8824                     }
8825
8826                 ];
8827                 
8828                 labelCfg = cfg.cn[0];
8829                 contentCfg = cfg.cn[1];
8830             
8831             }
8832             
8833             if(this.labelWidth > 12){
8834                 labelCfg.style = "width: " + this.labelWidth + 'px';
8835             }
8836             
8837             if(this.labelWidth < 13 && this.labelmd == 0){
8838                 this.labelmd = this.labelWidth;
8839             }
8840             
8841             if(this.labellg > 0){
8842                 labelCfg.cls += ' col-lg-' + this.labellg;
8843                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
8844             }
8845             
8846             if(this.labelmd > 0){
8847                 labelCfg.cls += ' col-md-' + this.labelmd;
8848                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
8849             }
8850             
8851             if(this.labelsm > 0){
8852                 labelCfg.cls += ' col-sm-' + this.labelsm;
8853                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
8854             }
8855             
8856             if(this.labelxs > 0){
8857                 labelCfg.cls += ' col-xs-' + this.labelxs;
8858                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
8859             }
8860             
8861             
8862         } else if ( this.fieldLabel.length) {
8863                 
8864             cfg.cn = [
8865                 {
8866                     tag : 'i',
8867                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8868                     tooltip : 'This field is required'
8869                 },
8870                 {
8871                     tag: 'label',
8872                    //cls : 'input-group-addon',
8873                     html : this.fieldLabel
8874
8875                 },
8876
8877                inputblock
8878
8879            ];
8880            
8881            if(this.indicatorpos == 'right'){
8882                 
8883                 cfg.cn = [
8884                     {
8885                         tag: 'label',
8886                        //cls : 'input-group-addon',
8887                         html : this.fieldLabel
8888
8889                     },
8890                     {
8891                         tag : 'i',
8892                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8893                         tooltip : 'This field is required'
8894                     },
8895
8896                    inputblock
8897
8898                ];
8899
8900             }
8901
8902         } else {
8903             
8904             cfg.cn = [
8905
8906                     inputblock
8907
8908             ];
8909                 
8910                 
8911         };
8912         
8913         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8914            cfg.cls += ' navbar-form';
8915         }
8916         
8917         if (this.parentType === 'NavGroup') {
8918            cfg.cls += ' navbar-form';
8919            cfg.tag = 'li';
8920         }
8921         
8922         return cfg;
8923         
8924     },
8925     /**
8926      * return the real input element.
8927      */
8928     inputEl: function ()
8929     {
8930         return this.el.select('input.form-control',true).first();
8931     },
8932     
8933     tooltipEl : function()
8934     {
8935         return this.inputEl();
8936     },
8937     
8938     indicatorEl : function()
8939     {
8940         var indicator = this.el.select('i.roo-required-indicator',true).first();
8941         
8942         if(!indicator){
8943             return false;
8944         }
8945         
8946         return indicator;
8947         
8948     },
8949     
8950     setDisabled : function(v)
8951     {
8952         var i  = this.inputEl().dom;
8953         if (!v) {
8954             i.removeAttribute('disabled');
8955             return;
8956             
8957         }
8958         i.setAttribute('disabled','true');
8959     },
8960     initEvents : function()
8961     {
8962           
8963         this.inputEl().on("keydown" , this.fireKey,  this);
8964         this.inputEl().on("focus", this.onFocus,  this);
8965         this.inputEl().on("blur", this.onBlur,  this);
8966         
8967         this.inputEl().relayEvent('keyup', this);
8968         
8969         this.indicator = this.indicatorEl();
8970         
8971         if(this.indicator){
8972             this.indicator.addClass('invisible');
8973             
8974         }
8975  
8976         // reference to original value for reset
8977         this.originalValue = this.getValue();
8978         //Roo.form.TextField.superclass.initEvents.call(this);
8979         if(this.validationEvent == 'keyup'){
8980             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8981             this.inputEl().on('keyup', this.filterValidation, this);
8982         }
8983         else if(this.validationEvent !== false){
8984             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8985         }
8986         
8987         if(this.selectOnFocus){
8988             this.on("focus", this.preFocus, this);
8989             
8990         }
8991         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8992             this.inputEl().on("keypress", this.filterKeys, this);
8993         } else {
8994             this.inputEl().relayEvent('keypress', this);
8995         }
8996        /* if(this.grow){
8997             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8998             this.el.on("click", this.autoSize,  this);
8999         }
9000         */
9001         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9002             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9003         }
9004         
9005         if (typeof(this.before) == 'object') {
9006             this.before.render(this.el.select('.roo-input-before',true).first());
9007         }
9008         if (typeof(this.after) == 'object') {
9009             this.after.render(this.el.select('.roo-input-after',true).first());
9010         }
9011         
9012         
9013     },
9014     filterValidation : function(e){
9015         if(!e.isNavKeyPress()){
9016             this.validationTask.delay(this.validationDelay);
9017         }
9018     },
9019      /**
9020      * Validates the field value
9021      * @return {Boolean} True if the value is valid, else false
9022      */
9023     validate : function(){
9024         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9025         if(this.disabled || this.validateValue(this.getRawValue())){
9026             this.markValid();
9027             return true;
9028         }
9029         
9030         this.markInvalid();
9031         return false;
9032     },
9033     
9034     
9035     /**
9036      * Validates a value according to the field's validation rules and marks the field as invalid
9037      * if the validation fails
9038      * @param {Mixed} value The value to validate
9039      * @return {Boolean} True if the value is valid, else false
9040      */
9041     validateValue : function(value){
9042         if(value.length < 1)  { // if it's blank
9043             if(this.allowBlank){
9044                 return true;
9045             }            
9046             return this.inputEl().hasClass('hide') ? true : false;
9047         }
9048         
9049         if(value.length < this.minLength){
9050             return false;
9051         }
9052         if(value.length > this.maxLength){
9053             return false;
9054         }
9055         if(this.vtype){
9056             var vt = Roo.form.VTypes;
9057             if(!vt[this.vtype](value, this)){
9058                 return false;
9059             }
9060         }
9061         if(typeof this.validator == "function"){
9062             var msg = this.validator(value);
9063             if(msg !== true){
9064                 return false;
9065             }
9066             if (typeof(msg) == 'string') {
9067                 this.invalidText = msg;
9068             }
9069         }
9070         
9071         if(this.regex && !this.regex.test(value)){
9072             return false;
9073         }
9074         
9075         return true;
9076     },
9077
9078     
9079     
9080      // private
9081     fireKey : function(e){
9082         //Roo.log('field ' + e.getKey());
9083         if(e.isNavKeyPress()){
9084             this.fireEvent("specialkey", this, e);
9085         }
9086     },
9087     focus : function (selectText){
9088         if(this.rendered){
9089             this.inputEl().focus();
9090             if(selectText === true){
9091                 this.inputEl().dom.select();
9092             }
9093         }
9094         return this;
9095     } ,
9096     
9097     onFocus : function(){
9098         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9099            // this.el.addClass(this.focusClass);
9100         }
9101         if(!this.hasFocus){
9102             this.hasFocus = true;
9103             this.startValue = this.getValue();
9104             this.fireEvent("focus", this);
9105         }
9106     },
9107     
9108     beforeBlur : Roo.emptyFn,
9109
9110     
9111     // private
9112     onBlur : function(){
9113         this.beforeBlur();
9114         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9115             //this.el.removeClass(this.focusClass);
9116         }
9117         this.hasFocus = false;
9118         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9119             this.validate();
9120         }
9121         var v = this.getValue();
9122         if(String(v) !== String(this.startValue)){
9123             this.fireEvent('change', this, v, this.startValue);
9124         }
9125         this.fireEvent("blur", this);
9126     },
9127     
9128     /**
9129      * Resets the current field value to the originally loaded value and clears any validation messages
9130      */
9131     reset : function(){
9132         this.setValue(this.originalValue);
9133         this.validate();
9134     },
9135      /**
9136      * Returns the name of the field
9137      * @return {Mixed} name The name field
9138      */
9139     getName: function(){
9140         return this.name;
9141     },
9142      /**
9143      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9144      * @return {Mixed} value The field value
9145      */
9146     getValue : function(){
9147         
9148         var v = this.inputEl().getValue();
9149         
9150         return v;
9151     },
9152     /**
9153      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9154      * @return {Mixed} value The field value
9155      */
9156     getRawValue : function(){
9157         var v = this.inputEl().getValue();
9158         
9159         return v;
9160     },
9161     
9162     /**
9163      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9164      * @param {Mixed} value The value to set
9165      */
9166     setRawValue : function(v){
9167         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9168     },
9169     
9170     selectText : function(start, end){
9171         var v = this.getRawValue();
9172         if(v.length > 0){
9173             start = start === undefined ? 0 : start;
9174             end = end === undefined ? v.length : end;
9175             var d = this.inputEl().dom;
9176             if(d.setSelectionRange){
9177                 d.setSelectionRange(start, end);
9178             }else if(d.createTextRange){
9179                 var range = d.createTextRange();
9180                 range.moveStart("character", start);
9181                 range.moveEnd("character", v.length-end);
9182                 range.select();
9183             }
9184         }
9185     },
9186     
9187     /**
9188      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9189      * @param {Mixed} value The value to set
9190      */
9191     setValue : function(v){
9192         this.value = v;
9193         if(this.rendered){
9194             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9195             this.validate();
9196         }
9197     },
9198     
9199     /*
9200     processValue : function(value){
9201         if(this.stripCharsRe){
9202             var newValue = value.replace(this.stripCharsRe, '');
9203             if(newValue !== value){
9204                 this.setRawValue(newValue);
9205                 return newValue;
9206             }
9207         }
9208         return value;
9209     },
9210   */
9211     preFocus : function(){
9212         
9213         if(this.selectOnFocus){
9214             this.inputEl().dom.select();
9215         }
9216     },
9217     filterKeys : function(e){
9218         var k = e.getKey();
9219         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9220             return;
9221         }
9222         var c = e.getCharCode(), cc = String.fromCharCode(c);
9223         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9224             return;
9225         }
9226         if(!this.maskRe.test(cc)){
9227             e.stopEvent();
9228         }
9229     },
9230      /**
9231      * Clear any invalid styles/messages for this field
9232      */
9233     clearInvalid : function(){
9234         
9235         if(!this.el || this.preventMark){ // not rendered
9236             return;
9237         }
9238         
9239      
9240         this.el.removeClass(this.invalidClass);
9241         
9242         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9243             
9244             var feedback = this.el.select('.form-control-feedback', true).first();
9245             
9246             if(feedback){
9247                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9248             }
9249             
9250         }
9251         
9252         this.fireEvent('valid', this);
9253     },
9254     
9255      /**
9256      * Mark this field as valid
9257      */
9258     markValid : function()
9259     {
9260         if(!this.el  || this.preventMark){ // not rendered...
9261             return;
9262         }
9263         
9264         this.el.removeClass([this.invalidClass, this.validClass]);
9265         
9266         var feedback = this.el.select('.form-control-feedback', true).first();
9267             
9268         if(feedback){
9269             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9270         }
9271         
9272         if(this.indicator){
9273             this.indicator.removeClass('visible');
9274             this.indicator.addClass('invisible');
9275         }
9276         
9277         if(this.disabled){
9278             return;
9279         }
9280         
9281         if(this.allowBlank && !this.getRawValue().length){
9282             return;
9283         }
9284         
9285         this.el.addClass(this.validClass);
9286         
9287         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9288             
9289             var feedback = this.el.select('.form-control-feedback', true).first();
9290             
9291             if(feedback){
9292                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9293                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9294             }
9295             
9296         }
9297         
9298         this.fireEvent('valid', this);
9299     },
9300     
9301      /**
9302      * Mark this field as invalid
9303      * @param {String} msg The validation message
9304      */
9305     markInvalid : function(msg)
9306     {
9307         if(!this.el  || this.preventMark){ // not rendered
9308             return;
9309         }
9310         
9311         this.el.removeClass([this.invalidClass, this.validClass]);
9312         
9313         var feedback = this.el.select('.form-control-feedback', true).first();
9314             
9315         if(feedback){
9316             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9317         }
9318
9319         if(this.disabled){
9320             return;
9321         }
9322         
9323         if(this.allowBlank && !this.getRawValue().length){
9324             return;
9325         }
9326         
9327         if(this.indicator){
9328             this.indicator.removeClass('invisible');
9329             this.indicator.addClass('visible');
9330         }
9331         
9332         this.el.addClass(this.invalidClass);
9333         
9334         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9335             
9336             var feedback = this.el.select('.form-control-feedback', true).first();
9337             
9338             if(feedback){
9339                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9340                 
9341                 if(this.getValue().length || this.forceFeedback){
9342                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9343                 }
9344                 
9345             }
9346             
9347         }
9348         
9349         this.fireEvent('invalid', this, msg);
9350     },
9351     // private
9352     SafariOnKeyDown : function(event)
9353     {
9354         // this is a workaround for a password hang bug on chrome/ webkit.
9355         if (this.inputEl().dom.type != 'password') {
9356             return;
9357         }
9358         
9359         var isSelectAll = false;
9360         
9361         if(this.inputEl().dom.selectionEnd > 0){
9362             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9363         }
9364         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9365             event.preventDefault();
9366             this.setValue('');
9367             return;
9368         }
9369         
9370         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9371             
9372             event.preventDefault();
9373             // this is very hacky as keydown always get's upper case.
9374             //
9375             var cc = String.fromCharCode(event.getCharCode());
9376             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9377             
9378         }
9379     },
9380     adjustWidth : function(tag, w){
9381         tag = tag.toLowerCase();
9382         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9383             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9384                 if(tag == 'input'){
9385                     return w + 2;
9386                 }
9387                 if(tag == 'textarea'){
9388                     return w-2;
9389                 }
9390             }else if(Roo.isOpera){
9391                 if(tag == 'input'){
9392                     return w + 2;
9393                 }
9394                 if(tag == 'textarea'){
9395                     return w-2;
9396                 }
9397             }
9398         }
9399         return w;
9400     },
9401     
9402     setFieldLabel : function(v)
9403     {
9404         if(!this.rendered){
9405             return;
9406         }
9407         
9408         if(this.indicator){
9409             var ar = this.el.select('label > span',true);
9410             
9411             if (ar.elements.length) {
9412                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9413                 this.fieldLabel = v;
9414                 return;
9415             }
9416             
9417             var br = this.el.select('label',true);
9418             
9419             if(br.elements.length) {
9420                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9421                 this.fieldLabel = v;
9422                 return;
9423             }
9424             
9425             Roo.log('Cannot Found any of label > span || label in input');
9426             return;
9427         }
9428         
9429         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9430         this.fieldLabel = v;
9431         
9432         
9433     }
9434 });
9435
9436  
9437 /*
9438  * - LGPL
9439  *
9440  * Input
9441  * 
9442  */
9443
9444 /**
9445  * @class Roo.bootstrap.TextArea
9446  * @extends Roo.bootstrap.Input
9447  * Bootstrap TextArea class
9448  * @cfg {Number} cols Specifies the visible width of a text area
9449  * @cfg {Number} rows Specifies the visible number of lines in a text area
9450  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9451  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9452  * @cfg {string} html text
9453  * 
9454  * @constructor
9455  * Create a new TextArea
9456  * @param {Object} config The config object
9457  */
9458
9459 Roo.bootstrap.TextArea = function(config){
9460     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9461    
9462 };
9463
9464 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9465      
9466     cols : false,
9467     rows : 5,
9468     readOnly : false,
9469     warp : 'soft',
9470     resize : false,
9471     value: false,
9472     html: false,
9473     
9474     getAutoCreate : function(){
9475         
9476         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9477         
9478         var id = Roo.id();
9479         
9480         var cfg = {};
9481         
9482         if(this.inputType != 'hidden'){
9483             cfg.cls = 'form-group' //input-group
9484         }
9485         
9486         var input =  {
9487             tag: 'textarea',
9488             id : id,
9489             warp : this.warp,
9490             rows : this.rows,
9491             value : this.value || '',
9492             html: this.html || '',
9493             cls : 'form-control',
9494             placeholder : this.placeholder || '' 
9495             
9496         };
9497         
9498         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9499             input.maxLength = this.maxLength;
9500         }
9501         
9502         if(this.resize){
9503             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9504         }
9505         
9506         if(this.cols){
9507             input.cols = this.cols;
9508         }
9509         
9510         if (this.readOnly) {
9511             input.readonly = true;
9512         }
9513         
9514         if (this.name) {
9515             input.name = this.name;
9516         }
9517         
9518         if (this.size) {
9519             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9520         }
9521         
9522         var settings=this;
9523         ['xs','sm','md','lg'].map(function(size){
9524             if (settings[size]) {
9525                 cfg.cls += ' col-' + size + '-' + settings[size];
9526             }
9527         });
9528         
9529         var inputblock = input;
9530         
9531         if(this.hasFeedback && !this.allowBlank){
9532             
9533             var feedback = {
9534                 tag: 'span',
9535                 cls: 'glyphicon form-control-feedback'
9536             };
9537
9538             inputblock = {
9539                 cls : 'has-feedback',
9540                 cn :  [
9541                     input,
9542                     feedback
9543                 ] 
9544             };  
9545         }
9546         
9547         
9548         if (this.before || this.after) {
9549             
9550             inputblock = {
9551                 cls : 'input-group',
9552                 cn :  [] 
9553             };
9554             if (this.before) {
9555                 inputblock.cn.push({
9556                     tag :'span',
9557                     cls : 'input-group-addon',
9558                     html : this.before
9559                 });
9560             }
9561             
9562             inputblock.cn.push(input);
9563             
9564             if(this.hasFeedback && !this.allowBlank){
9565                 inputblock.cls += ' has-feedback';
9566                 inputblock.cn.push(feedback);
9567             }
9568             
9569             if (this.after) {
9570                 inputblock.cn.push({
9571                     tag :'span',
9572                     cls : 'input-group-addon',
9573                     html : this.after
9574                 });
9575             }
9576             
9577         }
9578         
9579         if (align ==='left' && this.fieldLabel.length) {
9580             cfg.cn = [
9581                 {
9582                     tag: 'label',
9583                     'for' :  id,
9584                     cls : 'control-label',
9585                     html : this.fieldLabel
9586                 },
9587                 {
9588                     cls : "",
9589                     cn: [
9590                         inputblock
9591                     ]
9592                 }
9593
9594             ];
9595             
9596             if(this.labelWidth > 12){
9597                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9598             }
9599
9600             if(this.labelWidth < 13 && this.labelmd == 0){
9601                 this.labelmd = this.labelWidth;
9602             }
9603
9604             if(this.labellg > 0){
9605                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9606                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9607             }
9608
9609             if(this.labelmd > 0){
9610                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9611                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9612             }
9613
9614             if(this.labelsm > 0){
9615                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9616                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9617             }
9618
9619             if(this.labelxs > 0){
9620                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9621                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9622             }
9623             
9624         } else if ( this.fieldLabel.length) {
9625             cfg.cn = [
9626
9627                {
9628                    tag: 'label',
9629                    //cls : 'input-group-addon',
9630                    html : this.fieldLabel
9631
9632                },
9633
9634                inputblock
9635
9636            ];
9637
9638         } else {
9639
9640             cfg.cn = [
9641
9642                 inputblock
9643
9644             ];
9645                 
9646         }
9647         
9648         if (this.disabled) {
9649             input.disabled=true;
9650         }
9651         
9652         return cfg;
9653         
9654     },
9655     /**
9656      * return the real textarea element.
9657      */
9658     inputEl: function ()
9659     {
9660         return this.el.select('textarea.form-control',true).first();
9661     },
9662     
9663     /**
9664      * Clear any invalid styles/messages for this field
9665      */
9666     clearInvalid : function()
9667     {
9668         
9669         if(!this.el || this.preventMark){ // not rendered
9670             return;
9671         }
9672         
9673         var label = this.el.select('label', true).first();
9674         var icon = this.el.select('i.fa-star', true).first();
9675         
9676         if(label && icon){
9677             icon.remove();
9678         }
9679         
9680         this.el.removeClass(this.invalidClass);
9681         
9682         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9683             
9684             var feedback = this.el.select('.form-control-feedback', true).first();
9685             
9686             if(feedback){
9687                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9688             }
9689             
9690         }
9691         
9692         this.fireEvent('valid', this);
9693     },
9694     
9695      /**
9696      * Mark this field as valid
9697      */
9698     markValid : function()
9699     {
9700         if(!this.el  || this.preventMark){ // not rendered
9701             return;
9702         }
9703         
9704         this.el.removeClass([this.invalidClass, this.validClass]);
9705         
9706         var feedback = this.el.select('.form-control-feedback', true).first();
9707             
9708         if(feedback){
9709             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9710         }
9711
9712         if(this.disabled || this.allowBlank){
9713             return;
9714         }
9715         
9716         var label = this.el.select('label', true).first();
9717         var icon = this.el.select('i.fa-star', true).first();
9718         
9719         if(label && icon){
9720             icon.remove();
9721         }
9722         
9723         this.el.addClass(this.validClass);
9724         
9725         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9726             
9727             var feedback = this.el.select('.form-control-feedback', true).first();
9728             
9729             if(feedback){
9730                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9731                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9732             }
9733             
9734         }
9735         
9736         this.fireEvent('valid', this);
9737     },
9738     
9739      /**
9740      * Mark this field as invalid
9741      * @param {String} msg The validation message
9742      */
9743     markInvalid : function(msg)
9744     {
9745         if(!this.el  || this.preventMark){ // not rendered
9746             return;
9747         }
9748         
9749         this.el.removeClass([this.invalidClass, this.validClass]);
9750         
9751         var feedback = this.el.select('.form-control-feedback', true).first();
9752             
9753         if(feedback){
9754             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9755         }
9756
9757         if(this.disabled || this.allowBlank){
9758             return;
9759         }
9760         
9761         var label = this.el.select('label', true).first();
9762         var icon = this.el.select('i.fa-star', true).first();
9763         
9764         if(!this.getValue().length && label && !icon){
9765             this.el.createChild({
9766                 tag : 'i',
9767                 cls : 'text-danger fa fa-lg fa-star',
9768                 tooltip : 'This field is required',
9769                 style : 'margin-right:5px;'
9770             }, label, true);
9771         }
9772
9773         this.el.addClass(this.invalidClass);
9774         
9775         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9776             
9777             var feedback = this.el.select('.form-control-feedback', true).first();
9778             
9779             if(feedback){
9780                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9781                 
9782                 if(this.getValue().length || this.forceFeedback){
9783                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9784                 }
9785                 
9786             }
9787             
9788         }
9789         
9790         this.fireEvent('invalid', this, msg);
9791     }
9792 });
9793
9794  
9795 /*
9796  * - LGPL
9797  *
9798  * trigger field - base class for combo..
9799  * 
9800  */
9801  
9802 /**
9803  * @class Roo.bootstrap.TriggerField
9804  * @extends Roo.bootstrap.Input
9805  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9806  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9807  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9808  * for which you can provide a custom implementation.  For example:
9809  * <pre><code>
9810 var trigger = new Roo.bootstrap.TriggerField();
9811 trigger.onTriggerClick = myTriggerFn;
9812 trigger.applyTo('my-field');
9813 </code></pre>
9814  *
9815  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9816  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9817  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9818  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9819  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9820
9821  * @constructor
9822  * Create a new TriggerField.
9823  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9824  * to the base TextField)
9825  */
9826 Roo.bootstrap.TriggerField = function(config){
9827     this.mimicing = false;
9828     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9829 };
9830
9831 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9832     /**
9833      * @cfg {String} triggerClass A CSS class to apply to the trigger
9834      */
9835      /**
9836      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9837      */
9838     hideTrigger:false,
9839
9840     /**
9841      * @cfg {Boolean} removable (true|false) special filter default false
9842      */
9843     removable : false,
9844     
9845     /** @cfg {Boolean} grow @hide */
9846     /** @cfg {Number} growMin @hide */
9847     /** @cfg {Number} growMax @hide */
9848
9849     /**
9850      * @hide 
9851      * @method
9852      */
9853     autoSize: Roo.emptyFn,
9854     // private
9855     monitorTab : true,
9856     // private
9857     deferHeight : true,
9858
9859     
9860     actionMode : 'wrap',
9861     
9862     caret : false,
9863     
9864     
9865     getAutoCreate : function(){
9866        
9867         var align = this.labelAlign || this.parentLabelAlign();
9868         
9869         var id = Roo.id();
9870         
9871         var cfg = {
9872             cls: 'form-group' //input-group
9873         };
9874         
9875         
9876         var input =  {
9877             tag: 'input',
9878             id : id,
9879             type : this.inputType,
9880             cls : 'form-control',
9881             autocomplete: 'new-password',
9882             placeholder : this.placeholder || '' 
9883             
9884         };
9885         if (this.name) {
9886             input.name = this.name;
9887         }
9888         if (this.size) {
9889             input.cls += ' input-' + this.size;
9890         }
9891         
9892         if (this.disabled) {
9893             input.disabled=true;
9894         }
9895         
9896         var inputblock = input;
9897         
9898         if(this.hasFeedback && !this.allowBlank){
9899             
9900             var feedback = {
9901                 tag: 'span',
9902                 cls: 'glyphicon form-control-feedback'
9903             };
9904             
9905             if(this.removable && !this.editable && !this.tickable){
9906                 inputblock = {
9907                     cls : 'has-feedback',
9908                     cn :  [
9909                         inputblock,
9910                         {
9911                             tag: 'button',
9912                             html : 'x',
9913                             cls : 'roo-combo-removable-btn close'
9914                         },
9915                         feedback
9916                     ] 
9917                 };
9918             } else {
9919                 inputblock = {
9920                     cls : 'has-feedback',
9921                     cn :  [
9922                         inputblock,
9923                         feedback
9924                     ] 
9925                 };
9926             }
9927
9928         } else {
9929             if(this.removable && !this.editable && !this.tickable){
9930                 inputblock = {
9931                     cls : 'roo-removable',
9932                     cn :  [
9933                         inputblock,
9934                         {
9935                             tag: 'button',
9936                             html : 'x',
9937                             cls : 'roo-combo-removable-btn close'
9938                         }
9939                     ] 
9940                 };
9941             }
9942         }
9943         
9944         if (this.before || this.after) {
9945             
9946             inputblock = {
9947                 cls : 'input-group',
9948                 cn :  [] 
9949             };
9950             if (this.before) {
9951                 inputblock.cn.push({
9952                     tag :'span',
9953                     cls : 'input-group-addon',
9954                     html : this.before
9955                 });
9956             }
9957             
9958             inputblock.cn.push(input);
9959             
9960             if(this.hasFeedback && !this.allowBlank){
9961                 inputblock.cls += ' has-feedback';
9962                 inputblock.cn.push(feedback);
9963             }
9964             
9965             if (this.after) {
9966                 inputblock.cn.push({
9967                     tag :'span',
9968                     cls : 'input-group-addon',
9969                     html : this.after
9970                 });
9971             }
9972             
9973         };
9974         
9975         var box = {
9976             tag: 'div',
9977             cn: [
9978                 {
9979                     tag: 'input',
9980                     type : 'hidden',
9981                     cls: 'form-hidden-field'
9982                 },
9983                 inputblock
9984             ]
9985             
9986         };
9987         
9988         if(this.multiple){
9989             box = {
9990                 tag: 'div',
9991                 cn: [
9992                     {
9993                         tag: 'input',
9994                         type : 'hidden',
9995                         cls: 'form-hidden-field'
9996                     },
9997                     {
9998                         tag: 'ul',
9999                         cls: 'roo-select2-choices',
10000                         cn:[
10001                             {
10002                                 tag: 'li',
10003                                 cls: 'roo-select2-search-field',
10004                                 cn: [
10005
10006                                     inputblock
10007                                 ]
10008                             }
10009                         ]
10010                     }
10011                 ]
10012             }
10013         };
10014         
10015         var combobox = {
10016             cls: 'roo-select2-container input-group',
10017             cn: [
10018                 box
10019 //                {
10020 //                    tag: 'ul',
10021 //                    cls: 'typeahead typeahead-long dropdown-menu',
10022 //                    style: 'display:none'
10023 //                }
10024             ]
10025         };
10026         
10027         if(!this.multiple && this.showToggleBtn){
10028             
10029             var caret = {
10030                         tag: 'span',
10031                         cls: 'caret'
10032              };
10033             if (this.caret != false) {
10034                 caret = {
10035                      tag: 'i',
10036                      cls: 'fa fa-' + this.caret
10037                 };
10038                 
10039             }
10040             
10041             combobox.cn.push({
10042                 tag :'span',
10043                 cls : 'input-group-addon btn dropdown-toggle',
10044                 cn : [
10045                     caret,
10046                     {
10047                         tag: 'span',
10048                         cls: 'combobox-clear',
10049                         cn  : [
10050                             {
10051                                 tag : 'i',
10052                                 cls: 'icon-remove'
10053                             }
10054                         ]
10055                     }
10056                 ]
10057
10058             })
10059         }
10060         
10061         if(this.multiple){
10062             combobox.cls += ' roo-select2-container-multi';
10063         }
10064         
10065         if (align ==='left' && this.fieldLabel.length) {
10066             
10067             cfg.cls += ' roo-form-group-label-left';
10068
10069             cfg.cn = [
10070                 {
10071                     tag : 'i',
10072                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10073                     tooltip : 'This field is required'
10074                 },
10075                 {
10076                     tag: 'label',
10077                     'for' :  id,
10078                     cls : 'control-label',
10079                     html : this.fieldLabel
10080
10081                 },
10082                 {
10083                     cls : "", 
10084                     cn: [
10085                         combobox
10086                     ]
10087                 }
10088
10089             ];
10090             
10091             var labelCfg = cfg.cn[1];
10092             var contentCfg = cfg.cn[2];
10093             
10094             if(this.indicatorpos == 'right'){
10095                 cfg.cn = [
10096                     {
10097                         tag: 'label',
10098                         'for' :  id,
10099                         cls : 'control-label',
10100                         cn : [
10101                             {
10102                                 tag : 'span',
10103                                 html : this.fieldLabel
10104                             },
10105                             {
10106                                 tag : 'i',
10107                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10108                                 tooltip : 'This field is required'
10109                             }
10110                         ]
10111                     },
10112                     {
10113                         cls : "", 
10114                         cn: [
10115                             combobox
10116                         ]
10117                     }
10118
10119                 ];
10120                 
10121                 labelCfg = cfg.cn[0];
10122                 contentCfg = cfg.cn[1];
10123             }
10124             
10125             if(this.labelWidth > 12){
10126                 labelCfg.style = "width: " + this.labelWidth + 'px';
10127             }
10128             
10129             if(this.labelWidth < 13 && this.labelmd == 0){
10130                 this.labelmd = this.labelWidth;
10131             }
10132             
10133             if(this.labellg > 0){
10134                 labelCfg.cls += ' col-lg-' + this.labellg;
10135                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10136             }
10137             
10138             if(this.labelmd > 0){
10139                 labelCfg.cls += ' col-md-' + this.labelmd;
10140                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10141             }
10142             
10143             if(this.labelsm > 0){
10144                 labelCfg.cls += ' col-sm-' + this.labelsm;
10145                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10146             }
10147             
10148             if(this.labelxs > 0){
10149                 labelCfg.cls += ' col-xs-' + this.labelxs;
10150                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10151             }
10152             
10153         } else if ( this.fieldLabel.length) {
10154 //                Roo.log(" label");
10155             cfg.cn = [
10156                 {
10157                    tag : 'i',
10158                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10159                    tooltip : 'This field is required'
10160                },
10161                {
10162                    tag: 'label',
10163                    //cls : 'input-group-addon',
10164                    html : this.fieldLabel
10165
10166                },
10167
10168                combobox
10169
10170             ];
10171             
10172             if(this.indicatorpos == 'right'){
10173                 
10174                 cfg.cn = [
10175                     {
10176                        tag: 'label',
10177                        cn : [
10178                            {
10179                                tag : 'span',
10180                                html : this.fieldLabel
10181                            },
10182                            {
10183                               tag : 'i',
10184                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10185                               tooltip : 'This field is required'
10186                            }
10187                        ]
10188
10189                     },
10190                     combobox
10191
10192                 ];
10193
10194             }
10195
10196         } else {
10197             
10198 //                Roo.log(" no label && no align");
10199                 cfg = combobox
10200                      
10201                 
10202         }
10203         
10204         var settings=this;
10205         ['xs','sm','md','lg'].map(function(size){
10206             if (settings[size]) {
10207                 cfg.cls += ' col-' + size + '-' + settings[size];
10208             }
10209         });
10210         
10211         return cfg;
10212         
10213     },
10214     
10215     
10216     
10217     // private
10218     onResize : function(w, h){
10219 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10220 //        if(typeof w == 'number'){
10221 //            var x = w - this.trigger.getWidth();
10222 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10223 //            this.trigger.setStyle('left', x+'px');
10224 //        }
10225     },
10226
10227     // private
10228     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10229
10230     // private
10231     getResizeEl : function(){
10232         return this.inputEl();
10233     },
10234
10235     // private
10236     getPositionEl : function(){
10237         return this.inputEl();
10238     },
10239
10240     // private
10241     alignErrorIcon : function(){
10242         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10243     },
10244
10245     // private
10246     initEvents : function(){
10247         
10248         this.createList();
10249         
10250         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10251         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10252         if(!this.multiple && this.showToggleBtn){
10253             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10254             if(this.hideTrigger){
10255                 this.trigger.setDisplayed(false);
10256             }
10257             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10258         }
10259         
10260         if(this.multiple){
10261             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10262         }
10263         
10264         if(this.removable && !this.editable && !this.tickable){
10265             var close = this.closeTriggerEl();
10266             
10267             if(close){
10268                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10269                 close.on('click', this.removeBtnClick, this, close);
10270             }
10271         }
10272         
10273         //this.trigger.addClassOnOver('x-form-trigger-over');
10274         //this.trigger.addClassOnClick('x-form-trigger-click');
10275         
10276         //if(!this.width){
10277         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10278         //}
10279     },
10280     
10281     closeTriggerEl : function()
10282     {
10283         var close = this.el.select('.roo-combo-removable-btn', true).first();
10284         return close ? close : false;
10285     },
10286     
10287     removeBtnClick : function(e, h, el)
10288     {
10289         e.preventDefault();
10290         
10291         if(this.fireEvent("remove", this) !== false){
10292             this.reset();
10293             this.fireEvent("afterremove", this)
10294         }
10295     },
10296     
10297     createList : function()
10298     {
10299         this.list = Roo.get(document.body).createChild({
10300             tag: 'ul',
10301             cls: 'typeahead typeahead-long dropdown-menu',
10302             style: 'display:none'
10303         });
10304         
10305         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10306         
10307     },
10308
10309     // private
10310     initTrigger : function(){
10311        
10312     },
10313
10314     // private
10315     onDestroy : function(){
10316         if(this.trigger){
10317             this.trigger.removeAllListeners();
10318           //  this.trigger.remove();
10319         }
10320         //if(this.wrap){
10321         //    this.wrap.remove();
10322         //}
10323         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10324     },
10325
10326     // private
10327     onFocus : function(){
10328         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10329         /*
10330         if(!this.mimicing){
10331             this.wrap.addClass('x-trigger-wrap-focus');
10332             this.mimicing = true;
10333             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10334             if(this.monitorTab){
10335                 this.el.on("keydown", this.checkTab, this);
10336             }
10337         }
10338         */
10339     },
10340
10341     // private
10342     checkTab : function(e){
10343         if(e.getKey() == e.TAB){
10344             this.triggerBlur();
10345         }
10346     },
10347
10348     // private
10349     onBlur : function(){
10350         // do nothing
10351     },
10352
10353     // private
10354     mimicBlur : function(e, t){
10355         /*
10356         if(!this.wrap.contains(t) && this.validateBlur()){
10357             this.triggerBlur();
10358         }
10359         */
10360     },
10361
10362     // private
10363     triggerBlur : function(){
10364         this.mimicing = false;
10365         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10366         if(this.monitorTab){
10367             this.el.un("keydown", this.checkTab, this);
10368         }
10369         //this.wrap.removeClass('x-trigger-wrap-focus');
10370         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10371     },
10372
10373     // private
10374     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10375     validateBlur : function(e, t){
10376         return true;
10377     },
10378
10379     // private
10380     onDisable : function(){
10381         this.inputEl().dom.disabled = true;
10382         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10383         //if(this.wrap){
10384         //    this.wrap.addClass('x-item-disabled');
10385         //}
10386     },
10387
10388     // private
10389     onEnable : function(){
10390         this.inputEl().dom.disabled = false;
10391         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10392         //if(this.wrap){
10393         //    this.el.removeClass('x-item-disabled');
10394         //}
10395     },
10396
10397     // private
10398     onShow : function(){
10399         var ae = this.getActionEl();
10400         
10401         if(ae){
10402             ae.dom.style.display = '';
10403             ae.dom.style.visibility = 'visible';
10404         }
10405     },
10406
10407     // private
10408     
10409     onHide : function(){
10410         var ae = this.getActionEl();
10411         ae.dom.style.display = 'none';
10412     },
10413
10414     /**
10415      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10416      * by an implementing function.
10417      * @method
10418      * @param {EventObject} e
10419      */
10420     onTriggerClick : Roo.emptyFn
10421 });
10422  /*
10423  * Based on:
10424  * Ext JS Library 1.1.1
10425  * Copyright(c) 2006-2007, Ext JS, LLC.
10426  *
10427  * Originally Released Under LGPL - original licence link has changed is not relivant.
10428  *
10429  * Fork - LGPL
10430  * <script type="text/javascript">
10431  */
10432
10433
10434 /**
10435  * @class Roo.data.SortTypes
10436  * @singleton
10437  * Defines the default sorting (casting?) comparison functions used when sorting data.
10438  */
10439 Roo.data.SortTypes = {
10440     /**
10441      * Default sort that does nothing
10442      * @param {Mixed} s The value being converted
10443      * @return {Mixed} The comparison value
10444      */
10445     none : function(s){
10446         return s;
10447     },
10448     
10449     /**
10450      * The regular expression used to strip tags
10451      * @type {RegExp}
10452      * @property
10453      */
10454     stripTagsRE : /<\/?[^>]+>/gi,
10455     
10456     /**
10457      * Strips all HTML tags to sort on text only
10458      * @param {Mixed} s The value being converted
10459      * @return {String} The comparison value
10460      */
10461     asText : function(s){
10462         return String(s).replace(this.stripTagsRE, "");
10463     },
10464     
10465     /**
10466      * Strips all HTML tags to sort on text only - Case insensitive
10467      * @param {Mixed} s The value being converted
10468      * @return {String} The comparison value
10469      */
10470     asUCText : function(s){
10471         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10472     },
10473     
10474     /**
10475      * Case insensitive string
10476      * @param {Mixed} s The value being converted
10477      * @return {String} The comparison value
10478      */
10479     asUCString : function(s) {
10480         return String(s).toUpperCase();
10481     },
10482     
10483     /**
10484      * Date sorting
10485      * @param {Mixed} s The value being converted
10486      * @return {Number} The comparison value
10487      */
10488     asDate : function(s) {
10489         if(!s){
10490             return 0;
10491         }
10492         if(s instanceof Date){
10493             return s.getTime();
10494         }
10495         return Date.parse(String(s));
10496     },
10497     
10498     /**
10499      * Float sorting
10500      * @param {Mixed} s The value being converted
10501      * @return {Float} The comparison value
10502      */
10503     asFloat : function(s) {
10504         var val = parseFloat(String(s).replace(/,/g, ""));
10505         if(isNaN(val)) {
10506             val = 0;
10507         }
10508         return val;
10509     },
10510     
10511     /**
10512      * Integer sorting
10513      * @param {Mixed} s The value being converted
10514      * @return {Number} The comparison value
10515      */
10516     asInt : function(s) {
10517         var val = parseInt(String(s).replace(/,/g, ""));
10518         if(isNaN(val)) {
10519             val = 0;
10520         }
10521         return val;
10522     }
10523 };/*
10524  * Based on:
10525  * Ext JS Library 1.1.1
10526  * Copyright(c) 2006-2007, Ext JS, LLC.
10527  *
10528  * Originally Released Under LGPL - original licence link has changed is not relivant.
10529  *
10530  * Fork - LGPL
10531  * <script type="text/javascript">
10532  */
10533
10534 /**
10535 * @class Roo.data.Record
10536  * Instances of this class encapsulate both record <em>definition</em> information, and record
10537  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10538  * to access Records cached in an {@link Roo.data.Store} object.<br>
10539  * <p>
10540  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10541  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10542  * objects.<br>
10543  * <p>
10544  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10545  * @constructor
10546  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10547  * {@link #create}. The parameters are the same.
10548  * @param {Array} data An associative Array of data values keyed by the field name.
10549  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10550  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10551  * not specified an integer id is generated.
10552  */
10553 Roo.data.Record = function(data, id){
10554     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10555     this.data = data;
10556 };
10557
10558 /**
10559  * Generate a constructor for a specific record layout.
10560  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10561  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10562  * Each field definition object may contain the following properties: <ul>
10563  * <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,
10564  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10565  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10566  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10567  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10568  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10569  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10570  * this may be omitted.</p></li>
10571  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10572  * <ul><li>auto (Default, implies no conversion)</li>
10573  * <li>string</li>
10574  * <li>int</li>
10575  * <li>float</li>
10576  * <li>boolean</li>
10577  * <li>date</li></ul></p></li>
10578  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10579  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10580  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10581  * by the Reader into an object that will be stored in the Record. It is passed the
10582  * following parameters:<ul>
10583  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10584  * </ul></p></li>
10585  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10586  * </ul>
10587  * <br>usage:<br><pre><code>
10588 var TopicRecord = Roo.data.Record.create(
10589     {name: 'title', mapping: 'topic_title'},
10590     {name: 'author', mapping: 'username'},
10591     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10592     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10593     {name: 'lastPoster', mapping: 'user2'},
10594     {name: 'excerpt', mapping: 'post_text'}
10595 );
10596
10597 var myNewRecord = new TopicRecord({
10598     title: 'Do my job please',
10599     author: 'noobie',
10600     totalPosts: 1,
10601     lastPost: new Date(),
10602     lastPoster: 'Animal',
10603     excerpt: 'No way dude!'
10604 });
10605 myStore.add(myNewRecord);
10606 </code></pre>
10607  * @method create
10608  * @static
10609  */
10610 Roo.data.Record.create = function(o){
10611     var f = function(){
10612         f.superclass.constructor.apply(this, arguments);
10613     };
10614     Roo.extend(f, Roo.data.Record);
10615     var p = f.prototype;
10616     p.fields = new Roo.util.MixedCollection(false, function(field){
10617         return field.name;
10618     });
10619     for(var i = 0, len = o.length; i < len; i++){
10620         p.fields.add(new Roo.data.Field(o[i]));
10621     }
10622     f.getField = function(name){
10623         return p.fields.get(name);  
10624     };
10625     return f;
10626 };
10627
10628 Roo.data.Record.AUTO_ID = 1000;
10629 Roo.data.Record.EDIT = 'edit';
10630 Roo.data.Record.REJECT = 'reject';
10631 Roo.data.Record.COMMIT = 'commit';
10632
10633 Roo.data.Record.prototype = {
10634     /**
10635      * Readonly flag - true if this record has been modified.
10636      * @type Boolean
10637      */
10638     dirty : false,
10639     editing : false,
10640     error: null,
10641     modified: null,
10642
10643     // private
10644     join : function(store){
10645         this.store = store;
10646     },
10647
10648     /**
10649      * Set the named field to the specified value.
10650      * @param {String} name The name of the field to set.
10651      * @param {Object} value The value to set the field to.
10652      */
10653     set : function(name, value){
10654         if(this.data[name] == value){
10655             return;
10656         }
10657         this.dirty = true;
10658         if(!this.modified){
10659             this.modified = {};
10660         }
10661         if(typeof this.modified[name] == 'undefined'){
10662             this.modified[name] = this.data[name];
10663         }
10664         this.data[name] = value;
10665         if(!this.editing && this.store){
10666             this.store.afterEdit(this);
10667         }       
10668     },
10669
10670     /**
10671      * Get the value of the named field.
10672      * @param {String} name The name of the field to get the value of.
10673      * @return {Object} The value of the field.
10674      */
10675     get : function(name){
10676         return this.data[name]; 
10677     },
10678
10679     // private
10680     beginEdit : function(){
10681         this.editing = true;
10682         this.modified = {}; 
10683     },
10684
10685     // private
10686     cancelEdit : function(){
10687         this.editing = false;
10688         delete this.modified;
10689     },
10690
10691     // private
10692     endEdit : function(){
10693         this.editing = false;
10694         if(this.dirty && this.store){
10695             this.store.afterEdit(this);
10696         }
10697     },
10698
10699     /**
10700      * Usually called by the {@link Roo.data.Store} which owns the Record.
10701      * Rejects all changes made to the Record since either creation, or the last commit operation.
10702      * Modified fields are reverted to their original values.
10703      * <p>
10704      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10705      * of reject operations.
10706      */
10707     reject : function(){
10708         var m = this.modified;
10709         for(var n in m){
10710             if(typeof m[n] != "function"){
10711                 this.data[n] = m[n];
10712             }
10713         }
10714         this.dirty = false;
10715         delete this.modified;
10716         this.editing = false;
10717         if(this.store){
10718             this.store.afterReject(this);
10719         }
10720     },
10721
10722     /**
10723      * Usually called by the {@link Roo.data.Store} which owns the Record.
10724      * Commits all changes made to the Record since either creation, or the last commit operation.
10725      * <p>
10726      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10727      * of commit operations.
10728      */
10729     commit : function(){
10730         this.dirty = false;
10731         delete this.modified;
10732         this.editing = false;
10733         if(this.store){
10734             this.store.afterCommit(this);
10735         }
10736     },
10737
10738     // private
10739     hasError : function(){
10740         return this.error != null;
10741     },
10742
10743     // private
10744     clearError : function(){
10745         this.error = null;
10746     },
10747
10748     /**
10749      * Creates a copy of this record.
10750      * @param {String} id (optional) A new record id if you don't want to use this record's id
10751      * @return {Record}
10752      */
10753     copy : function(newId) {
10754         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10755     }
10756 };/*
10757  * Based on:
10758  * Ext JS Library 1.1.1
10759  * Copyright(c) 2006-2007, Ext JS, LLC.
10760  *
10761  * Originally Released Under LGPL - original licence link has changed is not relivant.
10762  *
10763  * Fork - LGPL
10764  * <script type="text/javascript">
10765  */
10766
10767
10768
10769 /**
10770  * @class Roo.data.Store
10771  * @extends Roo.util.Observable
10772  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10773  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10774  * <p>
10775  * 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
10776  * has no knowledge of the format of the data returned by the Proxy.<br>
10777  * <p>
10778  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10779  * instances from the data object. These records are cached and made available through accessor functions.
10780  * @constructor
10781  * Creates a new Store.
10782  * @param {Object} config A config object containing the objects needed for the Store to access data,
10783  * and read the data into Records.
10784  */
10785 Roo.data.Store = function(config){
10786     this.data = new Roo.util.MixedCollection(false);
10787     this.data.getKey = function(o){
10788         return o.id;
10789     };
10790     this.baseParams = {};
10791     // private
10792     this.paramNames = {
10793         "start" : "start",
10794         "limit" : "limit",
10795         "sort" : "sort",
10796         "dir" : "dir",
10797         "multisort" : "_multisort"
10798     };
10799
10800     if(config && config.data){
10801         this.inlineData = config.data;
10802         delete config.data;
10803     }
10804
10805     Roo.apply(this, config);
10806     
10807     if(this.reader){ // reader passed
10808         this.reader = Roo.factory(this.reader, Roo.data);
10809         this.reader.xmodule = this.xmodule || false;
10810         if(!this.recordType){
10811             this.recordType = this.reader.recordType;
10812         }
10813         if(this.reader.onMetaChange){
10814             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10815         }
10816     }
10817
10818     if(this.recordType){
10819         this.fields = this.recordType.prototype.fields;
10820     }
10821     this.modified = [];
10822
10823     this.addEvents({
10824         /**
10825          * @event datachanged
10826          * Fires when the data cache has changed, and a widget which is using this Store
10827          * as a Record cache should refresh its view.
10828          * @param {Store} this
10829          */
10830         datachanged : true,
10831         /**
10832          * @event metachange
10833          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10834          * @param {Store} this
10835          * @param {Object} meta The JSON metadata
10836          */
10837         metachange : true,
10838         /**
10839          * @event add
10840          * Fires when Records have been added to the Store
10841          * @param {Store} this
10842          * @param {Roo.data.Record[]} records The array of Records added
10843          * @param {Number} index The index at which the record(s) were added
10844          */
10845         add : true,
10846         /**
10847          * @event remove
10848          * Fires when a Record has been removed from the Store
10849          * @param {Store} this
10850          * @param {Roo.data.Record} record The Record that was removed
10851          * @param {Number} index The index at which the record was removed
10852          */
10853         remove : true,
10854         /**
10855          * @event update
10856          * Fires when a Record has been updated
10857          * @param {Store} this
10858          * @param {Roo.data.Record} record The Record that was updated
10859          * @param {String} operation The update operation being performed.  Value may be one of:
10860          * <pre><code>
10861  Roo.data.Record.EDIT
10862  Roo.data.Record.REJECT
10863  Roo.data.Record.COMMIT
10864          * </code></pre>
10865          */
10866         update : true,
10867         /**
10868          * @event clear
10869          * Fires when the data cache has been cleared.
10870          * @param {Store} this
10871          */
10872         clear : true,
10873         /**
10874          * @event beforeload
10875          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10876          * the load action will be canceled.
10877          * @param {Store} this
10878          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10879          */
10880         beforeload : true,
10881         /**
10882          * @event beforeloadadd
10883          * Fires after a new set of Records has been loaded.
10884          * @param {Store} this
10885          * @param {Roo.data.Record[]} records The Records that were loaded
10886          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10887          */
10888         beforeloadadd : true,
10889         /**
10890          * @event load
10891          * Fires after a new set of Records has been loaded, before they are added to the store.
10892          * @param {Store} this
10893          * @param {Roo.data.Record[]} records The Records that were loaded
10894          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10895          * @params {Object} return from reader
10896          */
10897         load : true,
10898         /**
10899          * @event loadexception
10900          * Fires if an exception occurs in the Proxy during loading.
10901          * Called with the signature of the Proxy's "loadexception" event.
10902          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10903          * 
10904          * @param {Proxy} 
10905          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10906          * @param {Object} load options 
10907          * @param {Object} jsonData from your request (normally this contains the Exception)
10908          */
10909         loadexception : true
10910     });
10911     
10912     if(this.proxy){
10913         this.proxy = Roo.factory(this.proxy, Roo.data);
10914         this.proxy.xmodule = this.xmodule || false;
10915         this.relayEvents(this.proxy,  ["loadexception"]);
10916     }
10917     this.sortToggle = {};
10918     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10919
10920     Roo.data.Store.superclass.constructor.call(this);
10921
10922     if(this.inlineData){
10923         this.loadData(this.inlineData);
10924         delete this.inlineData;
10925     }
10926 };
10927
10928 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10929      /**
10930     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10931     * without a remote query - used by combo/forms at present.
10932     */
10933     
10934     /**
10935     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10936     */
10937     /**
10938     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10939     */
10940     /**
10941     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10942     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10943     */
10944     /**
10945     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10946     * on any HTTP request
10947     */
10948     /**
10949     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10950     */
10951     /**
10952     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10953     */
10954     multiSort: false,
10955     /**
10956     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10957     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10958     */
10959     remoteSort : false,
10960
10961     /**
10962     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10963      * loaded or when a record is removed. (defaults to false).
10964     */
10965     pruneModifiedRecords : false,
10966
10967     // private
10968     lastOptions : null,
10969
10970     /**
10971      * Add Records to the Store and fires the add event.
10972      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10973      */
10974     add : function(records){
10975         records = [].concat(records);
10976         for(var i = 0, len = records.length; i < len; i++){
10977             records[i].join(this);
10978         }
10979         var index = this.data.length;
10980         this.data.addAll(records);
10981         this.fireEvent("add", this, records, index);
10982     },
10983
10984     /**
10985      * Remove a Record from the Store and fires the remove event.
10986      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10987      */
10988     remove : function(record){
10989         var index = this.data.indexOf(record);
10990         this.data.removeAt(index);
10991         if(this.pruneModifiedRecords){
10992             this.modified.remove(record);
10993         }
10994         this.fireEvent("remove", this, record, index);
10995     },
10996
10997     /**
10998      * Remove all Records from the Store and fires the clear event.
10999      */
11000     removeAll : function(){
11001         this.data.clear();
11002         if(this.pruneModifiedRecords){
11003             this.modified = [];
11004         }
11005         this.fireEvent("clear", this);
11006     },
11007
11008     /**
11009      * Inserts Records to the Store at the given index and fires the add event.
11010      * @param {Number} index The start index at which to insert the passed Records.
11011      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11012      */
11013     insert : function(index, records){
11014         records = [].concat(records);
11015         for(var i = 0, len = records.length; i < len; i++){
11016             this.data.insert(index, records[i]);
11017             records[i].join(this);
11018         }
11019         this.fireEvent("add", this, records, index);
11020     },
11021
11022     /**
11023      * Get the index within the cache of the passed Record.
11024      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11025      * @return {Number} The index of the passed Record. Returns -1 if not found.
11026      */
11027     indexOf : function(record){
11028         return this.data.indexOf(record);
11029     },
11030
11031     /**
11032      * Get the index within the cache of the Record with the passed id.
11033      * @param {String} id The id of the Record to find.
11034      * @return {Number} The index of the Record. Returns -1 if not found.
11035      */
11036     indexOfId : function(id){
11037         return this.data.indexOfKey(id);
11038     },
11039
11040     /**
11041      * Get the Record with the specified id.
11042      * @param {String} id The id of the Record to find.
11043      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11044      */
11045     getById : function(id){
11046         return this.data.key(id);
11047     },
11048
11049     /**
11050      * Get the Record at the specified index.
11051      * @param {Number} index The index of the Record to find.
11052      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11053      */
11054     getAt : function(index){
11055         return this.data.itemAt(index);
11056     },
11057
11058     /**
11059      * Returns a range of Records between specified indices.
11060      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11061      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11062      * @return {Roo.data.Record[]} An array of Records
11063      */
11064     getRange : function(start, end){
11065         return this.data.getRange(start, end);
11066     },
11067
11068     // private
11069     storeOptions : function(o){
11070         o = Roo.apply({}, o);
11071         delete o.callback;
11072         delete o.scope;
11073         this.lastOptions = o;
11074     },
11075
11076     /**
11077      * Loads the Record cache from the configured Proxy using the configured Reader.
11078      * <p>
11079      * If using remote paging, then the first load call must specify the <em>start</em>
11080      * and <em>limit</em> properties in the options.params property to establish the initial
11081      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11082      * <p>
11083      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11084      * and this call will return before the new data has been loaded. Perform any post-processing
11085      * in a callback function, or in a "load" event handler.</strong>
11086      * <p>
11087      * @param {Object} options An object containing properties which control loading options:<ul>
11088      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11089      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11090      * passed the following arguments:<ul>
11091      * <li>r : Roo.data.Record[]</li>
11092      * <li>options: Options object from the load call</li>
11093      * <li>success: Boolean success indicator</li></ul></li>
11094      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11095      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11096      * </ul>
11097      */
11098     load : function(options){
11099         options = options || {};
11100         if(this.fireEvent("beforeload", this, options) !== false){
11101             this.storeOptions(options);
11102             var p = Roo.apply(options.params || {}, this.baseParams);
11103             // if meta was not loaded from remote source.. try requesting it.
11104             if (!this.reader.metaFromRemote) {
11105                 p._requestMeta = 1;
11106             }
11107             if(this.sortInfo && this.remoteSort){
11108                 var pn = this.paramNames;
11109                 p[pn["sort"]] = this.sortInfo.field;
11110                 p[pn["dir"]] = this.sortInfo.direction;
11111             }
11112             if (this.multiSort) {
11113                 var pn = this.paramNames;
11114                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11115             }
11116             
11117             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11118         }
11119     },
11120
11121     /**
11122      * Reloads the Record cache from the configured Proxy using the configured Reader and
11123      * the options from the last load operation performed.
11124      * @param {Object} options (optional) An object containing properties which may override the options
11125      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11126      * the most recently used options are reused).
11127      */
11128     reload : function(options){
11129         this.load(Roo.applyIf(options||{}, this.lastOptions));
11130     },
11131
11132     // private
11133     // Called as a callback by the Reader during a load operation.
11134     loadRecords : function(o, options, success){
11135         if(!o || success === false){
11136             if(success !== false){
11137                 this.fireEvent("load", this, [], options, o);
11138             }
11139             if(options.callback){
11140                 options.callback.call(options.scope || this, [], options, false);
11141             }
11142             return;
11143         }
11144         // if data returned failure - throw an exception.
11145         if (o.success === false) {
11146             // show a message if no listener is registered.
11147             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11148                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11149             }
11150             // loadmask wil be hooked into this..
11151             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11152             return;
11153         }
11154         var r = o.records, t = o.totalRecords || r.length;
11155         
11156         this.fireEvent("beforeloadadd", this, r, options, o);
11157         
11158         if(!options || options.add !== true){
11159             if(this.pruneModifiedRecords){
11160                 this.modified = [];
11161             }
11162             for(var i = 0, len = r.length; i < len; i++){
11163                 r[i].join(this);
11164             }
11165             if(this.snapshot){
11166                 this.data = this.snapshot;
11167                 delete this.snapshot;
11168             }
11169             this.data.clear();
11170             this.data.addAll(r);
11171             this.totalLength = t;
11172             this.applySort();
11173             this.fireEvent("datachanged", this);
11174         }else{
11175             this.totalLength = Math.max(t, this.data.length+r.length);
11176             this.add(r);
11177         }
11178         
11179         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11180                 
11181             var e = new Roo.data.Record({});
11182
11183             e.set(this.parent.displayField, this.parent.emptyTitle);
11184             e.set(this.parent.valueField, '');
11185
11186             this.insert(0, e);
11187         }
11188             
11189         this.fireEvent("load", this, r, options, o);
11190         if(options.callback){
11191             options.callback.call(options.scope || this, r, options, true);
11192         }
11193     },
11194
11195
11196     /**
11197      * Loads data from a passed data block. A Reader which understands the format of the data
11198      * must have been configured in the constructor.
11199      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11200      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11201      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11202      */
11203     loadData : function(o, append){
11204         var r = this.reader.readRecords(o);
11205         this.loadRecords(r, {add: append}, true);
11206     },
11207
11208     /**
11209      * Gets the number of cached records.
11210      * <p>
11211      * <em>If using paging, this may not be the total size of the dataset. If the data object
11212      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11213      * the data set size</em>
11214      */
11215     getCount : function(){
11216         return this.data.length || 0;
11217     },
11218
11219     /**
11220      * Gets the total number of records in the dataset as returned by the server.
11221      * <p>
11222      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11223      * the dataset size</em>
11224      */
11225     getTotalCount : function(){
11226         return this.totalLength || 0;
11227     },
11228
11229     /**
11230      * Returns the sort state of the Store as an object with two properties:
11231      * <pre><code>
11232  field {String} The name of the field by which the Records are sorted
11233  direction {String} The sort order, "ASC" or "DESC"
11234      * </code></pre>
11235      */
11236     getSortState : function(){
11237         return this.sortInfo;
11238     },
11239
11240     // private
11241     applySort : function(){
11242         if(this.sortInfo && !this.remoteSort){
11243             var s = this.sortInfo, f = s.field;
11244             var st = this.fields.get(f).sortType;
11245             var fn = function(r1, r2){
11246                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11247                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11248             };
11249             this.data.sort(s.direction, fn);
11250             if(this.snapshot && this.snapshot != this.data){
11251                 this.snapshot.sort(s.direction, fn);
11252             }
11253         }
11254     },
11255
11256     /**
11257      * Sets the default sort column and order to be used by the next load operation.
11258      * @param {String} fieldName The name of the field to sort by.
11259      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11260      */
11261     setDefaultSort : function(field, dir){
11262         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11263     },
11264
11265     /**
11266      * Sort the Records.
11267      * If remote sorting is used, the sort is performed on the server, and the cache is
11268      * reloaded. If local sorting is used, the cache is sorted internally.
11269      * @param {String} fieldName The name of the field to sort by.
11270      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11271      */
11272     sort : function(fieldName, dir){
11273         var f = this.fields.get(fieldName);
11274         if(!dir){
11275             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11276             
11277             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11278                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11279             }else{
11280                 dir = f.sortDir;
11281             }
11282         }
11283         this.sortToggle[f.name] = dir;
11284         this.sortInfo = {field: f.name, direction: dir};
11285         if(!this.remoteSort){
11286             this.applySort();
11287             this.fireEvent("datachanged", this);
11288         }else{
11289             this.load(this.lastOptions);
11290         }
11291     },
11292
11293     /**
11294      * Calls the specified function for each of the Records in the cache.
11295      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11296      * Returning <em>false</em> aborts and exits the iteration.
11297      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11298      */
11299     each : function(fn, scope){
11300         this.data.each(fn, scope);
11301     },
11302
11303     /**
11304      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11305      * (e.g., during paging).
11306      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11307      */
11308     getModifiedRecords : function(){
11309         return this.modified;
11310     },
11311
11312     // private
11313     createFilterFn : function(property, value, anyMatch){
11314         if(!value.exec){ // not a regex
11315             value = String(value);
11316             if(value.length == 0){
11317                 return false;
11318             }
11319             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11320         }
11321         return function(r){
11322             return value.test(r.data[property]);
11323         };
11324     },
11325
11326     /**
11327      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11328      * @param {String} property A field on your records
11329      * @param {Number} start The record index to start at (defaults to 0)
11330      * @param {Number} end The last record index to include (defaults to length - 1)
11331      * @return {Number} The sum
11332      */
11333     sum : function(property, start, end){
11334         var rs = this.data.items, v = 0;
11335         start = start || 0;
11336         end = (end || end === 0) ? end : rs.length-1;
11337
11338         for(var i = start; i <= end; i++){
11339             v += (rs[i].data[property] || 0);
11340         }
11341         return v;
11342     },
11343
11344     /**
11345      * Filter the records by a specified property.
11346      * @param {String} field A field on your records
11347      * @param {String/RegExp} value Either a string that the field
11348      * should start with or a RegExp to test against the field
11349      * @param {Boolean} anyMatch True to match any part not just the beginning
11350      */
11351     filter : function(property, value, anyMatch){
11352         var fn = this.createFilterFn(property, value, anyMatch);
11353         return fn ? this.filterBy(fn) : this.clearFilter();
11354     },
11355
11356     /**
11357      * Filter by a function. The specified function will be called with each
11358      * record in this data source. If the function returns true the record is included,
11359      * otherwise it is filtered.
11360      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11361      * @param {Object} scope (optional) The scope of the function (defaults to this)
11362      */
11363     filterBy : function(fn, scope){
11364         this.snapshot = this.snapshot || this.data;
11365         this.data = this.queryBy(fn, scope||this);
11366         this.fireEvent("datachanged", this);
11367     },
11368
11369     /**
11370      * Query the records by a specified property.
11371      * @param {String} field A field on your records
11372      * @param {String/RegExp} value Either a string that the field
11373      * should start with or a RegExp to test against the field
11374      * @param {Boolean} anyMatch True to match any part not just the beginning
11375      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11376      */
11377     query : function(property, value, anyMatch){
11378         var fn = this.createFilterFn(property, value, anyMatch);
11379         return fn ? this.queryBy(fn) : this.data.clone();
11380     },
11381
11382     /**
11383      * Query by a function. The specified function will be called with each
11384      * record in this data source. If the function returns true the record is included
11385      * in the results.
11386      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11387      * @param {Object} scope (optional) The scope of the function (defaults to this)
11388       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11389      **/
11390     queryBy : function(fn, scope){
11391         var data = this.snapshot || this.data;
11392         return data.filterBy(fn, scope||this);
11393     },
11394
11395     /**
11396      * Collects unique values for a particular dataIndex from this store.
11397      * @param {String} dataIndex The property to collect
11398      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11399      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11400      * @return {Array} An array of the unique values
11401      **/
11402     collect : function(dataIndex, allowNull, bypassFilter){
11403         var d = (bypassFilter === true && this.snapshot) ?
11404                 this.snapshot.items : this.data.items;
11405         var v, sv, r = [], l = {};
11406         for(var i = 0, len = d.length; i < len; i++){
11407             v = d[i].data[dataIndex];
11408             sv = String(v);
11409             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11410                 l[sv] = true;
11411                 r[r.length] = v;
11412             }
11413         }
11414         return r;
11415     },
11416
11417     /**
11418      * Revert to a view of the Record cache with no filtering applied.
11419      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11420      */
11421     clearFilter : function(suppressEvent){
11422         if(this.snapshot && this.snapshot != this.data){
11423             this.data = this.snapshot;
11424             delete this.snapshot;
11425             if(suppressEvent !== true){
11426                 this.fireEvent("datachanged", this);
11427             }
11428         }
11429     },
11430
11431     // private
11432     afterEdit : function(record){
11433         if(this.modified.indexOf(record) == -1){
11434             this.modified.push(record);
11435         }
11436         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11437     },
11438     
11439     // private
11440     afterReject : function(record){
11441         this.modified.remove(record);
11442         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11443     },
11444
11445     // private
11446     afterCommit : function(record){
11447         this.modified.remove(record);
11448         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11449     },
11450
11451     /**
11452      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11453      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11454      */
11455     commitChanges : function(){
11456         var m = this.modified.slice(0);
11457         this.modified = [];
11458         for(var i = 0, len = m.length; i < len; i++){
11459             m[i].commit();
11460         }
11461     },
11462
11463     /**
11464      * Cancel outstanding changes on all changed records.
11465      */
11466     rejectChanges : function(){
11467         var m = this.modified.slice(0);
11468         this.modified = [];
11469         for(var i = 0, len = m.length; i < len; i++){
11470             m[i].reject();
11471         }
11472     },
11473
11474     onMetaChange : function(meta, rtype, o){
11475         this.recordType = rtype;
11476         this.fields = rtype.prototype.fields;
11477         delete this.snapshot;
11478         this.sortInfo = meta.sortInfo || this.sortInfo;
11479         this.modified = [];
11480         this.fireEvent('metachange', this, this.reader.meta);
11481     },
11482     
11483     moveIndex : function(data, type)
11484     {
11485         var index = this.indexOf(data);
11486         
11487         var newIndex = index + type;
11488         
11489         this.remove(data);
11490         
11491         this.insert(newIndex, data);
11492         
11493     }
11494 });/*
11495  * Based on:
11496  * Ext JS Library 1.1.1
11497  * Copyright(c) 2006-2007, Ext JS, LLC.
11498  *
11499  * Originally Released Under LGPL - original licence link has changed is not relivant.
11500  *
11501  * Fork - LGPL
11502  * <script type="text/javascript">
11503  */
11504
11505 /**
11506  * @class Roo.data.SimpleStore
11507  * @extends Roo.data.Store
11508  * Small helper class to make creating Stores from Array data easier.
11509  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11510  * @cfg {Array} fields An array of field definition objects, or field name strings.
11511  * @cfg {Array} data The multi-dimensional array of data
11512  * @constructor
11513  * @param {Object} config
11514  */
11515 Roo.data.SimpleStore = function(config){
11516     Roo.data.SimpleStore.superclass.constructor.call(this, {
11517         isLocal : true,
11518         reader: new Roo.data.ArrayReader({
11519                 id: config.id
11520             },
11521             Roo.data.Record.create(config.fields)
11522         ),
11523         proxy : new Roo.data.MemoryProxy(config.data)
11524     });
11525     this.load();
11526 };
11527 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11528  * Based on:
11529  * Ext JS Library 1.1.1
11530  * Copyright(c) 2006-2007, Ext JS, LLC.
11531  *
11532  * Originally Released Under LGPL - original licence link has changed is not relivant.
11533  *
11534  * Fork - LGPL
11535  * <script type="text/javascript">
11536  */
11537
11538 /**
11539 /**
11540  * @extends Roo.data.Store
11541  * @class Roo.data.JsonStore
11542  * Small helper class to make creating Stores for JSON data easier. <br/>
11543 <pre><code>
11544 var store = new Roo.data.JsonStore({
11545     url: 'get-images.php',
11546     root: 'images',
11547     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11548 });
11549 </code></pre>
11550  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11551  * JsonReader and HttpProxy (unless inline data is provided).</b>
11552  * @cfg {Array} fields An array of field definition objects, or field name strings.
11553  * @constructor
11554  * @param {Object} config
11555  */
11556 Roo.data.JsonStore = function(c){
11557     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11558         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11559         reader: new Roo.data.JsonReader(c, c.fields)
11560     }));
11561 };
11562 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11563  * Based on:
11564  * Ext JS Library 1.1.1
11565  * Copyright(c) 2006-2007, Ext JS, LLC.
11566  *
11567  * Originally Released Under LGPL - original licence link has changed is not relivant.
11568  *
11569  * Fork - LGPL
11570  * <script type="text/javascript">
11571  */
11572
11573  
11574 Roo.data.Field = function(config){
11575     if(typeof config == "string"){
11576         config = {name: config};
11577     }
11578     Roo.apply(this, config);
11579     
11580     if(!this.type){
11581         this.type = "auto";
11582     }
11583     
11584     var st = Roo.data.SortTypes;
11585     // named sortTypes are supported, here we look them up
11586     if(typeof this.sortType == "string"){
11587         this.sortType = st[this.sortType];
11588     }
11589     
11590     // set default sortType for strings and dates
11591     if(!this.sortType){
11592         switch(this.type){
11593             case "string":
11594                 this.sortType = st.asUCString;
11595                 break;
11596             case "date":
11597                 this.sortType = st.asDate;
11598                 break;
11599             default:
11600                 this.sortType = st.none;
11601         }
11602     }
11603
11604     // define once
11605     var stripRe = /[\$,%]/g;
11606
11607     // prebuilt conversion function for this field, instead of
11608     // switching every time we're reading a value
11609     if(!this.convert){
11610         var cv, dateFormat = this.dateFormat;
11611         switch(this.type){
11612             case "":
11613             case "auto":
11614             case undefined:
11615                 cv = function(v){ return v; };
11616                 break;
11617             case "string":
11618                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11619                 break;
11620             case "int":
11621                 cv = function(v){
11622                     return v !== undefined && v !== null && v !== '' ?
11623                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11624                     };
11625                 break;
11626             case "float":
11627                 cv = function(v){
11628                     return v !== undefined && v !== null && v !== '' ?
11629                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11630                     };
11631                 break;
11632             case "bool":
11633             case "boolean":
11634                 cv = function(v){ return v === true || v === "true" || v == 1; };
11635                 break;
11636             case "date":
11637                 cv = function(v){
11638                     if(!v){
11639                         return '';
11640                     }
11641                     if(v instanceof Date){
11642                         return v;
11643                     }
11644                     if(dateFormat){
11645                         if(dateFormat == "timestamp"){
11646                             return new Date(v*1000);
11647                         }
11648                         return Date.parseDate(v, dateFormat);
11649                     }
11650                     var parsed = Date.parse(v);
11651                     return parsed ? new Date(parsed) : null;
11652                 };
11653              break;
11654             
11655         }
11656         this.convert = cv;
11657     }
11658 };
11659
11660 Roo.data.Field.prototype = {
11661     dateFormat: null,
11662     defaultValue: "",
11663     mapping: null,
11664     sortType : null,
11665     sortDir : "ASC"
11666 };/*
11667  * Based on:
11668  * Ext JS Library 1.1.1
11669  * Copyright(c) 2006-2007, Ext JS, LLC.
11670  *
11671  * Originally Released Under LGPL - original licence link has changed is not relivant.
11672  *
11673  * Fork - LGPL
11674  * <script type="text/javascript">
11675  */
11676  
11677 // Base class for reading structured data from a data source.  This class is intended to be
11678 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11679
11680 /**
11681  * @class Roo.data.DataReader
11682  * Base class for reading structured data from a data source.  This class is intended to be
11683  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11684  */
11685
11686 Roo.data.DataReader = function(meta, recordType){
11687     
11688     this.meta = meta;
11689     
11690     this.recordType = recordType instanceof Array ? 
11691         Roo.data.Record.create(recordType) : recordType;
11692 };
11693
11694 Roo.data.DataReader.prototype = {
11695      /**
11696      * Create an empty record
11697      * @param {Object} data (optional) - overlay some values
11698      * @return {Roo.data.Record} record created.
11699      */
11700     newRow :  function(d) {
11701         var da =  {};
11702         this.recordType.prototype.fields.each(function(c) {
11703             switch( c.type) {
11704                 case 'int' : da[c.name] = 0; break;
11705                 case 'date' : da[c.name] = new Date(); break;
11706                 case 'float' : da[c.name] = 0.0; break;
11707                 case 'boolean' : da[c.name] = false; break;
11708                 default : da[c.name] = ""; break;
11709             }
11710             
11711         });
11712         return new this.recordType(Roo.apply(da, d));
11713     }
11714     
11715 };/*
11716  * Based on:
11717  * Ext JS Library 1.1.1
11718  * Copyright(c) 2006-2007, Ext JS, LLC.
11719  *
11720  * Originally Released Under LGPL - original licence link has changed is not relivant.
11721  *
11722  * Fork - LGPL
11723  * <script type="text/javascript">
11724  */
11725
11726 /**
11727  * @class Roo.data.DataProxy
11728  * @extends Roo.data.Observable
11729  * This class is an abstract base class for implementations which provide retrieval of
11730  * unformatted data objects.<br>
11731  * <p>
11732  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11733  * (of the appropriate type which knows how to parse the data object) to provide a block of
11734  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11735  * <p>
11736  * Custom implementations must implement the load method as described in
11737  * {@link Roo.data.HttpProxy#load}.
11738  */
11739 Roo.data.DataProxy = function(){
11740     this.addEvents({
11741         /**
11742          * @event beforeload
11743          * Fires before a network request is made to retrieve a data object.
11744          * @param {Object} This DataProxy object.
11745          * @param {Object} params The params parameter to the load function.
11746          */
11747         beforeload : true,
11748         /**
11749          * @event load
11750          * Fires before the load method's callback is called.
11751          * @param {Object} This DataProxy object.
11752          * @param {Object} o The data object.
11753          * @param {Object} arg The callback argument object passed to the load function.
11754          */
11755         load : true,
11756         /**
11757          * @event loadexception
11758          * Fires if an Exception occurs during data retrieval.
11759          * @param {Object} This DataProxy object.
11760          * @param {Object} o The data object.
11761          * @param {Object} arg The callback argument object passed to the load function.
11762          * @param {Object} e The Exception.
11763          */
11764         loadexception : true
11765     });
11766     Roo.data.DataProxy.superclass.constructor.call(this);
11767 };
11768
11769 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11770
11771     /**
11772      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11773      */
11774 /*
11775  * Based on:
11776  * Ext JS Library 1.1.1
11777  * Copyright(c) 2006-2007, Ext JS, LLC.
11778  *
11779  * Originally Released Under LGPL - original licence link has changed is not relivant.
11780  *
11781  * Fork - LGPL
11782  * <script type="text/javascript">
11783  */
11784 /**
11785  * @class Roo.data.MemoryProxy
11786  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11787  * to the Reader when its load method is called.
11788  * @constructor
11789  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11790  */
11791 Roo.data.MemoryProxy = function(data){
11792     if (data.data) {
11793         data = data.data;
11794     }
11795     Roo.data.MemoryProxy.superclass.constructor.call(this);
11796     this.data = data;
11797 };
11798
11799 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11800     
11801     /**
11802      * Load data from the requested source (in this case an in-memory
11803      * data object passed to the constructor), read the data object into
11804      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11805      * process that block using the passed callback.
11806      * @param {Object} params This parameter is not used by the MemoryProxy class.
11807      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11808      * object into a block of Roo.data.Records.
11809      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11810      * The function must be passed <ul>
11811      * <li>The Record block object</li>
11812      * <li>The "arg" argument from the load function</li>
11813      * <li>A boolean success indicator</li>
11814      * </ul>
11815      * @param {Object} scope The scope in which to call the callback
11816      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11817      */
11818     load : function(params, reader, callback, scope, arg){
11819         params = params || {};
11820         var result;
11821         try {
11822             result = reader.readRecords(this.data);
11823         }catch(e){
11824             this.fireEvent("loadexception", this, arg, null, e);
11825             callback.call(scope, null, arg, false);
11826             return;
11827         }
11828         callback.call(scope, result, arg, true);
11829     },
11830     
11831     // private
11832     update : function(params, records){
11833         
11834     }
11835 });/*
11836  * Based on:
11837  * Ext JS Library 1.1.1
11838  * Copyright(c) 2006-2007, Ext JS, LLC.
11839  *
11840  * Originally Released Under LGPL - original licence link has changed is not relivant.
11841  *
11842  * Fork - LGPL
11843  * <script type="text/javascript">
11844  */
11845 /**
11846  * @class Roo.data.HttpProxy
11847  * @extends Roo.data.DataProxy
11848  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11849  * configured to reference a certain URL.<br><br>
11850  * <p>
11851  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11852  * from which the running page was served.<br><br>
11853  * <p>
11854  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11855  * <p>
11856  * Be aware that to enable the browser to parse an XML document, the server must set
11857  * the Content-Type header in the HTTP response to "text/xml".
11858  * @constructor
11859  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11860  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11861  * will be used to make the request.
11862  */
11863 Roo.data.HttpProxy = function(conn){
11864     Roo.data.HttpProxy.superclass.constructor.call(this);
11865     // is conn a conn config or a real conn?
11866     this.conn = conn;
11867     this.useAjax = !conn || !conn.events;
11868   
11869 };
11870
11871 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11872     // thse are take from connection...
11873     
11874     /**
11875      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11876      */
11877     /**
11878      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11879      * extra parameters to each request made by this object. (defaults to undefined)
11880      */
11881     /**
11882      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11883      *  to each request made by this object. (defaults to undefined)
11884      */
11885     /**
11886      * @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)
11887      */
11888     /**
11889      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11890      */
11891      /**
11892      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11893      * @type Boolean
11894      */
11895   
11896
11897     /**
11898      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11899      * @type Boolean
11900      */
11901     /**
11902      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11903      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11904      * a finer-grained basis than the DataProxy events.
11905      */
11906     getConnection : function(){
11907         return this.useAjax ? Roo.Ajax : this.conn;
11908     },
11909
11910     /**
11911      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11912      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11913      * process that block using the passed callback.
11914      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11915      * for the request to the remote server.
11916      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11917      * object into a block of Roo.data.Records.
11918      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11919      * The function must be passed <ul>
11920      * <li>The Record block object</li>
11921      * <li>The "arg" argument from the load function</li>
11922      * <li>A boolean success indicator</li>
11923      * </ul>
11924      * @param {Object} scope The scope in which to call the callback
11925      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11926      */
11927     load : function(params, reader, callback, scope, arg){
11928         if(this.fireEvent("beforeload", this, params) !== false){
11929             var  o = {
11930                 params : params || {},
11931                 request: {
11932                     callback : callback,
11933                     scope : scope,
11934                     arg : arg
11935                 },
11936                 reader: reader,
11937                 callback : this.loadResponse,
11938                 scope: this
11939             };
11940             if(this.useAjax){
11941                 Roo.applyIf(o, this.conn);
11942                 if(this.activeRequest){
11943                     Roo.Ajax.abort(this.activeRequest);
11944                 }
11945                 this.activeRequest = Roo.Ajax.request(o);
11946             }else{
11947                 this.conn.request(o);
11948             }
11949         }else{
11950             callback.call(scope||this, null, arg, false);
11951         }
11952     },
11953
11954     // private
11955     loadResponse : function(o, success, response){
11956         delete this.activeRequest;
11957         if(!success){
11958             this.fireEvent("loadexception", this, o, response);
11959             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11960             return;
11961         }
11962         var result;
11963         try {
11964             result = o.reader.read(response);
11965         }catch(e){
11966             this.fireEvent("loadexception", this, o, response, e);
11967             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11968             return;
11969         }
11970         
11971         this.fireEvent("load", this, o, o.request.arg);
11972         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11973     },
11974
11975     // private
11976     update : function(dataSet){
11977
11978     },
11979
11980     // private
11981     updateResponse : function(dataSet){
11982
11983     }
11984 });/*
11985  * Based on:
11986  * Ext JS Library 1.1.1
11987  * Copyright(c) 2006-2007, Ext JS, LLC.
11988  *
11989  * Originally Released Under LGPL - original licence link has changed is not relivant.
11990  *
11991  * Fork - LGPL
11992  * <script type="text/javascript">
11993  */
11994
11995 /**
11996  * @class Roo.data.ScriptTagProxy
11997  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11998  * other than the originating domain of the running page.<br><br>
11999  * <p>
12000  * <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
12001  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12002  * <p>
12003  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12004  * source code that is used as the source inside a &lt;script> tag.<br><br>
12005  * <p>
12006  * In order for the browser to process the returned data, the server must wrap the data object
12007  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12008  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12009  * depending on whether the callback name was passed:
12010  * <p>
12011  * <pre><code>
12012 boolean scriptTag = false;
12013 String cb = request.getParameter("callback");
12014 if (cb != null) {
12015     scriptTag = true;
12016     response.setContentType("text/javascript");
12017 } else {
12018     response.setContentType("application/x-json");
12019 }
12020 Writer out = response.getWriter();
12021 if (scriptTag) {
12022     out.write(cb + "(");
12023 }
12024 out.print(dataBlock.toJsonString());
12025 if (scriptTag) {
12026     out.write(");");
12027 }
12028 </pre></code>
12029  *
12030  * @constructor
12031  * @param {Object} config A configuration object.
12032  */
12033 Roo.data.ScriptTagProxy = function(config){
12034     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12035     Roo.apply(this, config);
12036     this.head = document.getElementsByTagName("head")[0];
12037 };
12038
12039 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12040
12041 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12042     /**
12043      * @cfg {String} url The URL from which to request the data object.
12044      */
12045     /**
12046      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12047      */
12048     timeout : 30000,
12049     /**
12050      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12051      * the server the name of the callback function set up by the load call to process the returned data object.
12052      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12053      * javascript output which calls this named function passing the data object as its only parameter.
12054      */
12055     callbackParam : "callback",
12056     /**
12057      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12058      * name to the request.
12059      */
12060     nocache : true,
12061
12062     /**
12063      * Load data from the configured URL, read the data object into
12064      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12065      * process that block using the passed callback.
12066      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12067      * for the request to the remote server.
12068      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12069      * object into a block of Roo.data.Records.
12070      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12071      * The function must be passed <ul>
12072      * <li>The Record block object</li>
12073      * <li>The "arg" argument from the load function</li>
12074      * <li>A boolean success indicator</li>
12075      * </ul>
12076      * @param {Object} scope The scope in which to call the callback
12077      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12078      */
12079     load : function(params, reader, callback, scope, arg){
12080         if(this.fireEvent("beforeload", this, params) !== false){
12081
12082             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12083
12084             var url = this.url;
12085             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12086             if(this.nocache){
12087                 url += "&_dc=" + (new Date().getTime());
12088             }
12089             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12090             var trans = {
12091                 id : transId,
12092                 cb : "stcCallback"+transId,
12093                 scriptId : "stcScript"+transId,
12094                 params : params,
12095                 arg : arg,
12096                 url : url,
12097                 callback : callback,
12098                 scope : scope,
12099                 reader : reader
12100             };
12101             var conn = this;
12102
12103             window[trans.cb] = function(o){
12104                 conn.handleResponse(o, trans);
12105             };
12106
12107             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12108
12109             if(this.autoAbort !== false){
12110                 this.abort();
12111             }
12112
12113             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12114
12115             var script = document.createElement("script");
12116             script.setAttribute("src", url);
12117             script.setAttribute("type", "text/javascript");
12118             script.setAttribute("id", trans.scriptId);
12119             this.head.appendChild(script);
12120
12121             this.trans = trans;
12122         }else{
12123             callback.call(scope||this, null, arg, false);
12124         }
12125     },
12126
12127     // private
12128     isLoading : function(){
12129         return this.trans ? true : false;
12130     },
12131
12132     /**
12133      * Abort the current server request.
12134      */
12135     abort : function(){
12136         if(this.isLoading()){
12137             this.destroyTrans(this.trans);
12138         }
12139     },
12140
12141     // private
12142     destroyTrans : function(trans, isLoaded){
12143         this.head.removeChild(document.getElementById(trans.scriptId));
12144         clearTimeout(trans.timeoutId);
12145         if(isLoaded){
12146             window[trans.cb] = undefined;
12147             try{
12148                 delete window[trans.cb];
12149             }catch(e){}
12150         }else{
12151             // if hasn't been loaded, wait for load to remove it to prevent script error
12152             window[trans.cb] = function(){
12153                 window[trans.cb] = undefined;
12154                 try{
12155                     delete window[trans.cb];
12156                 }catch(e){}
12157             };
12158         }
12159     },
12160
12161     // private
12162     handleResponse : function(o, trans){
12163         this.trans = false;
12164         this.destroyTrans(trans, true);
12165         var result;
12166         try {
12167             result = trans.reader.readRecords(o);
12168         }catch(e){
12169             this.fireEvent("loadexception", this, o, trans.arg, e);
12170             trans.callback.call(trans.scope||window, null, trans.arg, false);
12171             return;
12172         }
12173         this.fireEvent("load", this, o, trans.arg);
12174         trans.callback.call(trans.scope||window, result, trans.arg, true);
12175     },
12176
12177     // private
12178     handleFailure : function(trans){
12179         this.trans = false;
12180         this.destroyTrans(trans, false);
12181         this.fireEvent("loadexception", this, null, trans.arg);
12182         trans.callback.call(trans.scope||window, null, trans.arg, false);
12183     }
12184 });/*
12185  * Based on:
12186  * Ext JS Library 1.1.1
12187  * Copyright(c) 2006-2007, Ext JS, LLC.
12188  *
12189  * Originally Released Under LGPL - original licence link has changed is not relivant.
12190  *
12191  * Fork - LGPL
12192  * <script type="text/javascript">
12193  */
12194
12195 /**
12196  * @class Roo.data.JsonReader
12197  * @extends Roo.data.DataReader
12198  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12199  * based on mappings in a provided Roo.data.Record constructor.
12200  * 
12201  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12202  * in the reply previously. 
12203  * 
12204  * <p>
12205  * Example code:
12206  * <pre><code>
12207 var RecordDef = Roo.data.Record.create([
12208     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12209     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12210 ]);
12211 var myReader = new Roo.data.JsonReader({
12212     totalProperty: "results",    // The property which contains the total dataset size (optional)
12213     root: "rows",                // The property which contains an Array of row objects
12214     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12215 }, RecordDef);
12216 </code></pre>
12217  * <p>
12218  * This would consume a JSON file like this:
12219  * <pre><code>
12220 { 'results': 2, 'rows': [
12221     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12222     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12223 }
12224 </code></pre>
12225  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12226  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12227  * paged from the remote server.
12228  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12229  * @cfg {String} root name of the property which contains the Array of row objects.
12230  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12231  * @cfg {Array} fields Array of field definition objects
12232  * @constructor
12233  * Create a new JsonReader
12234  * @param {Object} meta Metadata configuration options
12235  * @param {Object} recordType Either an Array of field definition objects,
12236  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12237  */
12238 Roo.data.JsonReader = function(meta, recordType){
12239     
12240     meta = meta || {};
12241     // set some defaults:
12242     Roo.applyIf(meta, {
12243         totalProperty: 'total',
12244         successProperty : 'success',
12245         root : 'data',
12246         id : 'id'
12247     });
12248     
12249     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12250 };
12251 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12252     
12253     /**
12254      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12255      * Used by Store query builder to append _requestMeta to params.
12256      * 
12257      */
12258     metaFromRemote : false,
12259     /**
12260      * This method is only used by a DataProxy which has retrieved data from a remote server.
12261      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12262      * @return {Object} data A data block which is used by an Roo.data.Store object as
12263      * a cache of Roo.data.Records.
12264      */
12265     read : function(response){
12266         var json = response.responseText;
12267        
12268         var o = /* eval:var:o */ eval("("+json+")");
12269         if(!o) {
12270             throw {message: "JsonReader.read: Json object not found"};
12271         }
12272         
12273         if(o.metaData){
12274             
12275             delete this.ef;
12276             this.metaFromRemote = true;
12277             this.meta = o.metaData;
12278             this.recordType = Roo.data.Record.create(o.metaData.fields);
12279             this.onMetaChange(this.meta, this.recordType, o);
12280         }
12281         return this.readRecords(o);
12282     },
12283
12284     // private function a store will implement
12285     onMetaChange : function(meta, recordType, o){
12286
12287     },
12288
12289     /**
12290          * @ignore
12291          */
12292     simpleAccess: function(obj, subsc) {
12293         return obj[subsc];
12294     },
12295
12296         /**
12297          * @ignore
12298          */
12299     getJsonAccessor: function(){
12300         var re = /[\[\.]/;
12301         return function(expr) {
12302             try {
12303                 return(re.test(expr))
12304                     ? new Function("obj", "return obj." + expr)
12305                     : function(obj){
12306                         return obj[expr];
12307                     };
12308             } catch(e){}
12309             return Roo.emptyFn;
12310         };
12311     }(),
12312
12313     /**
12314      * Create a data block containing Roo.data.Records from an XML document.
12315      * @param {Object} o An object which contains an Array of row objects in the property specified
12316      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12317      * which contains the total size of the dataset.
12318      * @return {Object} data A data block which is used by an Roo.data.Store object as
12319      * a cache of Roo.data.Records.
12320      */
12321     readRecords : function(o){
12322         /**
12323          * After any data loads, the raw JSON data is available for further custom processing.
12324          * @type Object
12325          */
12326         this.o = o;
12327         var s = this.meta, Record = this.recordType,
12328             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12329
12330 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12331         if (!this.ef) {
12332             if(s.totalProperty) {
12333                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12334                 }
12335                 if(s.successProperty) {
12336                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12337                 }
12338                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12339                 if (s.id) {
12340                         var g = this.getJsonAccessor(s.id);
12341                         this.getId = function(rec) {
12342                                 var r = g(rec);  
12343                                 return (r === undefined || r === "") ? null : r;
12344                         };
12345                 } else {
12346                         this.getId = function(){return null;};
12347                 }
12348             this.ef = [];
12349             for(var jj = 0; jj < fl; jj++){
12350                 f = fi[jj];
12351                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12352                 this.ef[jj] = this.getJsonAccessor(map);
12353             }
12354         }
12355
12356         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12357         if(s.totalProperty){
12358             var vt = parseInt(this.getTotal(o), 10);
12359             if(!isNaN(vt)){
12360                 totalRecords = vt;
12361             }
12362         }
12363         if(s.successProperty){
12364             var vs = this.getSuccess(o);
12365             if(vs === false || vs === 'false'){
12366                 success = false;
12367             }
12368         }
12369         var records = [];
12370         for(var i = 0; i < c; i++){
12371                 var n = root[i];
12372             var values = {};
12373             var id = this.getId(n);
12374             for(var j = 0; j < fl; j++){
12375                 f = fi[j];
12376             var v = this.ef[j](n);
12377             if (!f.convert) {
12378                 Roo.log('missing convert for ' + f.name);
12379                 Roo.log(f);
12380                 continue;
12381             }
12382             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12383             }
12384             var record = new Record(values, id);
12385             record.json = n;
12386             records[i] = record;
12387         }
12388         return {
12389             raw : o,
12390             success : success,
12391             records : records,
12392             totalRecords : totalRecords
12393         };
12394     }
12395 });/*
12396  * Based on:
12397  * Ext JS Library 1.1.1
12398  * Copyright(c) 2006-2007, Ext JS, LLC.
12399  *
12400  * Originally Released Under LGPL - original licence link has changed is not relivant.
12401  *
12402  * Fork - LGPL
12403  * <script type="text/javascript">
12404  */
12405
12406 /**
12407  * @class Roo.data.ArrayReader
12408  * @extends Roo.data.DataReader
12409  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12410  * Each element of that Array represents a row of data fields. The
12411  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12412  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12413  * <p>
12414  * Example code:.
12415  * <pre><code>
12416 var RecordDef = Roo.data.Record.create([
12417     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12418     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12419 ]);
12420 var myReader = new Roo.data.ArrayReader({
12421     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12422 }, RecordDef);
12423 </code></pre>
12424  * <p>
12425  * This would consume an Array like this:
12426  * <pre><code>
12427 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12428   </code></pre>
12429  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12430  * @constructor
12431  * Create a new JsonReader
12432  * @param {Object} meta Metadata configuration options.
12433  * @param {Object} recordType Either an Array of field definition objects
12434  * as specified to {@link Roo.data.Record#create},
12435  * or an {@link Roo.data.Record} object
12436  * created using {@link Roo.data.Record#create}.
12437  */
12438 Roo.data.ArrayReader = function(meta, recordType){
12439     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12440 };
12441
12442 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12443     /**
12444      * Create a data block containing Roo.data.Records from an XML document.
12445      * @param {Object} o An Array of row objects which represents the dataset.
12446      * @return {Object} data A data block which is used by an Roo.data.Store object as
12447      * a cache of Roo.data.Records.
12448      */
12449     readRecords : function(o){
12450         var sid = this.meta ? this.meta.id : null;
12451         var recordType = this.recordType, fields = recordType.prototype.fields;
12452         var records = [];
12453         var root = o;
12454             for(var i = 0; i < root.length; i++){
12455                     var n = root[i];
12456                 var values = {};
12457                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12458                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12459                 var f = fields.items[j];
12460                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12461                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12462                 v = f.convert(v);
12463                 values[f.name] = v;
12464             }
12465                 var record = new recordType(values, id);
12466                 record.json = n;
12467                 records[records.length] = record;
12468             }
12469             return {
12470                 records : records,
12471                 totalRecords : records.length
12472             };
12473     }
12474 });/*
12475  * - LGPL
12476  * * 
12477  */
12478
12479 /**
12480  * @class Roo.bootstrap.ComboBox
12481  * @extends Roo.bootstrap.TriggerField
12482  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12483  * @cfg {Boolean} append (true|false) default false
12484  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12485  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12486  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12487  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12488  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12489  * @cfg {Boolean} animate default true
12490  * @cfg {Boolean} emptyResultText only for touch device
12491  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12492  * @cfg {String} emptyTitle default ''
12493  * @constructor
12494  * Create a new ComboBox.
12495  * @param {Object} config Configuration options
12496  */
12497 Roo.bootstrap.ComboBox = function(config){
12498     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12499     this.addEvents({
12500         /**
12501          * @event expand
12502          * Fires when the dropdown list is expanded
12503         * @param {Roo.bootstrap.ComboBox} combo This combo box
12504         */
12505         'expand' : true,
12506         /**
12507          * @event collapse
12508          * Fires when the dropdown list is collapsed
12509         * @param {Roo.bootstrap.ComboBox} combo This combo box
12510         */
12511         'collapse' : true,
12512         /**
12513          * @event beforeselect
12514          * Fires before a list item is selected. Return false to cancel the selection.
12515         * @param {Roo.bootstrap.ComboBox} combo This combo box
12516         * @param {Roo.data.Record} record The data record returned from the underlying store
12517         * @param {Number} index The index of the selected item in the dropdown list
12518         */
12519         'beforeselect' : true,
12520         /**
12521          * @event select
12522          * Fires when a list item is selected
12523         * @param {Roo.bootstrap.ComboBox} combo This combo box
12524         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12525         * @param {Number} index The index of the selected item in the dropdown list
12526         */
12527         'select' : true,
12528         /**
12529          * @event beforequery
12530          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12531          * The event object passed has these properties:
12532         * @param {Roo.bootstrap.ComboBox} combo This combo box
12533         * @param {String} query The query
12534         * @param {Boolean} forceAll true to force "all" query
12535         * @param {Boolean} cancel true to cancel the query
12536         * @param {Object} e The query event object
12537         */
12538         'beforequery': true,
12539          /**
12540          * @event add
12541          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12542         * @param {Roo.bootstrap.ComboBox} combo This combo box
12543         */
12544         'add' : true,
12545         /**
12546          * @event edit
12547          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12548         * @param {Roo.bootstrap.ComboBox} combo This combo box
12549         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12550         */
12551         'edit' : true,
12552         /**
12553          * @event remove
12554          * Fires when the remove value from the combobox array
12555         * @param {Roo.bootstrap.ComboBox} combo This combo box
12556         */
12557         'remove' : true,
12558         /**
12559          * @event afterremove
12560          * Fires when the remove value from the combobox array
12561         * @param {Roo.bootstrap.ComboBox} combo This combo box
12562         */
12563         'afterremove' : true,
12564         /**
12565          * @event specialfilter
12566          * Fires when specialfilter
12567             * @param {Roo.bootstrap.ComboBox} combo This combo box
12568             */
12569         'specialfilter' : true,
12570         /**
12571          * @event tick
12572          * Fires when tick the element
12573             * @param {Roo.bootstrap.ComboBox} combo This combo box
12574             */
12575         'tick' : true,
12576         /**
12577          * @event touchviewdisplay
12578          * Fires when touch view require special display (default is using displayField)
12579             * @param {Roo.bootstrap.ComboBox} combo This combo box
12580             * @param {Object} cfg set html .
12581             */
12582         'touchviewdisplay' : true
12583         
12584     });
12585     
12586     this.item = [];
12587     this.tickItems = [];
12588     
12589     this.selectedIndex = -1;
12590     if(this.mode == 'local'){
12591         if(config.queryDelay === undefined){
12592             this.queryDelay = 10;
12593         }
12594         if(config.minChars === undefined){
12595             this.minChars = 0;
12596         }
12597     }
12598 };
12599
12600 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12601      
12602     /**
12603      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12604      * rendering into an Roo.Editor, defaults to false)
12605      */
12606     /**
12607      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12608      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12609      */
12610     /**
12611      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12612      */
12613     /**
12614      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12615      * the dropdown list (defaults to undefined, with no header element)
12616      */
12617
12618      /**
12619      * @cfg {String/Roo.Template} tpl The template to use to render the output
12620      */
12621      
12622      /**
12623      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12624      */
12625     listWidth: undefined,
12626     /**
12627      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12628      * mode = 'remote' or 'text' if mode = 'local')
12629      */
12630     displayField: undefined,
12631     
12632     /**
12633      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12634      * mode = 'remote' or 'value' if mode = 'local'). 
12635      * Note: use of a valueField requires the user make a selection
12636      * in order for a value to be mapped.
12637      */
12638     valueField: undefined,
12639     /**
12640      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12641      */
12642     modalTitle : '',
12643     
12644     /**
12645      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12646      * field's data value (defaults to the underlying DOM element's name)
12647      */
12648     hiddenName: undefined,
12649     /**
12650      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12651      */
12652     listClass: '',
12653     /**
12654      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12655      */
12656     selectedClass: 'active',
12657     
12658     /**
12659      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12660      */
12661     shadow:'sides',
12662     /**
12663      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12664      * anchor positions (defaults to 'tl-bl')
12665      */
12666     listAlign: 'tl-bl?',
12667     /**
12668      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12669      */
12670     maxHeight: 300,
12671     /**
12672      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12673      * query specified by the allQuery config option (defaults to 'query')
12674      */
12675     triggerAction: 'query',
12676     /**
12677      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12678      * (defaults to 4, does not apply if editable = false)
12679      */
12680     minChars : 4,
12681     /**
12682      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12683      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12684      */
12685     typeAhead: false,
12686     /**
12687      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12688      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12689      */
12690     queryDelay: 500,
12691     /**
12692      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12693      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12694      */
12695     pageSize: 0,
12696     /**
12697      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12698      * when editable = true (defaults to false)
12699      */
12700     selectOnFocus:false,
12701     /**
12702      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12703      */
12704     queryParam: 'query',
12705     /**
12706      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12707      * when mode = 'remote' (defaults to 'Loading...')
12708      */
12709     loadingText: 'Loading...',
12710     /**
12711      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12712      */
12713     resizable: false,
12714     /**
12715      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12716      */
12717     handleHeight : 8,
12718     /**
12719      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12720      * traditional select (defaults to true)
12721      */
12722     editable: true,
12723     /**
12724      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12725      */
12726     allQuery: '',
12727     /**
12728      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12729      */
12730     mode: 'remote',
12731     /**
12732      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12733      * listWidth has a higher value)
12734      */
12735     minListWidth : 70,
12736     /**
12737      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12738      * allow the user to set arbitrary text into the field (defaults to false)
12739      */
12740     forceSelection:false,
12741     /**
12742      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12743      * if typeAhead = true (defaults to 250)
12744      */
12745     typeAheadDelay : 250,
12746     /**
12747      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12748      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12749      */
12750     valueNotFoundText : undefined,
12751     /**
12752      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12753      */
12754     blockFocus : false,
12755     
12756     /**
12757      * @cfg {Boolean} disableClear Disable showing of clear button.
12758      */
12759     disableClear : false,
12760     /**
12761      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12762      */
12763     alwaysQuery : false,
12764     
12765     /**
12766      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12767      */
12768     multiple : false,
12769     
12770     /**
12771      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12772      */
12773     invalidClass : "has-warning",
12774     
12775     /**
12776      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12777      */
12778     validClass : "has-success",
12779     
12780     /**
12781      * @cfg {Boolean} specialFilter (true|false) special filter default false
12782      */
12783     specialFilter : false,
12784     
12785     /**
12786      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12787      */
12788     mobileTouchView : true,
12789     
12790     /**
12791      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12792      */
12793     useNativeIOS : false,
12794     
12795     ios_options : false,
12796     
12797     //private
12798     addicon : false,
12799     editicon: false,
12800     
12801     page: 0,
12802     hasQuery: false,
12803     append: false,
12804     loadNext: false,
12805     autoFocus : true,
12806     tickable : false,
12807     btnPosition : 'right',
12808     triggerList : true,
12809     showToggleBtn : true,
12810     animate : true,
12811     emptyResultText: 'Empty',
12812     triggerText : 'Select',
12813     emptyTitle : '',
12814     
12815     // element that contains real text value.. (when hidden is used..)
12816     
12817     getAutoCreate : function()
12818     {   
12819         var cfg = false;
12820         //render
12821         /*
12822          * Render classic select for iso
12823          */
12824         
12825         if(Roo.isIOS && this.useNativeIOS){
12826             cfg = this.getAutoCreateNativeIOS();
12827             return cfg;
12828         }
12829         
12830         /*
12831          * Touch Devices
12832          */
12833         
12834         if(Roo.isTouch && this.mobileTouchView){
12835             cfg = this.getAutoCreateTouchView();
12836             return cfg;;
12837         }
12838         
12839         /*
12840          *  Normal ComboBox
12841          */
12842         if(!this.tickable){
12843             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12844             return cfg;
12845         }
12846         
12847         /*
12848          *  ComboBox with tickable selections
12849          */
12850              
12851         var align = this.labelAlign || this.parentLabelAlign();
12852         
12853         cfg = {
12854             cls : 'form-group roo-combobox-tickable' //input-group
12855         };
12856         
12857         var btn_text_select = '';
12858         var btn_text_done = '';
12859         var btn_text_cancel = '';
12860         
12861         if (this.btn_text_show) {
12862             btn_text_select = 'Select';
12863             btn_text_done = 'Done';
12864             btn_text_cancel = 'Cancel'; 
12865         }
12866         
12867         var buttons = {
12868             tag : 'div',
12869             cls : 'tickable-buttons',
12870             cn : [
12871                 {
12872                     tag : 'button',
12873                     type : 'button',
12874                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12875                     //html : this.triggerText
12876                     html: btn_text_select
12877                 },
12878                 {
12879                     tag : 'button',
12880                     type : 'button',
12881                     name : 'ok',
12882                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12883                     //html : 'Done'
12884                     html: btn_text_done
12885                 },
12886                 {
12887                     tag : 'button',
12888                     type : 'button',
12889                     name : 'cancel',
12890                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12891                     //html : 'Cancel'
12892                     html: btn_text_cancel
12893                 }
12894             ]
12895         };
12896         
12897         if(this.editable){
12898             buttons.cn.unshift({
12899                 tag: 'input',
12900                 cls: 'roo-select2-search-field-input'
12901             });
12902         }
12903         
12904         var _this = this;
12905         
12906         Roo.each(buttons.cn, function(c){
12907             if (_this.size) {
12908                 c.cls += ' btn-' + _this.size;
12909             }
12910
12911             if (_this.disabled) {
12912                 c.disabled = true;
12913             }
12914         });
12915         
12916         var box = {
12917             tag: 'div',
12918             cn: [
12919                 {
12920                     tag: 'input',
12921                     type : 'hidden',
12922                     cls: 'form-hidden-field'
12923                 },
12924                 {
12925                     tag: 'ul',
12926                     cls: 'roo-select2-choices',
12927                     cn:[
12928                         {
12929                             tag: 'li',
12930                             cls: 'roo-select2-search-field',
12931                             cn: [
12932                                 buttons
12933                             ]
12934                         }
12935                     ]
12936                 }
12937             ]
12938         };
12939         
12940         var combobox = {
12941             cls: 'roo-select2-container input-group roo-select2-container-multi',
12942             cn: [
12943                 box
12944 //                {
12945 //                    tag: 'ul',
12946 //                    cls: 'typeahead typeahead-long dropdown-menu',
12947 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12948 //                }
12949             ]
12950         };
12951         
12952         if(this.hasFeedback && !this.allowBlank){
12953             
12954             var feedback = {
12955                 tag: 'span',
12956                 cls: 'glyphicon form-control-feedback'
12957             };
12958
12959             combobox.cn.push(feedback);
12960         }
12961         
12962         
12963         if (align ==='left' && this.fieldLabel.length) {
12964             
12965             cfg.cls += ' roo-form-group-label-left';
12966             
12967             cfg.cn = [
12968                 {
12969                     tag : 'i',
12970                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12971                     tooltip : 'This field is required'
12972                 },
12973                 {
12974                     tag: 'label',
12975                     'for' :  id,
12976                     cls : 'control-label',
12977                     html : this.fieldLabel
12978
12979                 },
12980                 {
12981                     cls : "", 
12982                     cn: [
12983                         combobox
12984                     ]
12985                 }
12986
12987             ];
12988             
12989             var labelCfg = cfg.cn[1];
12990             var contentCfg = cfg.cn[2];
12991             
12992
12993             if(this.indicatorpos == 'right'){
12994                 
12995                 cfg.cn = [
12996                     {
12997                         tag: 'label',
12998                         'for' :  id,
12999                         cls : 'control-label',
13000                         cn : [
13001                             {
13002                                 tag : 'span',
13003                                 html : this.fieldLabel
13004                             },
13005                             {
13006                                 tag : 'i',
13007                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13008                                 tooltip : 'This field is required'
13009                             }
13010                         ]
13011                     },
13012                     {
13013                         cls : "",
13014                         cn: [
13015                             combobox
13016                         ]
13017                     }
13018
13019                 ];
13020                 
13021                 
13022                 
13023                 labelCfg = cfg.cn[0];
13024                 contentCfg = cfg.cn[1];
13025             
13026             }
13027             
13028             if(this.labelWidth > 12){
13029                 labelCfg.style = "width: " + this.labelWidth + 'px';
13030             }
13031             
13032             if(this.labelWidth < 13 && this.labelmd == 0){
13033                 this.labelmd = this.labelWidth;
13034             }
13035             
13036             if(this.labellg > 0){
13037                 labelCfg.cls += ' col-lg-' + this.labellg;
13038                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13039             }
13040             
13041             if(this.labelmd > 0){
13042                 labelCfg.cls += ' col-md-' + this.labelmd;
13043                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13044             }
13045             
13046             if(this.labelsm > 0){
13047                 labelCfg.cls += ' col-sm-' + this.labelsm;
13048                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13049             }
13050             
13051             if(this.labelxs > 0){
13052                 labelCfg.cls += ' col-xs-' + this.labelxs;
13053                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13054             }
13055                 
13056                 
13057         } else if ( this.fieldLabel.length) {
13058 //                Roo.log(" label");
13059                  cfg.cn = [
13060                     {
13061                         tag : 'i',
13062                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13063                         tooltip : 'This field is required'
13064                     },
13065                     {
13066                         tag: 'label',
13067                         //cls : 'input-group-addon',
13068                         html : this.fieldLabel
13069                     },
13070                     combobox
13071                 ];
13072                 
13073                 if(this.indicatorpos == 'right'){
13074                     cfg.cn = [
13075                         {
13076                             tag: 'label',
13077                             //cls : 'input-group-addon',
13078                             html : this.fieldLabel
13079                         },
13080                         {
13081                             tag : 'i',
13082                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13083                             tooltip : 'This field is required'
13084                         },
13085                         combobox
13086                     ];
13087                     
13088                 }
13089
13090         } else {
13091             
13092 //                Roo.log(" no label && no align");
13093                 cfg = combobox
13094                      
13095                 
13096         }
13097          
13098         var settings=this;
13099         ['xs','sm','md','lg'].map(function(size){
13100             if (settings[size]) {
13101                 cfg.cls += ' col-' + size + '-' + settings[size];
13102             }
13103         });
13104         
13105         return cfg;
13106         
13107     },
13108     
13109     _initEventsCalled : false,
13110     
13111     // private
13112     initEvents: function()
13113     {   
13114         if (this._initEventsCalled) { // as we call render... prevent looping...
13115             return;
13116         }
13117         this._initEventsCalled = true;
13118         
13119         if (!this.store) {
13120             throw "can not find store for combo";
13121         }
13122         
13123         this.indicator = this.indicatorEl();
13124         
13125         this.store = Roo.factory(this.store, Roo.data);
13126         this.store.parent = this;
13127         
13128         // if we are building from html. then this element is so complex, that we can not really
13129         // use the rendered HTML.
13130         // so we have to trash and replace the previous code.
13131         if (Roo.XComponent.build_from_html) {
13132             // remove this element....
13133             var e = this.el.dom, k=0;
13134             while (e ) { e = e.previousSibling;  ++k;}
13135
13136             this.el.remove();
13137             
13138             this.el=false;
13139             this.rendered = false;
13140             
13141             this.render(this.parent().getChildContainer(true), k);
13142         }
13143         
13144         if(Roo.isIOS && this.useNativeIOS){
13145             this.initIOSView();
13146             return;
13147         }
13148         
13149         /*
13150          * Touch Devices
13151          */
13152         
13153         if(Roo.isTouch && this.mobileTouchView){
13154             this.initTouchView();
13155             return;
13156         }
13157         
13158         if(this.tickable){
13159             this.initTickableEvents();
13160             return;
13161         }
13162         
13163         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13164         
13165         if(this.hiddenName){
13166             
13167             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13168             
13169             this.hiddenField.dom.value =
13170                 this.hiddenValue !== undefined ? this.hiddenValue :
13171                 this.value !== undefined ? this.value : '';
13172
13173             // prevent input submission
13174             this.el.dom.removeAttribute('name');
13175             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13176              
13177              
13178         }
13179         //if(Roo.isGecko){
13180         //    this.el.dom.setAttribute('autocomplete', 'off');
13181         //}
13182         
13183         var cls = 'x-combo-list';
13184         
13185         //this.list = new Roo.Layer({
13186         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13187         //});
13188         
13189         var _this = this;
13190         
13191         (function(){
13192             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13193             _this.list.setWidth(lw);
13194         }).defer(100);
13195         
13196         this.list.on('mouseover', this.onViewOver, this);
13197         this.list.on('mousemove', this.onViewMove, this);
13198         this.list.on('scroll', this.onViewScroll, this);
13199         
13200         /*
13201         this.list.swallowEvent('mousewheel');
13202         this.assetHeight = 0;
13203
13204         if(this.title){
13205             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13206             this.assetHeight += this.header.getHeight();
13207         }
13208
13209         this.innerList = this.list.createChild({cls:cls+'-inner'});
13210         this.innerList.on('mouseover', this.onViewOver, this);
13211         this.innerList.on('mousemove', this.onViewMove, this);
13212         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13213         
13214         if(this.allowBlank && !this.pageSize && !this.disableClear){
13215             this.footer = this.list.createChild({cls:cls+'-ft'});
13216             this.pageTb = new Roo.Toolbar(this.footer);
13217            
13218         }
13219         if(this.pageSize){
13220             this.footer = this.list.createChild({cls:cls+'-ft'});
13221             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13222                     {pageSize: this.pageSize});
13223             
13224         }
13225         
13226         if (this.pageTb && this.allowBlank && !this.disableClear) {
13227             var _this = this;
13228             this.pageTb.add(new Roo.Toolbar.Fill(), {
13229                 cls: 'x-btn-icon x-btn-clear',
13230                 text: '&#160;',
13231                 handler: function()
13232                 {
13233                     _this.collapse();
13234                     _this.clearValue();
13235                     _this.onSelect(false, -1);
13236                 }
13237             });
13238         }
13239         if (this.footer) {
13240             this.assetHeight += this.footer.getHeight();
13241         }
13242         */
13243             
13244         if(!this.tpl){
13245             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13246         }
13247
13248         this.view = new Roo.View(this.list, this.tpl, {
13249             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13250         });
13251         //this.view.wrapEl.setDisplayed(false);
13252         this.view.on('click', this.onViewClick, this);
13253         
13254         
13255         this.store.on('beforeload', this.onBeforeLoad, this);
13256         this.store.on('load', this.onLoad, this);
13257         this.store.on('loadexception', this.onLoadException, this);
13258         /*
13259         if(this.resizable){
13260             this.resizer = new Roo.Resizable(this.list,  {
13261                pinned:true, handles:'se'
13262             });
13263             this.resizer.on('resize', function(r, w, h){
13264                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13265                 this.listWidth = w;
13266                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13267                 this.restrictHeight();
13268             }, this);
13269             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13270         }
13271         */
13272         if(!this.editable){
13273             this.editable = true;
13274             this.setEditable(false);
13275         }
13276         
13277         /*
13278         
13279         if (typeof(this.events.add.listeners) != 'undefined') {
13280             
13281             this.addicon = this.wrap.createChild(
13282                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13283        
13284             this.addicon.on('click', function(e) {
13285                 this.fireEvent('add', this);
13286             }, this);
13287         }
13288         if (typeof(this.events.edit.listeners) != 'undefined') {
13289             
13290             this.editicon = this.wrap.createChild(
13291                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13292             if (this.addicon) {
13293                 this.editicon.setStyle('margin-left', '40px');
13294             }
13295             this.editicon.on('click', function(e) {
13296                 
13297                 // we fire even  if inothing is selected..
13298                 this.fireEvent('edit', this, this.lastData );
13299                 
13300             }, this);
13301         }
13302         */
13303         
13304         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13305             "up" : function(e){
13306                 this.inKeyMode = true;
13307                 this.selectPrev();
13308             },
13309
13310             "down" : function(e){
13311                 if(!this.isExpanded()){
13312                     this.onTriggerClick();
13313                 }else{
13314                     this.inKeyMode = true;
13315                     this.selectNext();
13316                 }
13317             },
13318
13319             "enter" : function(e){
13320 //                this.onViewClick();
13321                 //return true;
13322                 this.collapse();
13323                 
13324                 if(this.fireEvent("specialkey", this, e)){
13325                     this.onViewClick(false);
13326                 }
13327                 
13328                 return true;
13329             },
13330
13331             "esc" : function(e){
13332                 this.collapse();
13333             },
13334
13335             "tab" : function(e){
13336                 this.collapse();
13337                 
13338                 if(this.fireEvent("specialkey", this, e)){
13339                     this.onViewClick(false);
13340                 }
13341                 
13342                 return true;
13343             },
13344
13345             scope : this,
13346
13347             doRelay : function(foo, bar, hname){
13348                 if(hname == 'down' || this.scope.isExpanded()){
13349                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13350                 }
13351                 return true;
13352             },
13353
13354             forceKeyDown: true
13355         });
13356         
13357         
13358         this.queryDelay = Math.max(this.queryDelay || 10,
13359                 this.mode == 'local' ? 10 : 250);
13360         
13361         
13362         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13363         
13364         if(this.typeAhead){
13365             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13366         }
13367         if(this.editable !== false){
13368             this.inputEl().on("keyup", this.onKeyUp, this);
13369         }
13370         if(this.forceSelection){
13371             this.inputEl().on('blur', this.doForce, this);
13372         }
13373         
13374         if(this.multiple){
13375             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13376             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13377         }
13378     },
13379     
13380     initTickableEvents: function()
13381     {   
13382         this.createList();
13383         
13384         if(this.hiddenName){
13385             
13386             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13387             
13388             this.hiddenField.dom.value =
13389                 this.hiddenValue !== undefined ? this.hiddenValue :
13390                 this.value !== undefined ? this.value : '';
13391
13392             // prevent input submission
13393             this.el.dom.removeAttribute('name');
13394             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13395              
13396              
13397         }
13398         
13399 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13400         
13401         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13402         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13403         if(this.triggerList){
13404             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13405         }
13406          
13407         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13408         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13409         
13410         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13411         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13412         
13413         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13414         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13415         
13416         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13417         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13418         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13419         
13420         this.okBtn.hide();
13421         this.cancelBtn.hide();
13422         
13423         var _this = this;
13424         
13425         (function(){
13426             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13427             _this.list.setWidth(lw);
13428         }).defer(100);
13429         
13430         this.list.on('mouseover', this.onViewOver, this);
13431         this.list.on('mousemove', this.onViewMove, this);
13432         
13433         this.list.on('scroll', this.onViewScroll, this);
13434         
13435         if(!this.tpl){
13436             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>';
13437         }
13438
13439         this.view = new Roo.View(this.list, this.tpl, {
13440             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13441         });
13442         
13443         //this.view.wrapEl.setDisplayed(false);
13444         this.view.on('click', this.onViewClick, this);
13445         
13446         
13447         
13448         this.store.on('beforeload', this.onBeforeLoad, this);
13449         this.store.on('load', this.onLoad, this);
13450         this.store.on('loadexception', this.onLoadException, this);
13451         
13452         if(this.editable){
13453             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13454                 "up" : function(e){
13455                     this.inKeyMode = true;
13456                     this.selectPrev();
13457                 },
13458
13459                 "down" : function(e){
13460                     this.inKeyMode = true;
13461                     this.selectNext();
13462                 },
13463
13464                 "enter" : function(e){
13465                     if(this.fireEvent("specialkey", this, e)){
13466                         this.onViewClick(false);
13467                     }
13468                     
13469                     return true;
13470                 },
13471
13472                 "esc" : function(e){
13473                     this.onTickableFooterButtonClick(e, false, false);
13474                 },
13475
13476                 "tab" : function(e){
13477                     this.fireEvent("specialkey", this, e);
13478                     
13479                     this.onTickableFooterButtonClick(e, false, false);
13480                     
13481                     return true;
13482                 },
13483
13484                 scope : this,
13485
13486                 doRelay : function(e, fn, key){
13487                     if(this.scope.isExpanded()){
13488                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13489                     }
13490                     return true;
13491                 },
13492
13493                 forceKeyDown: true
13494             });
13495         }
13496         
13497         this.queryDelay = Math.max(this.queryDelay || 10,
13498                 this.mode == 'local' ? 10 : 250);
13499         
13500         
13501         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13502         
13503         if(this.typeAhead){
13504             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13505         }
13506         
13507         if(this.editable !== false){
13508             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13509         }
13510         
13511         this.indicator = this.indicatorEl();
13512         
13513         if(this.indicator){
13514             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13515             this.indicator.hide();
13516         }
13517         
13518     },
13519
13520     onDestroy : function(){
13521         if(this.view){
13522             this.view.setStore(null);
13523             this.view.el.removeAllListeners();
13524             this.view.el.remove();
13525             this.view.purgeListeners();
13526         }
13527         if(this.list){
13528             this.list.dom.innerHTML  = '';
13529         }
13530         
13531         if(this.store){
13532             this.store.un('beforeload', this.onBeforeLoad, this);
13533             this.store.un('load', this.onLoad, this);
13534             this.store.un('loadexception', this.onLoadException, this);
13535         }
13536         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13537     },
13538
13539     // private
13540     fireKey : function(e){
13541         if(e.isNavKeyPress() && !this.list.isVisible()){
13542             this.fireEvent("specialkey", this, e);
13543         }
13544     },
13545
13546     // private
13547     onResize: function(w, h){
13548 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13549 //        
13550 //        if(typeof w != 'number'){
13551 //            // we do not handle it!?!?
13552 //            return;
13553 //        }
13554 //        var tw = this.trigger.getWidth();
13555 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13556 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13557 //        var x = w - tw;
13558 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13559 //            
13560 //        //this.trigger.setStyle('left', x+'px');
13561 //        
13562 //        if(this.list && this.listWidth === undefined){
13563 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13564 //            this.list.setWidth(lw);
13565 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13566 //        }
13567         
13568     
13569         
13570     },
13571
13572     /**
13573      * Allow or prevent the user from directly editing the field text.  If false is passed,
13574      * the user will only be able to select from the items defined in the dropdown list.  This method
13575      * is the runtime equivalent of setting the 'editable' config option at config time.
13576      * @param {Boolean} value True to allow the user to directly edit the field text
13577      */
13578     setEditable : function(value){
13579         if(value == this.editable){
13580             return;
13581         }
13582         this.editable = value;
13583         if(!value){
13584             this.inputEl().dom.setAttribute('readOnly', true);
13585             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13586             this.inputEl().addClass('x-combo-noedit');
13587         }else{
13588             this.inputEl().dom.setAttribute('readOnly', false);
13589             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13590             this.inputEl().removeClass('x-combo-noedit');
13591         }
13592     },
13593
13594     // private
13595     
13596     onBeforeLoad : function(combo,opts){
13597         if(!this.hasFocus){
13598             return;
13599         }
13600          if (!opts.add) {
13601             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13602          }
13603         this.restrictHeight();
13604         this.selectedIndex = -1;
13605     },
13606
13607     // private
13608     onLoad : function(){
13609         
13610         this.hasQuery = false;
13611         
13612         if(!this.hasFocus){
13613             return;
13614         }
13615         
13616         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13617             this.loading.hide();
13618         }
13619         
13620         if(this.store.getCount() > 0){
13621             
13622             this.expand();
13623             this.restrictHeight();
13624             if(this.lastQuery == this.allQuery){
13625                 if(this.editable && !this.tickable){
13626                     this.inputEl().dom.select();
13627                 }
13628                 
13629                 if(
13630                     !this.selectByValue(this.value, true) &&
13631                     this.autoFocus && 
13632                     (
13633                         !this.store.lastOptions ||
13634                         typeof(this.store.lastOptions.add) == 'undefined' || 
13635                         this.store.lastOptions.add != true
13636                     )
13637                 ){
13638                     this.select(0, true);
13639                 }
13640             }else{
13641                 if(this.autoFocus){
13642                     this.selectNext();
13643                 }
13644                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13645                     this.taTask.delay(this.typeAheadDelay);
13646                 }
13647             }
13648         }else{
13649             this.onEmptyResults();
13650         }
13651         
13652         //this.el.focus();
13653     },
13654     // private
13655     onLoadException : function()
13656     {
13657         this.hasQuery = false;
13658         
13659         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13660             this.loading.hide();
13661         }
13662         
13663         if(this.tickable && this.editable){
13664             return;
13665         }
13666         
13667         this.collapse();
13668         // only causes errors at present
13669         //Roo.log(this.store.reader.jsonData);
13670         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13671             // fixme
13672             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13673         //}
13674         
13675         
13676     },
13677     // private
13678     onTypeAhead : function(){
13679         if(this.store.getCount() > 0){
13680             var r = this.store.getAt(0);
13681             var newValue = r.data[this.displayField];
13682             var len = newValue.length;
13683             var selStart = this.getRawValue().length;
13684             
13685             if(selStart != len){
13686                 this.setRawValue(newValue);
13687                 this.selectText(selStart, newValue.length);
13688             }
13689         }
13690     },
13691
13692     // private
13693     onSelect : function(record, index){
13694         
13695         if(this.fireEvent('beforeselect', this, record, index) !== false){
13696         
13697             this.setFromData(index > -1 ? record.data : false);
13698             
13699             this.collapse();
13700             this.fireEvent('select', this, record, index);
13701         }
13702     },
13703
13704     /**
13705      * Returns the currently selected field value or empty string if no value is set.
13706      * @return {String} value The selected value
13707      */
13708     getValue : function()
13709     {
13710         if(Roo.isIOS && this.useNativeIOS){
13711             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13712         }
13713         
13714         if(this.multiple){
13715             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13716         }
13717         
13718         if(this.valueField){
13719             return typeof this.value != 'undefined' ? this.value : '';
13720         }else{
13721             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13722         }
13723     },
13724     
13725     getRawValue : function()
13726     {
13727         if(Roo.isIOS && this.useNativeIOS){
13728             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13729         }
13730         
13731         var v = this.inputEl().getValue();
13732         
13733         return v;
13734     },
13735
13736     /**
13737      * Clears any text/value currently set in the field
13738      */
13739     clearValue : function(){
13740         
13741         if(this.hiddenField){
13742             this.hiddenField.dom.value = '';
13743         }
13744         this.value = '';
13745         this.setRawValue('');
13746         this.lastSelectionText = '';
13747         this.lastData = false;
13748         
13749         var close = this.closeTriggerEl();
13750         
13751         if(close){
13752             close.hide();
13753         }
13754         
13755         this.validate();
13756         
13757     },
13758
13759     /**
13760      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13761      * will be displayed in the field.  If the value does not match the data value of an existing item,
13762      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13763      * Otherwise the field will be blank (although the value will still be set).
13764      * @param {String} value The value to match
13765      */
13766     setValue : function(v)
13767     {
13768         if(Roo.isIOS && this.useNativeIOS){
13769             this.setIOSValue(v);
13770             return;
13771         }
13772         
13773         if(this.multiple){
13774             this.syncValue();
13775             return;
13776         }
13777         
13778         var text = v;
13779         if(this.valueField){
13780             var r = this.findRecord(this.valueField, v);
13781             if(r){
13782                 text = r.data[this.displayField];
13783             }else if(this.valueNotFoundText !== undefined){
13784                 text = this.valueNotFoundText;
13785             }
13786         }
13787         this.lastSelectionText = text;
13788         if(this.hiddenField){
13789             this.hiddenField.dom.value = v;
13790         }
13791         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13792         this.value = v;
13793         
13794         var close = this.closeTriggerEl();
13795         
13796         if(close){
13797             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13798         }
13799         
13800         this.validate();
13801     },
13802     /**
13803      * @property {Object} the last set data for the element
13804      */
13805     
13806     lastData : false,
13807     /**
13808      * Sets the value of the field based on a object which is related to the record format for the store.
13809      * @param {Object} value the value to set as. or false on reset?
13810      */
13811     setFromData : function(o){
13812         
13813         if(this.multiple){
13814             this.addItem(o);
13815             return;
13816         }
13817             
13818         var dv = ''; // display value
13819         var vv = ''; // value value..
13820         this.lastData = o;
13821         if (this.displayField) {
13822             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13823         } else {
13824             // this is an error condition!!!
13825             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13826         }
13827         
13828         if(this.valueField){
13829             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13830         }
13831         
13832         var close = this.closeTriggerEl();
13833         
13834         if(close){
13835             if(dv.length || vv * 1 > 0){
13836                 close.show() ;
13837                 this.blockFocus=true;
13838             } else {
13839                 close.hide();
13840             }             
13841         }
13842         
13843         if(this.hiddenField){
13844             this.hiddenField.dom.value = vv;
13845             
13846             this.lastSelectionText = dv;
13847             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13848             this.value = vv;
13849             return;
13850         }
13851         // no hidden field.. - we store the value in 'value', but still display
13852         // display field!!!!
13853         this.lastSelectionText = dv;
13854         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13855         this.value = vv;
13856         
13857         
13858         
13859     },
13860     // private
13861     reset : function(){
13862         // overridden so that last data is reset..
13863         
13864         if(this.multiple){
13865             this.clearItem();
13866             return;
13867         }
13868         
13869         this.setValue(this.originalValue);
13870         //this.clearInvalid();
13871         this.lastData = false;
13872         if (this.view) {
13873             this.view.clearSelections();
13874         }
13875         
13876         this.validate();
13877     },
13878     // private
13879     findRecord : function(prop, value){
13880         var record;
13881         if(this.store.getCount() > 0){
13882             this.store.each(function(r){
13883                 if(r.data[prop] == value){
13884                     record = r;
13885                     return false;
13886                 }
13887                 return true;
13888             });
13889         }
13890         return record;
13891     },
13892     
13893     getName: function()
13894     {
13895         // returns hidden if it's set..
13896         if (!this.rendered) {return ''};
13897         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13898         
13899     },
13900     // private
13901     onViewMove : function(e, t){
13902         this.inKeyMode = false;
13903     },
13904
13905     // private
13906     onViewOver : function(e, t){
13907         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13908             return;
13909         }
13910         var item = this.view.findItemFromChild(t);
13911         
13912         if(item){
13913             var index = this.view.indexOf(item);
13914             this.select(index, false);
13915         }
13916     },
13917
13918     // private
13919     onViewClick : function(view, doFocus, el, e)
13920     {
13921         var index = this.view.getSelectedIndexes()[0];
13922         
13923         var r = this.store.getAt(index);
13924         
13925         if(this.tickable){
13926             
13927             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13928                 return;
13929             }
13930             
13931             var rm = false;
13932             var _this = this;
13933             
13934             Roo.each(this.tickItems, function(v,k){
13935                 
13936                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13937                     Roo.log(v);
13938                     _this.tickItems.splice(k, 1);
13939                     
13940                     if(typeof(e) == 'undefined' && view == false){
13941                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13942                     }
13943                     
13944                     rm = true;
13945                     return;
13946                 }
13947             });
13948             
13949             if(rm){
13950                 return;
13951             }
13952             
13953             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13954                 this.tickItems.push(r.data);
13955             }
13956             
13957             if(typeof(e) == 'undefined' && view == false){
13958                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13959             }
13960                     
13961             return;
13962         }
13963         
13964         if(r){
13965             this.onSelect(r, index);
13966         }
13967         if(doFocus !== false && !this.blockFocus){
13968             this.inputEl().focus();
13969         }
13970     },
13971
13972     // private
13973     restrictHeight : function(){
13974         //this.innerList.dom.style.height = '';
13975         //var inner = this.innerList.dom;
13976         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13977         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13978         //this.list.beginUpdate();
13979         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13980         this.list.alignTo(this.inputEl(), this.listAlign);
13981         this.list.alignTo(this.inputEl(), this.listAlign);
13982         //this.list.endUpdate();
13983     },
13984
13985     // private
13986     onEmptyResults : function(){
13987         
13988         if(this.tickable && this.editable){
13989             this.hasFocus = false;
13990             this.restrictHeight();
13991             return;
13992         }
13993         
13994         this.collapse();
13995     },
13996
13997     /**
13998      * Returns true if the dropdown list is expanded, else false.
13999      */
14000     isExpanded : function(){
14001         return this.list.isVisible();
14002     },
14003
14004     /**
14005      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14006      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14007      * @param {String} value The data value of the item to select
14008      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14009      * selected item if it is not currently in view (defaults to true)
14010      * @return {Boolean} True if the value matched an item in the list, else false
14011      */
14012     selectByValue : function(v, scrollIntoView){
14013         if(v !== undefined && v !== null){
14014             var r = this.findRecord(this.valueField || this.displayField, v);
14015             if(r){
14016                 this.select(this.store.indexOf(r), scrollIntoView);
14017                 return true;
14018             }
14019         }
14020         return false;
14021     },
14022
14023     /**
14024      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14025      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14026      * @param {Number} index The zero-based index of the list item to select
14027      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14028      * selected item if it is not currently in view (defaults to true)
14029      */
14030     select : function(index, scrollIntoView){
14031         this.selectedIndex = index;
14032         this.view.select(index);
14033         if(scrollIntoView !== false){
14034             var el = this.view.getNode(index);
14035             /*
14036              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14037              */
14038             if(el){
14039                 this.list.scrollChildIntoView(el, false);
14040             }
14041         }
14042     },
14043
14044     // private
14045     selectNext : function(){
14046         var ct = this.store.getCount();
14047         if(ct > 0){
14048             if(this.selectedIndex == -1){
14049                 this.select(0);
14050             }else if(this.selectedIndex < ct-1){
14051                 this.select(this.selectedIndex+1);
14052             }
14053         }
14054     },
14055
14056     // private
14057     selectPrev : function(){
14058         var ct = this.store.getCount();
14059         if(ct > 0){
14060             if(this.selectedIndex == -1){
14061                 this.select(0);
14062             }else if(this.selectedIndex != 0){
14063                 this.select(this.selectedIndex-1);
14064             }
14065         }
14066     },
14067
14068     // private
14069     onKeyUp : function(e){
14070         if(this.editable !== false && !e.isSpecialKey()){
14071             this.lastKey = e.getKey();
14072             this.dqTask.delay(this.queryDelay);
14073         }
14074     },
14075
14076     // private
14077     validateBlur : function(){
14078         return !this.list || !this.list.isVisible();   
14079     },
14080
14081     // private
14082     initQuery : function(){
14083         
14084         var v = this.getRawValue();
14085         
14086         if(this.tickable && this.editable){
14087             v = this.tickableInputEl().getValue();
14088         }
14089         
14090         this.doQuery(v);
14091     },
14092
14093     // private
14094     doForce : function(){
14095         if(this.inputEl().dom.value.length > 0){
14096             this.inputEl().dom.value =
14097                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14098              
14099         }
14100     },
14101
14102     /**
14103      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14104      * query allowing the query action to be canceled if needed.
14105      * @param {String} query The SQL query to execute
14106      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14107      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14108      * saved in the current store (defaults to false)
14109      */
14110     doQuery : function(q, forceAll){
14111         
14112         if(q === undefined || q === null){
14113             q = '';
14114         }
14115         var qe = {
14116             query: q,
14117             forceAll: forceAll,
14118             combo: this,
14119             cancel:false
14120         };
14121         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14122             return false;
14123         }
14124         q = qe.query;
14125         
14126         forceAll = qe.forceAll;
14127         if(forceAll === true || (q.length >= this.minChars)){
14128             
14129             this.hasQuery = true;
14130             
14131             if(this.lastQuery != q || this.alwaysQuery){
14132                 this.lastQuery = q;
14133                 if(this.mode == 'local'){
14134                     this.selectedIndex = -1;
14135                     if(forceAll){
14136                         this.store.clearFilter();
14137                     }else{
14138                         
14139                         if(this.specialFilter){
14140                             this.fireEvent('specialfilter', this);
14141                             this.onLoad();
14142                             return;
14143                         }
14144                         
14145                         this.store.filter(this.displayField, q);
14146                     }
14147                     
14148                     this.store.fireEvent("datachanged", this.store);
14149                     
14150                     this.onLoad();
14151                     
14152                     
14153                 }else{
14154                     
14155                     this.store.baseParams[this.queryParam] = q;
14156                     
14157                     var options = {params : this.getParams(q)};
14158                     
14159                     if(this.loadNext){
14160                         options.add = true;
14161                         options.params.start = this.page * this.pageSize;
14162                     }
14163                     
14164                     this.store.load(options);
14165                     
14166                     /*
14167                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14168                      *  we should expand the list on onLoad
14169                      *  so command out it
14170                      */
14171 //                    this.expand();
14172                 }
14173             }else{
14174                 this.selectedIndex = -1;
14175                 this.onLoad();   
14176             }
14177         }
14178         
14179         this.loadNext = false;
14180     },
14181     
14182     // private
14183     getParams : function(q){
14184         var p = {};
14185         //p[this.queryParam] = q;
14186         
14187         if(this.pageSize){
14188             p.start = 0;
14189             p.limit = this.pageSize;
14190         }
14191         return p;
14192     },
14193
14194     /**
14195      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14196      */
14197     collapse : function(){
14198         if(!this.isExpanded()){
14199             return;
14200         }
14201         
14202         this.list.hide();
14203         
14204         this.hasFocus = false;
14205         
14206         if(this.tickable){
14207             this.okBtn.hide();
14208             this.cancelBtn.hide();
14209             this.trigger.show();
14210             
14211             if(this.editable){
14212                 this.tickableInputEl().dom.value = '';
14213                 this.tickableInputEl().blur();
14214             }
14215             
14216         }
14217         
14218         Roo.get(document).un('mousedown', this.collapseIf, this);
14219         Roo.get(document).un('mousewheel', this.collapseIf, this);
14220         if (!this.editable) {
14221             Roo.get(document).un('keydown', this.listKeyPress, this);
14222         }
14223         this.fireEvent('collapse', this);
14224         
14225         this.validate();
14226     },
14227
14228     // private
14229     collapseIf : function(e){
14230         var in_combo  = e.within(this.el);
14231         var in_list =  e.within(this.list);
14232         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14233         
14234         if (in_combo || in_list || is_list) {
14235             //e.stopPropagation();
14236             return;
14237         }
14238         
14239         if(this.tickable){
14240             this.onTickableFooterButtonClick(e, false, false);
14241         }
14242
14243         this.collapse();
14244         
14245     },
14246
14247     /**
14248      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14249      */
14250     expand : function(){
14251        
14252         if(this.isExpanded() || !this.hasFocus){
14253             return;
14254         }
14255         
14256         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14257         this.list.setWidth(lw);
14258         
14259         Roo.log('expand');
14260         
14261         this.list.show();
14262         
14263         this.restrictHeight();
14264         
14265         if(this.tickable){
14266             
14267             this.tickItems = Roo.apply([], this.item);
14268             
14269             this.okBtn.show();
14270             this.cancelBtn.show();
14271             this.trigger.hide();
14272             
14273             if(this.editable){
14274                 this.tickableInputEl().focus();
14275             }
14276             
14277         }
14278         
14279         Roo.get(document).on('mousedown', this.collapseIf, this);
14280         Roo.get(document).on('mousewheel', this.collapseIf, this);
14281         if (!this.editable) {
14282             Roo.get(document).on('keydown', this.listKeyPress, this);
14283         }
14284         
14285         this.fireEvent('expand', this);
14286     },
14287
14288     // private
14289     // Implements the default empty TriggerField.onTriggerClick function
14290     onTriggerClick : function(e)
14291     {
14292         Roo.log('trigger click');
14293         
14294         if(this.disabled || !this.triggerList){
14295             return;
14296         }
14297         
14298         this.page = 0;
14299         this.loadNext = false;
14300         
14301         if(this.isExpanded()){
14302             this.collapse();
14303             if (!this.blockFocus) {
14304                 this.inputEl().focus();
14305             }
14306             
14307         }else {
14308             this.hasFocus = true;
14309             if(this.triggerAction == 'all') {
14310                 this.doQuery(this.allQuery, true);
14311             } else {
14312                 this.doQuery(this.getRawValue());
14313             }
14314             if (!this.blockFocus) {
14315                 this.inputEl().focus();
14316             }
14317         }
14318     },
14319     
14320     onTickableTriggerClick : function(e)
14321     {
14322         if(this.disabled){
14323             return;
14324         }
14325         
14326         this.page = 0;
14327         this.loadNext = false;
14328         this.hasFocus = true;
14329         
14330         if(this.triggerAction == 'all') {
14331             this.doQuery(this.allQuery, true);
14332         } else {
14333             this.doQuery(this.getRawValue());
14334         }
14335     },
14336     
14337     onSearchFieldClick : function(e)
14338     {
14339         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14340             this.onTickableFooterButtonClick(e, false, false);
14341             return;
14342         }
14343         
14344         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14345             return;
14346         }
14347         
14348         this.page = 0;
14349         this.loadNext = false;
14350         this.hasFocus = true;
14351         
14352         if(this.triggerAction == 'all') {
14353             this.doQuery(this.allQuery, true);
14354         } else {
14355             this.doQuery(this.getRawValue());
14356         }
14357     },
14358     
14359     listKeyPress : function(e)
14360     {
14361         //Roo.log('listkeypress');
14362         // scroll to first matching element based on key pres..
14363         if (e.isSpecialKey()) {
14364             return false;
14365         }
14366         var k = String.fromCharCode(e.getKey()).toUpperCase();
14367         //Roo.log(k);
14368         var match  = false;
14369         var csel = this.view.getSelectedNodes();
14370         var cselitem = false;
14371         if (csel.length) {
14372             var ix = this.view.indexOf(csel[0]);
14373             cselitem  = this.store.getAt(ix);
14374             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14375                 cselitem = false;
14376             }
14377             
14378         }
14379         
14380         this.store.each(function(v) { 
14381             if (cselitem) {
14382                 // start at existing selection.
14383                 if (cselitem.id == v.id) {
14384                     cselitem = false;
14385                 }
14386                 return true;
14387             }
14388                 
14389             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14390                 match = this.store.indexOf(v);
14391                 return false;
14392             }
14393             return true;
14394         }, this);
14395         
14396         if (match === false) {
14397             return true; // no more action?
14398         }
14399         // scroll to?
14400         this.view.select(match);
14401         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14402         sn.scrollIntoView(sn.dom.parentNode, false);
14403     },
14404     
14405     onViewScroll : function(e, t){
14406         
14407         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){
14408             return;
14409         }
14410         
14411         this.hasQuery = true;
14412         
14413         this.loading = this.list.select('.loading', true).first();
14414         
14415         if(this.loading === null){
14416             this.list.createChild({
14417                 tag: 'div',
14418                 cls: 'loading roo-select2-more-results roo-select2-active',
14419                 html: 'Loading more results...'
14420             });
14421             
14422             this.loading = this.list.select('.loading', true).first();
14423             
14424             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14425             
14426             this.loading.hide();
14427         }
14428         
14429         this.loading.show();
14430         
14431         var _combo = this;
14432         
14433         this.page++;
14434         this.loadNext = true;
14435         
14436         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14437         
14438         return;
14439     },
14440     
14441     addItem : function(o)
14442     {   
14443         var dv = ''; // display value
14444         
14445         if (this.displayField) {
14446             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14447         } else {
14448             // this is an error condition!!!
14449             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14450         }
14451         
14452         if(!dv.length){
14453             return;
14454         }
14455         
14456         var choice = this.choices.createChild({
14457             tag: 'li',
14458             cls: 'roo-select2-search-choice',
14459             cn: [
14460                 {
14461                     tag: 'div',
14462                     html: dv
14463                 },
14464                 {
14465                     tag: 'a',
14466                     href: '#',
14467                     cls: 'roo-select2-search-choice-close fa fa-times',
14468                     tabindex: '-1'
14469                 }
14470             ]
14471             
14472         }, this.searchField);
14473         
14474         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14475         
14476         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14477         
14478         this.item.push(o);
14479         
14480         this.lastData = o;
14481         
14482         this.syncValue();
14483         
14484         this.inputEl().dom.value = '';
14485         
14486         this.validate();
14487     },
14488     
14489     onRemoveItem : function(e, _self, o)
14490     {
14491         e.preventDefault();
14492         
14493         this.lastItem = Roo.apply([], this.item);
14494         
14495         var index = this.item.indexOf(o.data) * 1;
14496         
14497         if( index < 0){
14498             Roo.log('not this item?!');
14499             return;
14500         }
14501         
14502         this.item.splice(index, 1);
14503         o.item.remove();
14504         
14505         this.syncValue();
14506         
14507         this.fireEvent('remove', this, e);
14508         
14509         this.validate();
14510         
14511     },
14512     
14513     syncValue : function()
14514     {
14515         if(!this.item.length){
14516             this.clearValue();
14517             return;
14518         }
14519             
14520         var value = [];
14521         var _this = this;
14522         Roo.each(this.item, function(i){
14523             if(_this.valueField){
14524                 value.push(i[_this.valueField]);
14525                 return;
14526             }
14527
14528             value.push(i);
14529         });
14530
14531         this.value = value.join(',');
14532
14533         if(this.hiddenField){
14534             this.hiddenField.dom.value = this.value;
14535         }
14536         
14537         this.store.fireEvent("datachanged", this.store);
14538         
14539         this.validate();
14540     },
14541     
14542     clearItem : function()
14543     {
14544         if(!this.multiple){
14545             return;
14546         }
14547         
14548         this.item = [];
14549         
14550         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14551            c.remove();
14552         });
14553         
14554         this.syncValue();
14555         
14556         this.validate();
14557         
14558         if(this.tickable && !Roo.isTouch){
14559             this.view.refresh();
14560         }
14561     },
14562     
14563     inputEl: function ()
14564     {
14565         if(Roo.isIOS && this.useNativeIOS){
14566             return this.el.select('select.roo-ios-select', true).first();
14567         }
14568         
14569         if(Roo.isTouch && this.mobileTouchView){
14570             return this.el.select('input.form-control',true).first();
14571         }
14572         
14573         if(this.tickable){
14574             return this.searchField;
14575         }
14576         
14577         return this.el.select('input.form-control',true).first();
14578     },
14579     
14580     onTickableFooterButtonClick : function(e, btn, el)
14581     {
14582         e.preventDefault();
14583         
14584         this.lastItem = Roo.apply([], this.item);
14585         
14586         if(btn && btn.name == 'cancel'){
14587             this.tickItems = Roo.apply([], this.item);
14588             this.collapse();
14589             return;
14590         }
14591         
14592         this.clearItem();
14593         
14594         var _this = this;
14595         
14596         Roo.each(this.tickItems, function(o){
14597             _this.addItem(o);
14598         });
14599         
14600         this.collapse();
14601         
14602     },
14603     
14604     validate : function()
14605     {
14606         var v = this.getRawValue();
14607         
14608         if(this.multiple){
14609             v = this.getValue();
14610         }
14611         
14612         if(this.disabled || this.allowBlank || v.length){
14613             this.markValid();
14614             return true;
14615         }
14616         
14617         this.markInvalid();
14618         return false;
14619     },
14620     
14621     tickableInputEl : function()
14622     {
14623         if(!this.tickable || !this.editable){
14624             return this.inputEl();
14625         }
14626         
14627         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14628     },
14629     
14630     
14631     getAutoCreateTouchView : function()
14632     {
14633         var id = Roo.id();
14634         
14635         var cfg = {
14636             cls: 'form-group' //input-group
14637         };
14638         
14639         var input =  {
14640             tag: 'input',
14641             id : id,
14642             type : this.inputType,
14643             cls : 'form-control x-combo-noedit',
14644             autocomplete: 'new-password',
14645             placeholder : this.placeholder || '',
14646             readonly : true
14647         };
14648         
14649         if (this.name) {
14650             input.name = this.name;
14651         }
14652         
14653         if (this.size) {
14654             input.cls += ' input-' + this.size;
14655         }
14656         
14657         if (this.disabled) {
14658             input.disabled = true;
14659         }
14660         
14661         var inputblock = {
14662             cls : '',
14663             cn : [
14664                 input
14665             ]
14666         };
14667         
14668         if(this.before){
14669             inputblock.cls += ' input-group';
14670             
14671             inputblock.cn.unshift({
14672                 tag :'span',
14673                 cls : 'input-group-addon',
14674                 html : this.before
14675             });
14676         }
14677         
14678         if(this.removable && !this.multiple){
14679             inputblock.cls += ' roo-removable';
14680             
14681             inputblock.cn.push({
14682                 tag: 'button',
14683                 html : 'x',
14684                 cls : 'roo-combo-removable-btn close'
14685             });
14686         }
14687
14688         if(this.hasFeedback && !this.allowBlank){
14689             
14690             inputblock.cls += ' has-feedback';
14691             
14692             inputblock.cn.push({
14693                 tag: 'span',
14694                 cls: 'glyphicon form-control-feedback'
14695             });
14696             
14697         }
14698         
14699         if (this.after) {
14700             
14701             inputblock.cls += (this.before) ? '' : ' input-group';
14702             
14703             inputblock.cn.push({
14704                 tag :'span',
14705                 cls : 'input-group-addon',
14706                 html : this.after
14707             });
14708         }
14709
14710         var box = {
14711             tag: 'div',
14712             cn: [
14713                 {
14714                     tag: 'input',
14715                     type : 'hidden',
14716                     cls: 'form-hidden-field'
14717                 },
14718                 inputblock
14719             ]
14720             
14721         };
14722         
14723         if(this.multiple){
14724             box = {
14725                 tag: 'div',
14726                 cn: [
14727                     {
14728                         tag: 'input',
14729                         type : 'hidden',
14730                         cls: 'form-hidden-field'
14731                     },
14732                     {
14733                         tag: 'ul',
14734                         cls: 'roo-select2-choices',
14735                         cn:[
14736                             {
14737                                 tag: 'li',
14738                                 cls: 'roo-select2-search-field',
14739                                 cn: [
14740
14741                                     inputblock
14742                                 ]
14743                             }
14744                         ]
14745                     }
14746                 ]
14747             }
14748         };
14749         
14750         var combobox = {
14751             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14752             cn: [
14753                 box
14754             ]
14755         };
14756         
14757         if(!this.multiple && this.showToggleBtn){
14758             
14759             var caret = {
14760                         tag: 'span',
14761                         cls: 'caret'
14762             };
14763             
14764             if (this.caret != false) {
14765                 caret = {
14766                      tag: 'i',
14767                      cls: 'fa fa-' + this.caret
14768                 };
14769                 
14770             }
14771             
14772             combobox.cn.push({
14773                 tag :'span',
14774                 cls : 'input-group-addon btn dropdown-toggle',
14775                 cn : [
14776                     caret,
14777                     {
14778                         tag: 'span',
14779                         cls: 'combobox-clear',
14780                         cn  : [
14781                             {
14782                                 tag : 'i',
14783                                 cls: 'icon-remove'
14784                             }
14785                         ]
14786                     }
14787                 ]
14788
14789             })
14790         }
14791         
14792         if(this.multiple){
14793             combobox.cls += ' roo-select2-container-multi';
14794         }
14795         
14796         var align = this.labelAlign || this.parentLabelAlign();
14797         
14798         if (align ==='left' && this.fieldLabel.length) {
14799
14800             cfg.cn = [
14801                 {
14802                    tag : 'i',
14803                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14804                    tooltip : 'This field is required'
14805                 },
14806                 {
14807                     tag: 'label',
14808                     cls : 'control-label',
14809                     html : this.fieldLabel
14810
14811                 },
14812                 {
14813                     cls : '', 
14814                     cn: [
14815                         combobox
14816                     ]
14817                 }
14818             ];
14819             
14820             var labelCfg = cfg.cn[1];
14821             var contentCfg = cfg.cn[2];
14822             
14823
14824             if(this.indicatorpos == 'right'){
14825                 cfg.cn = [
14826                     {
14827                         tag: 'label',
14828                         'for' :  id,
14829                         cls : 'control-label',
14830                         cn : [
14831                             {
14832                                 tag : 'span',
14833                                 html : this.fieldLabel
14834                             },
14835                             {
14836                                 tag : 'i',
14837                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14838                                 tooltip : 'This field is required'
14839                             }
14840                         ]
14841                     },
14842                     {
14843                         cls : "",
14844                         cn: [
14845                             combobox
14846                         ]
14847                     }
14848
14849                 ];
14850                 
14851                 labelCfg = cfg.cn[0];
14852                 contentCfg = cfg.cn[1];
14853             }
14854             
14855            
14856             
14857             if(this.labelWidth > 12){
14858                 labelCfg.style = "width: " + this.labelWidth + 'px';
14859             }
14860             
14861             if(this.labelWidth < 13 && this.labelmd == 0){
14862                 this.labelmd = this.labelWidth;
14863             }
14864             
14865             if(this.labellg > 0){
14866                 labelCfg.cls += ' col-lg-' + this.labellg;
14867                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14868             }
14869             
14870             if(this.labelmd > 0){
14871                 labelCfg.cls += ' col-md-' + this.labelmd;
14872                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14873             }
14874             
14875             if(this.labelsm > 0){
14876                 labelCfg.cls += ' col-sm-' + this.labelsm;
14877                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14878             }
14879             
14880             if(this.labelxs > 0){
14881                 labelCfg.cls += ' col-xs-' + this.labelxs;
14882                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14883             }
14884                 
14885                 
14886         } else if ( this.fieldLabel.length) {
14887             cfg.cn = [
14888                 {
14889                    tag : 'i',
14890                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14891                    tooltip : 'This field is required'
14892                 },
14893                 {
14894                     tag: 'label',
14895                     cls : 'control-label',
14896                     html : this.fieldLabel
14897
14898                 },
14899                 {
14900                     cls : '', 
14901                     cn: [
14902                         combobox
14903                     ]
14904                 }
14905             ];
14906             
14907             if(this.indicatorpos == 'right'){
14908                 cfg.cn = [
14909                     {
14910                         tag: 'label',
14911                         cls : 'control-label',
14912                         html : this.fieldLabel,
14913                         cn : [
14914                             {
14915                                tag : 'i',
14916                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14917                                tooltip : 'This field is required'
14918                             }
14919                         ]
14920                     },
14921                     {
14922                         cls : '', 
14923                         cn: [
14924                             combobox
14925                         ]
14926                     }
14927                 ];
14928             }
14929         } else {
14930             cfg.cn = combobox;    
14931         }
14932         
14933         
14934         var settings = this;
14935         
14936         ['xs','sm','md','lg'].map(function(size){
14937             if (settings[size]) {
14938                 cfg.cls += ' col-' + size + '-' + settings[size];
14939             }
14940         });
14941         
14942         return cfg;
14943     },
14944     
14945     initTouchView : function()
14946     {
14947         this.renderTouchView();
14948         
14949         this.touchViewEl.on('scroll', function(){
14950             this.el.dom.scrollTop = 0;
14951         }, this);
14952         
14953         this.originalValue = this.getValue();
14954         
14955         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14956         
14957         this.inputEl().on("click", this.showTouchView, this);
14958         if (this.triggerEl) {
14959             this.triggerEl.on("click", this.showTouchView, this);
14960         }
14961         
14962         
14963         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14964         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14965         
14966         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14967         
14968         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14969         this.store.on('load', this.onTouchViewLoad, this);
14970         this.store.on('loadexception', this.onTouchViewLoadException, this);
14971         
14972         if(this.hiddenName){
14973             
14974             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14975             
14976             this.hiddenField.dom.value =
14977                 this.hiddenValue !== undefined ? this.hiddenValue :
14978                 this.value !== undefined ? this.value : '';
14979         
14980             this.el.dom.removeAttribute('name');
14981             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14982         }
14983         
14984         if(this.multiple){
14985             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14986             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14987         }
14988         
14989         if(this.removable && !this.multiple){
14990             var close = this.closeTriggerEl();
14991             if(close){
14992                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14993                 close.on('click', this.removeBtnClick, this, close);
14994             }
14995         }
14996         /*
14997          * fix the bug in Safari iOS8
14998          */
14999         this.inputEl().on("focus", function(e){
15000             document.activeElement.blur();
15001         }, this);
15002         
15003         return;
15004         
15005         
15006     },
15007     
15008     renderTouchView : function()
15009     {
15010         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15011         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15012         
15013         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15014         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15015         
15016         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15017         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15018         this.touchViewBodyEl.setStyle('overflow', 'auto');
15019         
15020         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15021         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15022         
15023         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15024         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15025         
15026     },
15027     
15028     showTouchView : function()
15029     {
15030         if(this.disabled){
15031             return;
15032         }
15033         
15034         this.touchViewHeaderEl.hide();
15035
15036         if(this.modalTitle.length){
15037             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15038             this.touchViewHeaderEl.show();
15039         }
15040
15041         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15042         this.touchViewEl.show();
15043
15044         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15045         
15046         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15047         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15048
15049         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15050
15051         if(this.modalTitle.length){
15052             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15053         }
15054         
15055         this.touchViewBodyEl.setHeight(bodyHeight);
15056
15057         if(this.animate){
15058             var _this = this;
15059             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15060         }else{
15061             this.touchViewEl.addClass('in');
15062         }
15063
15064         this.doTouchViewQuery();
15065         
15066     },
15067     
15068     hideTouchView : function()
15069     {
15070         this.touchViewEl.removeClass('in');
15071
15072         if(this.animate){
15073             var _this = this;
15074             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15075         }else{
15076             this.touchViewEl.setStyle('display', 'none');
15077         }
15078         
15079     },
15080     
15081     setTouchViewValue : function()
15082     {
15083         if(this.multiple){
15084             this.clearItem();
15085         
15086             var _this = this;
15087
15088             Roo.each(this.tickItems, function(o){
15089                 this.addItem(o);
15090             }, this);
15091         }
15092         
15093         this.hideTouchView();
15094     },
15095     
15096     doTouchViewQuery : function()
15097     {
15098         var qe = {
15099             query: '',
15100             forceAll: true,
15101             combo: this,
15102             cancel:false
15103         };
15104         
15105         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15106             return false;
15107         }
15108         
15109         if(!this.alwaysQuery || this.mode == 'local'){
15110             this.onTouchViewLoad();
15111             return;
15112         }
15113         
15114         this.store.load();
15115     },
15116     
15117     onTouchViewBeforeLoad : function(combo,opts)
15118     {
15119         return;
15120     },
15121
15122     // private
15123     onTouchViewLoad : function()
15124     {
15125         if(this.store.getCount() < 1){
15126             this.onTouchViewEmptyResults();
15127             return;
15128         }
15129         
15130         this.clearTouchView();
15131         
15132         var rawValue = this.getRawValue();
15133         
15134         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15135         
15136         this.tickItems = [];
15137         
15138         this.store.data.each(function(d, rowIndex){
15139             var row = this.touchViewListGroup.createChild(template);
15140             
15141             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15142                 row.addClass(d.data.cls);
15143             }
15144             
15145             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15146                 var cfg = {
15147                     data : d.data,
15148                     html : d.data[this.displayField]
15149                 };
15150                 
15151                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15152                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15153                 }
15154             }
15155             row.removeClass('selected');
15156             if(!this.multiple && this.valueField &&
15157                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15158             {
15159                 // radio buttons..
15160                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15161                 row.addClass('selected');
15162             }
15163             
15164             if(this.multiple && this.valueField &&
15165                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15166             {
15167                 
15168                 // checkboxes...
15169                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15170                 this.tickItems.push(d.data);
15171             }
15172             
15173             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15174             
15175         }, this);
15176         
15177         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15178         
15179         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15180
15181         if(this.modalTitle.length){
15182             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15183         }
15184
15185         var listHeight = this.touchViewListGroup.getHeight();
15186         
15187         var _this = this;
15188         
15189         if(firstChecked && listHeight > bodyHeight){
15190             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15191         }
15192         
15193     },
15194     
15195     onTouchViewLoadException : function()
15196     {
15197         this.hideTouchView();
15198     },
15199     
15200     onTouchViewEmptyResults : function()
15201     {
15202         this.clearTouchView();
15203         
15204         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15205         
15206         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15207         
15208     },
15209     
15210     clearTouchView : function()
15211     {
15212         this.touchViewListGroup.dom.innerHTML = '';
15213     },
15214     
15215     onTouchViewClick : function(e, el, o)
15216     {
15217         e.preventDefault();
15218         
15219         var row = o.row;
15220         var rowIndex = o.rowIndex;
15221         
15222         var r = this.store.getAt(rowIndex);
15223         
15224         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15225             
15226             if(!this.multiple){
15227                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15228                     c.dom.removeAttribute('checked');
15229                 }, this);
15230
15231                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15232
15233                 this.setFromData(r.data);
15234
15235                 var close = this.closeTriggerEl();
15236
15237                 if(close){
15238                     close.show();
15239                 }
15240
15241                 this.hideTouchView();
15242
15243                 this.fireEvent('select', this, r, rowIndex);
15244
15245                 return;
15246             }
15247
15248             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15249                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15250                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15251                 return;
15252             }
15253
15254             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15255             this.addItem(r.data);
15256             this.tickItems.push(r.data);
15257         }
15258     },
15259     
15260     getAutoCreateNativeIOS : function()
15261     {
15262         var cfg = {
15263             cls: 'form-group' //input-group,
15264         };
15265         
15266         var combobox =  {
15267             tag: 'select',
15268             cls : 'roo-ios-select'
15269         };
15270         
15271         if (this.name) {
15272             combobox.name = this.name;
15273         }
15274         
15275         if (this.disabled) {
15276             combobox.disabled = true;
15277         }
15278         
15279         var settings = this;
15280         
15281         ['xs','sm','md','lg'].map(function(size){
15282             if (settings[size]) {
15283                 cfg.cls += ' col-' + size + '-' + settings[size];
15284             }
15285         });
15286         
15287         cfg.cn = combobox;
15288         
15289         return cfg;
15290         
15291     },
15292     
15293     initIOSView : function()
15294     {
15295         this.store.on('load', this.onIOSViewLoad, this);
15296         
15297         return;
15298     },
15299     
15300     onIOSViewLoad : function()
15301     {
15302         if(this.store.getCount() < 1){
15303             return;
15304         }
15305         
15306         this.clearIOSView();
15307         
15308         if(this.allowBlank) {
15309             
15310             var default_text = '-- SELECT --';
15311             
15312             if(this.placeholder.length){
15313                 default_text = this.placeholder;
15314             }
15315             
15316             if(this.emptyTitle.length){
15317                 default_text += ' - ' + this.emptyTitle + ' -';
15318             }
15319             
15320             var opt = this.inputEl().createChild({
15321                 tag: 'option',
15322                 value : 0,
15323                 html : default_text
15324             });
15325             
15326             var o = {};
15327             o[this.valueField] = 0;
15328             o[this.displayField] = default_text;
15329             
15330             this.ios_options.push({
15331                 data : o,
15332                 el : opt
15333             });
15334             
15335         }
15336         
15337         this.store.data.each(function(d, rowIndex){
15338             
15339             var html = '';
15340             
15341             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15342                 html = d.data[this.displayField];
15343             }
15344             
15345             var value = '';
15346             
15347             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15348                 value = d.data[this.valueField];
15349             }
15350             
15351             var option = {
15352                 tag: 'option',
15353                 value : value,
15354                 html : html
15355             };
15356             
15357             if(this.value == d.data[this.valueField]){
15358                 option['selected'] = true;
15359             }
15360             
15361             var opt = this.inputEl().createChild(option);
15362             
15363             this.ios_options.push({
15364                 data : d.data,
15365                 el : opt
15366             });
15367             
15368         }, this);
15369         
15370         this.inputEl().on('change', function(){
15371            this.fireEvent('select', this);
15372         }, this);
15373         
15374     },
15375     
15376     clearIOSView: function()
15377     {
15378         this.inputEl().dom.innerHTML = '';
15379         
15380         this.ios_options = [];
15381     },
15382     
15383     setIOSValue: function(v)
15384     {
15385         this.value = v;
15386         
15387         if(!this.ios_options){
15388             return;
15389         }
15390         
15391         Roo.each(this.ios_options, function(opts){
15392            
15393            opts.el.dom.removeAttribute('selected');
15394            
15395            if(opts.data[this.valueField] != v){
15396                return;
15397            }
15398            
15399            opts.el.dom.setAttribute('selected', true);
15400            
15401         }, this);
15402     }
15403
15404     /** 
15405     * @cfg {Boolean} grow 
15406     * @hide 
15407     */
15408     /** 
15409     * @cfg {Number} growMin 
15410     * @hide 
15411     */
15412     /** 
15413     * @cfg {Number} growMax 
15414     * @hide 
15415     */
15416     /**
15417      * @hide
15418      * @method autoSize
15419      */
15420 });
15421
15422 Roo.apply(Roo.bootstrap.ComboBox,  {
15423     
15424     header : {
15425         tag: 'div',
15426         cls: 'modal-header',
15427         cn: [
15428             {
15429                 tag: 'h4',
15430                 cls: 'modal-title'
15431             }
15432         ]
15433     },
15434     
15435     body : {
15436         tag: 'div',
15437         cls: 'modal-body',
15438         cn: [
15439             {
15440                 tag: 'ul',
15441                 cls: 'list-group'
15442             }
15443         ]
15444     },
15445     
15446     listItemRadio : {
15447         tag: 'li',
15448         cls: 'list-group-item',
15449         cn: [
15450             {
15451                 tag: 'span',
15452                 cls: 'roo-combobox-list-group-item-value'
15453             },
15454             {
15455                 tag: 'div',
15456                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15457                 cn: [
15458                     {
15459                         tag: 'input',
15460                         type: 'radio'
15461                     },
15462                     {
15463                         tag: 'label'
15464                     }
15465                 ]
15466             }
15467         ]
15468     },
15469     
15470     listItemCheckbox : {
15471         tag: 'li',
15472         cls: 'list-group-item',
15473         cn: [
15474             {
15475                 tag: 'span',
15476                 cls: 'roo-combobox-list-group-item-value'
15477             },
15478             {
15479                 tag: 'div',
15480                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15481                 cn: [
15482                     {
15483                         tag: 'input',
15484                         type: 'checkbox'
15485                     },
15486                     {
15487                         tag: 'label'
15488                     }
15489                 ]
15490             }
15491         ]
15492     },
15493     
15494     emptyResult : {
15495         tag: 'div',
15496         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15497     },
15498     
15499     footer : {
15500         tag: 'div',
15501         cls: 'modal-footer',
15502         cn: [
15503             {
15504                 tag: 'div',
15505                 cls: 'row',
15506                 cn: [
15507                     {
15508                         tag: 'div',
15509                         cls: 'col-xs-6 text-left',
15510                         cn: {
15511                             tag: 'button',
15512                             cls: 'btn btn-danger roo-touch-view-cancel',
15513                             html: 'Cancel'
15514                         }
15515                     },
15516                     {
15517                         tag: 'div',
15518                         cls: 'col-xs-6 text-right',
15519                         cn: {
15520                             tag: 'button',
15521                             cls: 'btn btn-success roo-touch-view-ok',
15522                             html: 'OK'
15523                         }
15524                     }
15525                 ]
15526             }
15527         ]
15528         
15529     }
15530 });
15531
15532 Roo.apply(Roo.bootstrap.ComboBox,  {
15533     
15534     touchViewTemplate : {
15535         tag: 'div',
15536         cls: 'modal fade roo-combobox-touch-view',
15537         cn: [
15538             {
15539                 tag: 'div',
15540                 cls: 'modal-dialog',
15541                 style : 'position:fixed', // we have to fix position....
15542                 cn: [
15543                     {
15544                         tag: 'div',
15545                         cls: 'modal-content',
15546                         cn: [
15547                             Roo.bootstrap.ComboBox.header,
15548                             Roo.bootstrap.ComboBox.body,
15549                             Roo.bootstrap.ComboBox.footer
15550                         ]
15551                     }
15552                 ]
15553             }
15554         ]
15555     }
15556 });/*
15557  * Based on:
15558  * Ext JS Library 1.1.1
15559  * Copyright(c) 2006-2007, Ext JS, LLC.
15560  *
15561  * Originally Released Under LGPL - original licence link has changed is not relivant.
15562  *
15563  * Fork - LGPL
15564  * <script type="text/javascript">
15565  */
15566
15567 /**
15568  * @class Roo.View
15569  * @extends Roo.util.Observable
15570  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15571  * This class also supports single and multi selection modes. <br>
15572  * Create a data model bound view:
15573  <pre><code>
15574  var store = new Roo.data.Store(...);
15575
15576  var view = new Roo.View({
15577     el : "my-element",
15578     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15579  
15580     singleSelect: true,
15581     selectedClass: "ydataview-selected",
15582     store: store
15583  });
15584
15585  // listen for node click?
15586  view.on("click", function(vw, index, node, e){
15587  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15588  });
15589
15590  // load XML data
15591  dataModel.load("foobar.xml");
15592  </code></pre>
15593  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15594  * <br><br>
15595  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15596  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15597  * 
15598  * Note: old style constructor is still suported (container, template, config)
15599  * 
15600  * @constructor
15601  * Create a new View
15602  * @param {Object} config The config object
15603  * 
15604  */
15605 Roo.View = function(config, depreciated_tpl, depreciated_config){
15606     
15607     this.parent = false;
15608     
15609     if (typeof(depreciated_tpl) == 'undefined') {
15610         // new way.. - universal constructor.
15611         Roo.apply(this, config);
15612         this.el  = Roo.get(this.el);
15613     } else {
15614         // old format..
15615         this.el  = Roo.get(config);
15616         this.tpl = depreciated_tpl;
15617         Roo.apply(this, depreciated_config);
15618     }
15619     this.wrapEl  = this.el.wrap().wrap();
15620     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15621     
15622     
15623     if(typeof(this.tpl) == "string"){
15624         this.tpl = new Roo.Template(this.tpl);
15625     } else {
15626         // support xtype ctors..
15627         this.tpl = new Roo.factory(this.tpl, Roo);
15628     }
15629     
15630     
15631     this.tpl.compile();
15632     
15633     /** @private */
15634     this.addEvents({
15635         /**
15636          * @event beforeclick
15637          * Fires before a click is processed. Returns false to cancel the default action.
15638          * @param {Roo.View} this
15639          * @param {Number} index The index of the target node
15640          * @param {HTMLElement} node The target node
15641          * @param {Roo.EventObject} e The raw event object
15642          */
15643             "beforeclick" : true,
15644         /**
15645          * @event click
15646          * Fires when a template node is clicked.
15647          * @param {Roo.View} this
15648          * @param {Number} index The index of the target node
15649          * @param {HTMLElement} node The target node
15650          * @param {Roo.EventObject} e The raw event object
15651          */
15652             "click" : true,
15653         /**
15654          * @event dblclick
15655          * Fires when a template node is double clicked.
15656          * @param {Roo.View} this
15657          * @param {Number} index The index of the target node
15658          * @param {HTMLElement} node The target node
15659          * @param {Roo.EventObject} e The raw event object
15660          */
15661             "dblclick" : true,
15662         /**
15663          * @event contextmenu
15664          * Fires when a template node is right clicked.
15665          * @param {Roo.View} this
15666          * @param {Number} index The index of the target node
15667          * @param {HTMLElement} node The target node
15668          * @param {Roo.EventObject} e The raw event object
15669          */
15670             "contextmenu" : true,
15671         /**
15672          * @event selectionchange
15673          * Fires when the selected nodes change.
15674          * @param {Roo.View} this
15675          * @param {Array} selections Array of the selected nodes
15676          */
15677             "selectionchange" : true,
15678     
15679         /**
15680          * @event beforeselect
15681          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15682          * @param {Roo.View} this
15683          * @param {HTMLElement} node The node to be selected
15684          * @param {Array} selections Array of currently selected nodes
15685          */
15686             "beforeselect" : true,
15687         /**
15688          * @event preparedata
15689          * Fires on every row to render, to allow you to change the data.
15690          * @param {Roo.View} this
15691          * @param {Object} data to be rendered (change this)
15692          */
15693           "preparedata" : true
15694           
15695           
15696         });
15697
15698
15699
15700     this.el.on({
15701         "click": this.onClick,
15702         "dblclick": this.onDblClick,
15703         "contextmenu": this.onContextMenu,
15704         scope:this
15705     });
15706
15707     this.selections = [];
15708     this.nodes = [];
15709     this.cmp = new Roo.CompositeElementLite([]);
15710     if(this.store){
15711         this.store = Roo.factory(this.store, Roo.data);
15712         this.setStore(this.store, true);
15713     }
15714     
15715     if ( this.footer && this.footer.xtype) {
15716            
15717          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15718         
15719         this.footer.dataSource = this.store;
15720         this.footer.container = fctr;
15721         this.footer = Roo.factory(this.footer, Roo);
15722         fctr.insertFirst(this.el);
15723         
15724         // this is a bit insane - as the paging toolbar seems to detach the el..
15725 //        dom.parentNode.parentNode.parentNode
15726          // they get detached?
15727     }
15728     
15729     
15730     Roo.View.superclass.constructor.call(this);
15731     
15732     
15733 };
15734
15735 Roo.extend(Roo.View, Roo.util.Observable, {
15736     
15737      /**
15738      * @cfg {Roo.data.Store} store Data store to load data from.
15739      */
15740     store : false,
15741     
15742     /**
15743      * @cfg {String|Roo.Element} el The container element.
15744      */
15745     el : '',
15746     
15747     /**
15748      * @cfg {String|Roo.Template} tpl The template used by this View 
15749      */
15750     tpl : false,
15751     /**
15752      * @cfg {String} dataName the named area of the template to use as the data area
15753      *                          Works with domtemplates roo-name="name"
15754      */
15755     dataName: false,
15756     /**
15757      * @cfg {String} selectedClass The css class to add to selected nodes
15758      */
15759     selectedClass : "x-view-selected",
15760      /**
15761      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15762      */
15763     emptyText : "",
15764     
15765     /**
15766      * @cfg {String} text to display on mask (default Loading)
15767      */
15768     mask : false,
15769     /**
15770      * @cfg {Boolean} multiSelect Allow multiple selection
15771      */
15772     multiSelect : false,
15773     /**
15774      * @cfg {Boolean} singleSelect Allow single selection
15775      */
15776     singleSelect:  false,
15777     
15778     /**
15779      * @cfg {Boolean} toggleSelect - selecting 
15780      */
15781     toggleSelect : false,
15782     
15783     /**
15784      * @cfg {Boolean} tickable - selecting 
15785      */
15786     tickable : false,
15787     
15788     /**
15789      * Returns the element this view is bound to.
15790      * @return {Roo.Element}
15791      */
15792     getEl : function(){
15793         return this.wrapEl;
15794     },
15795     
15796     
15797
15798     /**
15799      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15800      */
15801     refresh : function(){
15802         //Roo.log('refresh');
15803         var t = this.tpl;
15804         
15805         // if we are using something like 'domtemplate', then
15806         // the what gets used is:
15807         // t.applySubtemplate(NAME, data, wrapping data..)
15808         // the outer template then get' applied with
15809         //     the store 'extra data'
15810         // and the body get's added to the
15811         //      roo-name="data" node?
15812         //      <span class='roo-tpl-{name}'></span> ?????
15813         
15814         
15815         
15816         this.clearSelections();
15817         this.el.update("");
15818         var html = [];
15819         var records = this.store.getRange();
15820         if(records.length < 1) {
15821             
15822             // is this valid??  = should it render a template??
15823             
15824             this.el.update(this.emptyText);
15825             return;
15826         }
15827         var el = this.el;
15828         if (this.dataName) {
15829             this.el.update(t.apply(this.store.meta)); //????
15830             el = this.el.child('.roo-tpl-' + this.dataName);
15831         }
15832         
15833         for(var i = 0, len = records.length; i < len; i++){
15834             var data = this.prepareData(records[i].data, i, records[i]);
15835             this.fireEvent("preparedata", this, data, i, records[i]);
15836             
15837             var d = Roo.apply({}, data);
15838             
15839             if(this.tickable){
15840                 Roo.apply(d, {'roo-id' : Roo.id()});
15841                 
15842                 var _this = this;
15843             
15844                 Roo.each(this.parent.item, function(item){
15845                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15846                         return;
15847                     }
15848                     Roo.apply(d, {'roo-data-checked' : 'checked'});
15849                 });
15850             }
15851             
15852             html[html.length] = Roo.util.Format.trim(
15853                 this.dataName ?
15854                     t.applySubtemplate(this.dataName, d, this.store.meta) :
15855                     t.apply(d)
15856             );
15857         }
15858         
15859         
15860         
15861         el.update(html.join(""));
15862         this.nodes = el.dom.childNodes;
15863         this.updateIndexes(0);
15864     },
15865     
15866
15867     /**
15868      * Function to override to reformat the data that is sent to
15869      * the template for each node.
15870      * DEPRICATED - use the preparedata event handler.
15871      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15872      * a JSON object for an UpdateManager bound view).
15873      */
15874     prepareData : function(data, index, record)
15875     {
15876         this.fireEvent("preparedata", this, data, index, record);
15877         return data;
15878     },
15879
15880     onUpdate : function(ds, record){
15881         // Roo.log('on update');   
15882         this.clearSelections();
15883         var index = this.store.indexOf(record);
15884         var n = this.nodes[index];
15885         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15886         n.parentNode.removeChild(n);
15887         this.updateIndexes(index, index);
15888     },
15889
15890     
15891     
15892 // --------- FIXME     
15893     onAdd : function(ds, records, index)
15894     {
15895         //Roo.log(['on Add', ds, records, index] );        
15896         this.clearSelections();
15897         if(this.nodes.length == 0){
15898             this.refresh();
15899             return;
15900         }
15901         var n = this.nodes[index];
15902         for(var i = 0, len = records.length; i < len; i++){
15903             var d = this.prepareData(records[i].data, i, records[i]);
15904             if(n){
15905                 this.tpl.insertBefore(n, d);
15906             }else{
15907                 
15908                 this.tpl.append(this.el, d);
15909             }
15910         }
15911         this.updateIndexes(index);
15912     },
15913
15914     onRemove : function(ds, record, index){
15915        // Roo.log('onRemove');
15916         this.clearSelections();
15917         var el = this.dataName  ?
15918             this.el.child('.roo-tpl-' + this.dataName) :
15919             this.el; 
15920         
15921         el.dom.removeChild(this.nodes[index]);
15922         this.updateIndexes(index);
15923     },
15924
15925     /**
15926      * Refresh an individual node.
15927      * @param {Number} index
15928      */
15929     refreshNode : function(index){
15930         this.onUpdate(this.store, this.store.getAt(index));
15931     },
15932
15933     updateIndexes : function(startIndex, endIndex){
15934         var ns = this.nodes;
15935         startIndex = startIndex || 0;
15936         endIndex = endIndex || ns.length - 1;
15937         for(var i = startIndex; i <= endIndex; i++){
15938             ns[i].nodeIndex = i;
15939         }
15940     },
15941
15942     /**
15943      * Changes the data store this view uses and refresh the view.
15944      * @param {Store} store
15945      */
15946     setStore : function(store, initial){
15947         if(!initial && this.store){
15948             this.store.un("datachanged", this.refresh);
15949             this.store.un("add", this.onAdd);
15950             this.store.un("remove", this.onRemove);
15951             this.store.un("update", this.onUpdate);
15952             this.store.un("clear", this.refresh);
15953             this.store.un("beforeload", this.onBeforeLoad);
15954             this.store.un("load", this.onLoad);
15955             this.store.un("loadexception", this.onLoad);
15956         }
15957         if(store){
15958           
15959             store.on("datachanged", this.refresh, this);
15960             store.on("add", this.onAdd, this);
15961             store.on("remove", this.onRemove, this);
15962             store.on("update", this.onUpdate, this);
15963             store.on("clear", this.refresh, this);
15964             store.on("beforeload", this.onBeforeLoad, this);
15965             store.on("load", this.onLoad, this);
15966             store.on("loadexception", this.onLoad, this);
15967         }
15968         
15969         if(store){
15970             this.refresh();
15971         }
15972     },
15973     /**
15974      * onbeforeLoad - masks the loading area.
15975      *
15976      */
15977     onBeforeLoad : function(store,opts)
15978     {
15979          //Roo.log('onBeforeLoad');   
15980         if (!opts.add) {
15981             this.el.update("");
15982         }
15983         this.el.mask(this.mask ? this.mask : "Loading" ); 
15984     },
15985     onLoad : function ()
15986     {
15987         this.el.unmask();
15988     },
15989     
15990
15991     /**
15992      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15993      * @param {HTMLElement} node
15994      * @return {HTMLElement} The template node
15995      */
15996     findItemFromChild : function(node){
15997         var el = this.dataName  ?
15998             this.el.child('.roo-tpl-' + this.dataName,true) :
15999             this.el.dom; 
16000         
16001         if(!node || node.parentNode == el){
16002                     return node;
16003             }
16004             var p = node.parentNode;
16005             while(p && p != el){
16006             if(p.parentNode == el){
16007                 return p;
16008             }
16009             p = p.parentNode;
16010         }
16011             return null;
16012     },
16013
16014     /** @ignore */
16015     onClick : function(e){
16016         var item = this.findItemFromChild(e.getTarget());
16017         if(item){
16018             var index = this.indexOf(item);
16019             if(this.onItemClick(item, index, e) !== false){
16020                 this.fireEvent("click", this, index, item, e);
16021             }
16022         }else{
16023             this.clearSelections();
16024         }
16025     },
16026
16027     /** @ignore */
16028     onContextMenu : function(e){
16029         var item = this.findItemFromChild(e.getTarget());
16030         if(item){
16031             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16032         }
16033     },
16034
16035     /** @ignore */
16036     onDblClick : function(e){
16037         var item = this.findItemFromChild(e.getTarget());
16038         if(item){
16039             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16040         }
16041     },
16042
16043     onItemClick : function(item, index, e)
16044     {
16045         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16046             return false;
16047         }
16048         if (this.toggleSelect) {
16049             var m = this.isSelected(item) ? 'unselect' : 'select';
16050             //Roo.log(m);
16051             var _t = this;
16052             _t[m](item, true, false);
16053             return true;
16054         }
16055         if(this.multiSelect || this.singleSelect){
16056             if(this.multiSelect && e.shiftKey && this.lastSelection){
16057                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16058             }else{
16059                 this.select(item, this.multiSelect && e.ctrlKey);
16060                 this.lastSelection = item;
16061             }
16062             
16063             if(!this.tickable){
16064                 e.preventDefault();
16065             }
16066             
16067         }
16068         return true;
16069     },
16070
16071     /**
16072      * Get the number of selected nodes.
16073      * @return {Number}
16074      */
16075     getSelectionCount : function(){
16076         return this.selections.length;
16077     },
16078
16079     /**
16080      * Get the currently selected nodes.
16081      * @return {Array} An array of HTMLElements
16082      */
16083     getSelectedNodes : function(){
16084         return this.selections;
16085     },
16086
16087     /**
16088      * Get the indexes of the selected nodes.
16089      * @return {Array}
16090      */
16091     getSelectedIndexes : function(){
16092         var indexes = [], s = this.selections;
16093         for(var i = 0, len = s.length; i < len; i++){
16094             indexes.push(s[i].nodeIndex);
16095         }
16096         return indexes;
16097     },
16098
16099     /**
16100      * Clear all selections
16101      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16102      */
16103     clearSelections : function(suppressEvent){
16104         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16105             this.cmp.elements = this.selections;
16106             this.cmp.removeClass(this.selectedClass);
16107             this.selections = [];
16108             if(!suppressEvent){
16109                 this.fireEvent("selectionchange", this, this.selections);
16110             }
16111         }
16112     },
16113
16114     /**
16115      * Returns true if the passed node is selected
16116      * @param {HTMLElement/Number} node The node or node index
16117      * @return {Boolean}
16118      */
16119     isSelected : function(node){
16120         var s = this.selections;
16121         if(s.length < 1){
16122             return false;
16123         }
16124         node = this.getNode(node);
16125         return s.indexOf(node) !== -1;
16126     },
16127
16128     /**
16129      * Selects nodes.
16130      * @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
16131      * @param {Boolean} keepExisting (optional) true to keep existing selections
16132      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16133      */
16134     select : function(nodeInfo, keepExisting, suppressEvent){
16135         if(nodeInfo instanceof Array){
16136             if(!keepExisting){
16137                 this.clearSelections(true);
16138             }
16139             for(var i = 0, len = nodeInfo.length; i < len; i++){
16140                 this.select(nodeInfo[i], true, true);
16141             }
16142             return;
16143         } 
16144         var node = this.getNode(nodeInfo);
16145         if(!node || this.isSelected(node)){
16146             return; // already selected.
16147         }
16148         if(!keepExisting){
16149             this.clearSelections(true);
16150         }
16151         
16152         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16153             Roo.fly(node).addClass(this.selectedClass);
16154             this.selections.push(node);
16155             if(!suppressEvent){
16156                 this.fireEvent("selectionchange", this, this.selections);
16157             }
16158         }
16159         
16160         
16161     },
16162       /**
16163      * Unselects nodes.
16164      * @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
16165      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16166      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16167      */
16168     unselect : function(nodeInfo, keepExisting, suppressEvent)
16169     {
16170         if(nodeInfo instanceof Array){
16171             Roo.each(this.selections, function(s) {
16172                 this.unselect(s, nodeInfo);
16173             }, this);
16174             return;
16175         }
16176         var node = this.getNode(nodeInfo);
16177         if(!node || !this.isSelected(node)){
16178             //Roo.log("not selected");
16179             return; // not selected.
16180         }
16181         // fireevent???
16182         var ns = [];
16183         Roo.each(this.selections, function(s) {
16184             if (s == node ) {
16185                 Roo.fly(node).removeClass(this.selectedClass);
16186
16187                 return;
16188             }
16189             ns.push(s);
16190         },this);
16191         
16192         this.selections= ns;
16193         this.fireEvent("selectionchange", this, this.selections);
16194     },
16195
16196     /**
16197      * Gets a template node.
16198      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16199      * @return {HTMLElement} The node or null if it wasn't found
16200      */
16201     getNode : function(nodeInfo){
16202         if(typeof nodeInfo == "string"){
16203             return document.getElementById(nodeInfo);
16204         }else if(typeof nodeInfo == "number"){
16205             return this.nodes[nodeInfo];
16206         }
16207         return nodeInfo;
16208     },
16209
16210     /**
16211      * Gets a range template nodes.
16212      * @param {Number} startIndex
16213      * @param {Number} endIndex
16214      * @return {Array} An array of nodes
16215      */
16216     getNodes : function(start, end){
16217         var ns = this.nodes;
16218         start = start || 0;
16219         end = typeof end == "undefined" ? ns.length - 1 : end;
16220         var nodes = [];
16221         if(start <= end){
16222             for(var i = start; i <= end; i++){
16223                 nodes.push(ns[i]);
16224             }
16225         } else{
16226             for(var i = start; i >= end; i--){
16227                 nodes.push(ns[i]);
16228             }
16229         }
16230         return nodes;
16231     },
16232
16233     /**
16234      * Finds the index of the passed node
16235      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16236      * @return {Number} The index of the node or -1
16237      */
16238     indexOf : function(node){
16239         node = this.getNode(node);
16240         if(typeof node.nodeIndex == "number"){
16241             return node.nodeIndex;
16242         }
16243         var ns = this.nodes;
16244         for(var i = 0, len = ns.length; i < len; i++){
16245             if(ns[i] == node){
16246                 return i;
16247             }
16248         }
16249         return -1;
16250     }
16251 });
16252 /*
16253  * - LGPL
16254  *
16255  * based on jquery fullcalendar
16256  * 
16257  */
16258
16259 Roo.bootstrap = Roo.bootstrap || {};
16260 /**
16261  * @class Roo.bootstrap.Calendar
16262  * @extends Roo.bootstrap.Component
16263  * Bootstrap Calendar class
16264  * @cfg {Boolean} loadMask (true|false) default false
16265  * @cfg {Object} header generate the user specific header of the calendar, default false
16266
16267  * @constructor
16268  * Create a new Container
16269  * @param {Object} config The config object
16270  */
16271
16272
16273
16274 Roo.bootstrap.Calendar = function(config){
16275     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16276      this.addEvents({
16277         /**
16278              * @event select
16279              * Fires when a date is selected
16280              * @param {DatePicker} this
16281              * @param {Date} date The selected date
16282              */
16283         'select': true,
16284         /**
16285              * @event monthchange
16286              * Fires when the displayed month changes 
16287              * @param {DatePicker} this
16288              * @param {Date} date The selected month
16289              */
16290         'monthchange': true,
16291         /**
16292              * @event evententer
16293              * Fires when mouse over an event
16294              * @param {Calendar} this
16295              * @param {event} Event
16296              */
16297         'evententer': true,
16298         /**
16299              * @event eventleave
16300              * Fires when the mouse leaves an
16301              * @param {Calendar} this
16302              * @param {event}
16303              */
16304         'eventleave': true,
16305         /**
16306              * @event eventclick
16307              * Fires when the mouse click an
16308              * @param {Calendar} this
16309              * @param {event}
16310              */
16311         'eventclick': true
16312         
16313     });
16314
16315 };
16316
16317 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16318     
16319      /**
16320      * @cfg {Number} startDay
16321      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16322      */
16323     startDay : 0,
16324     
16325     loadMask : false,
16326     
16327     header : false,
16328       
16329     getAutoCreate : function(){
16330         
16331         
16332         var fc_button = function(name, corner, style, content ) {
16333             return Roo.apply({},{
16334                 tag : 'span',
16335                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16336                          (corner.length ?
16337                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16338                             ''
16339                         ),
16340                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16341                 unselectable: 'on'
16342             });
16343         };
16344         
16345         var header = {};
16346         
16347         if(!this.header){
16348             header = {
16349                 tag : 'table',
16350                 cls : 'fc-header',
16351                 style : 'width:100%',
16352                 cn : [
16353                     {
16354                         tag: 'tr',
16355                         cn : [
16356                             {
16357                                 tag : 'td',
16358                                 cls : 'fc-header-left',
16359                                 cn : [
16360                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16361                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16362                                     { tag: 'span', cls: 'fc-header-space' },
16363                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16364
16365
16366                                 ]
16367                             },
16368
16369                             {
16370                                 tag : 'td',
16371                                 cls : 'fc-header-center',
16372                                 cn : [
16373                                     {
16374                                         tag: 'span',
16375                                         cls: 'fc-header-title',
16376                                         cn : {
16377                                             tag: 'H2',
16378                                             html : 'month / year'
16379                                         }
16380                                     }
16381
16382                                 ]
16383                             },
16384                             {
16385                                 tag : 'td',
16386                                 cls : 'fc-header-right',
16387                                 cn : [
16388                               /*      fc_button('month', 'left', '', 'month' ),
16389                                     fc_button('week', '', '', 'week' ),
16390                                     fc_button('day', 'right', '', 'day' )
16391                                 */    
16392
16393                                 ]
16394                             }
16395
16396                         ]
16397                     }
16398                 ]
16399             };
16400         }
16401         
16402         header = this.header;
16403         
16404        
16405         var cal_heads = function() {
16406             var ret = [];
16407             // fixme - handle this.
16408             
16409             for (var i =0; i < Date.dayNames.length; i++) {
16410                 var d = Date.dayNames[i];
16411                 ret.push({
16412                     tag: 'th',
16413                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16414                     html : d.substring(0,3)
16415                 });
16416                 
16417             }
16418             ret[0].cls += ' fc-first';
16419             ret[6].cls += ' fc-last';
16420             return ret;
16421         };
16422         var cal_cell = function(n) {
16423             return  {
16424                 tag: 'td',
16425                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16426                 cn : [
16427                     {
16428                         cn : [
16429                             {
16430                                 cls: 'fc-day-number',
16431                                 html: 'D'
16432                             },
16433                             {
16434                                 cls: 'fc-day-content',
16435                              
16436                                 cn : [
16437                                      {
16438                                         style: 'position: relative;' // height: 17px;
16439                                     }
16440                                 ]
16441                             }
16442                             
16443                             
16444                         ]
16445                     }
16446                 ]
16447                 
16448             }
16449         };
16450         var cal_rows = function() {
16451             
16452             var ret = [];
16453             for (var r = 0; r < 6; r++) {
16454                 var row= {
16455                     tag : 'tr',
16456                     cls : 'fc-week',
16457                     cn : []
16458                 };
16459                 
16460                 for (var i =0; i < Date.dayNames.length; i++) {
16461                     var d = Date.dayNames[i];
16462                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16463
16464                 }
16465                 row.cn[0].cls+=' fc-first';
16466                 row.cn[0].cn[0].style = 'min-height:90px';
16467                 row.cn[6].cls+=' fc-last';
16468                 ret.push(row);
16469                 
16470             }
16471             ret[0].cls += ' fc-first';
16472             ret[4].cls += ' fc-prev-last';
16473             ret[5].cls += ' fc-last';
16474             return ret;
16475             
16476         };
16477         
16478         var cal_table = {
16479             tag: 'table',
16480             cls: 'fc-border-separate',
16481             style : 'width:100%',
16482             cellspacing  : 0,
16483             cn : [
16484                 { 
16485                     tag: 'thead',
16486                     cn : [
16487                         { 
16488                             tag: 'tr',
16489                             cls : 'fc-first fc-last',
16490                             cn : cal_heads()
16491                         }
16492                     ]
16493                 },
16494                 { 
16495                     tag: 'tbody',
16496                     cn : cal_rows()
16497                 }
16498                   
16499             ]
16500         };
16501          
16502          var cfg = {
16503             cls : 'fc fc-ltr',
16504             cn : [
16505                 header,
16506                 {
16507                     cls : 'fc-content',
16508                     style : "position: relative;",
16509                     cn : [
16510                         {
16511                             cls : 'fc-view fc-view-month fc-grid',
16512                             style : 'position: relative',
16513                             unselectable : 'on',
16514                             cn : [
16515                                 {
16516                                     cls : 'fc-event-container',
16517                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16518                                 },
16519                                 cal_table
16520                             ]
16521                         }
16522                     ]
16523     
16524                 }
16525            ] 
16526             
16527         };
16528         
16529          
16530         
16531         return cfg;
16532     },
16533     
16534     
16535     initEvents : function()
16536     {
16537         if(!this.store){
16538             throw "can not find store for calendar";
16539         }
16540         
16541         var mark = {
16542             tag: "div",
16543             cls:"x-dlg-mask",
16544             style: "text-align:center",
16545             cn: [
16546                 {
16547                     tag: "div",
16548                     style: "background-color:white;width:50%;margin:250 auto",
16549                     cn: [
16550                         {
16551                             tag: "img",
16552                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16553                         },
16554                         {
16555                             tag: "span",
16556                             html: "Loading"
16557                         }
16558                         
16559                     ]
16560                 }
16561             ]
16562         };
16563         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16564         
16565         var size = this.el.select('.fc-content', true).first().getSize();
16566         this.maskEl.setSize(size.width, size.height);
16567         this.maskEl.enableDisplayMode("block");
16568         if(!this.loadMask){
16569             this.maskEl.hide();
16570         }
16571         
16572         this.store = Roo.factory(this.store, Roo.data);
16573         this.store.on('load', this.onLoad, this);
16574         this.store.on('beforeload', this.onBeforeLoad, this);
16575         
16576         this.resize();
16577         
16578         this.cells = this.el.select('.fc-day',true);
16579         //Roo.log(this.cells);
16580         this.textNodes = this.el.query('.fc-day-number');
16581         this.cells.addClassOnOver('fc-state-hover');
16582         
16583         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16584         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16585         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16586         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16587         
16588         this.on('monthchange', this.onMonthChange, this);
16589         
16590         this.update(new Date().clearTime());
16591     },
16592     
16593     resize : function() {
16594         var sz  = this.el.getSize();
16595         
16596         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16597         this.el.select('.fc-day-content div',true).setHeight(34);
16598     },
16599     
16600     
16601     // private
16602     showPrevMonth : function(e){
16603         this.update(this.activeDate.add("mo", -1));
16604     },
16605     showToday : function(e){
16606         this.update(new Date().clearTime());
16607     },
16608     // private
16609     showNextMonth : function(e){
16610         this.update(this.activeDate.add("mo", 1));
16611     },
16612
16613     // private
16614     showPrevYear : function(){
16615         this.update(this.activeDate.add("y", -1));
16616     },
16617
16618     // private
16619     showNextYear : function(){
16620         this.update(this.activeDate.add("y", 1));
16621     },
16622
16623     
16624    // private
16625     update : function(date)
16626     {
16627         var vd = this.activeDate;
16628         this.activeDate = date;
16629 //        if(vd && this.el){
16630 //            var t = date.getTime();
16631 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16632 //                Roo.log('using add remove');
16633 //                
16634 //                this.fireEvent('monthchange', this, date);
16635 //                
16636 //                this.cells.removeClass("fc-state-highlight");
16637 //                this.cells.each(function(c){
16638 //                   if(c.dateValue == t){
16639 //                       c.addClass("fc-state-highlight");
16640 //                       setTimeout(function(){
16641 //                            try{c.dom.firstChild.focus();}catch(e){}
16642 //                       }, 50);
16643 //                       return false;
16644 //                   }
16645 //                   return true;
16646 //                });
16647 //                return;
16648 //            }
16649 //        }
16650         
16651         var days = date.getDaysInMonth();
16652         
16653         var firstOfMonth = date.getFirstDateOfMonth();
16654         var startingPos = firstOfMonth.getDay()-this.startDay;
16655         
16656         if(startingPos < this.startDay){
16657             startingPos += 7;
16658         }
16659         
16660         var pm = date.add(Date.MONTH, -1);
16661         var prevStart = pm.getDaysInMonth()-startingPos;
16662 //        
16663         this.cells = this.el.select('.fc-day',true);
16664         this.textNodes = this.el.query('.fc-day-number');
16665         this.cells.addClassOnOver('fc-state-hover');
16666         
16667         var cells = this.cells.elements;
16668         var textEls = this.textNodes;
16669         
16670         Roo.each(cells, function(cell){
16671             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16672         });
16673         
16674         days += startingPos;
16675
16676         // convert everything to numbers so it's fast
16677         var day = 86400000;
16678         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16679         //Roo.log(d);
16680         //Roo.log(pm);
16681         //Roo.log(prevStart);
16682         
16683         var today = new Date().clearTime().getTime();
16684         var sel = date.clearTime().getTime();
16685         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16686         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16687         var ddMatch = this.disabledDatesRE;
16688         var ddText = this.disabledDatesText;
16689         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16690         var ddaysText = this.disabledDaysText;
16691         var format = this.format;
16692         
16693         var setCellClass = function(cal, cell){
16694             cell.row = 0;
16695             cell.events = [];
16696             cell.more = [];
16697             //Roo.log('set Cell Class');
16698             cell.title = "";
16699             var t = d.getTime();
16700             
16701             //Roo.log(d);
16702             
16703             cell.dateValue = t;
16704             if(t == today){
16705                 cell.className += " fc-today";
16706                 cell.className += " fc-state-highlight";
16707                 cell.title = cal.todayText;
16708             }
16709             if(t == sel){
16710                 // disable highlight in other month..
16711                 //cell.className += " fc-state-highlight";
16712                 
16713             }
16714             // disabling
16715             if(t < min) {
16716                 cell.className = " fc-state-disabled";
16717                 cell.title = cal.minText;
16718                 return;
16719             }
16720             if(t > max) {
16721                 cell.className = " fc-state-disabled";
16722                 cell.title = cal.maxText;
16723                 return;
16724             }
16725             if(ddays){
16726                 if(ddays.indexOf(d.getDay()) != -1){
16727                     cell.title = ddaysText;
16728                     cell.className = " fc-state-disabled";
16729                 }
16730             }
16731             if(ddMatch && format){
16732                 var fvalue = d.dateFormat(format);
16733                 if(ddMatch.test(fvalue)){
16734                     cell.title = ddText.replace("%0", fvalue);
16735                     cell.className = " fc-state-disabled";
16736                 }
16737             }
16738             
16739             if (!cell.initialClassName) {
16740                 cell.initialClassName = cell.dom.className;
16741             }
16742             
16743             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16744         };
16745
16746         var i = 0;
16747         
16748         for(; i < startingPos; i++) {
16749             textEls[i].innerHTML = (++prevStart);
16750             d.setDate(d.getDate()+1);
16751             
16752             cells[i].className = "fc-past fc-other-month";
16753             setCellClass(this, cells[i]);
16754         }
16755         
16756         var intDay = 0;
16757         
16758         for(; i < days; i++){
16759             intDay = i - startingPos + 1;
16760             textEls[i].innerHTML = (intDay);
16761             d.setDate(d.getDate()+1);
16762             
16763             cells[i].className = ''; // "x-date-active";
16764             setCellClass(this, cells[i]);
16765         }
16766         var extraDays = 0;
16767         
16768         for(; i < 42; i++) {
16769             textEls[i].innerHTML = (++extraDays);
16770             d.setDate(d.getDate()+1);
16771             
16772             cells[i].className = "fc-future fc-other-month";
16773             setCellClass(this, cells[i]);
16774         }
16775         
16776         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16777         
16778         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16779         
16780         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16781         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16782         
16783         if(totalRows != 6){
16784             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16785             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16786         }
16787         
16788         this.fireEvent('monthchange', this, date);
16789         
16790         
16791         /*
16792         if(!this.internalRender){
16793             var main = this.el.dom.firstChild;
16794             var w = main.offsetWidth;
16795             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16796             Roo.fly(main).setWidth(w);
16797             this.internalRender = true;
16798             // opera does not respect the auto grow header center column
16799             // then, after it gets a width opera refuses to recalculate
16800             // without a second pass
16801             if(Roo.isOpera && !this.secondPass){
16802                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16803                 this.secondPass = true;
16804                 this.update.defer(10, this, [date]);
16805             }
16806         }
16807         */
16808         
16809     },
16810     
16811     findCell : function(dt) {
16812         dt = dt.clearTime().getTime();
16813         var ret = false;
16814         this.cells.each(function(c){
16815             //Roo.log("check " +c.dateValue + '?=' + dt);
16816             if(c.dateValue == dt){
16817                 ret = c;
16818                 return false;
16819             }
16820             return true;
16821         });
16822         
16823         return ret;
16824     },
16825     
16826     findCells : function(ev) {
16827         var s = ev.start.clone().clearTime().getTime();
16828        // Roo.log(s);
16829         var e= ev.end.clone().clearTime().getTime();
16830        // Roo.log(e);
16831         var ret = [];
16832         this.cells.each(function(c){
16833              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16834             
16835             if(c.dateValue > e){
16836                 return ;
16837             }
16838             if(c.dateValue < s){
16839                 return ;
16840             }
16841             ret.push(c);
16842         });
16843         
16844         return ret;    
16845     },
16846     
16847 //    findBestRow: function(cells)
16848 //    {
16849 //        var ret = 0;
16850 //        
16851 //        for (var i =0 ; i < cells.length;i++) {
16852 //            ret  = Math.max(cells[i].rows || 0,ret);
16853 //        }
16854 //        return ret;
16855 //        
16856 //    },
16857     
16858     
16859     addItem : function(ev)
16860     {
16861         // look for vertical location slot in
16862         var cells = this.findCells(ev);
16863         
16864 //        ev.row = this.findBestRow(cells);
16865         
16866         // work out the location.
16867         
16868         var crow = false;
16869         var rows = [];
16870         for(var i =0; i < cells.length; i++) {
16871             
16872             cells[i].row = cells[0].row;
16873             
16874             if(i == 0){
16875                 cells[i].row = cells[i].row + 1;
16876             }
16877             
16878             if (!crow) {
16879                 crow = {
16880                     start : cells[i],
16881                     end :  cells[i]
16882                 };
16883                 continue;
16884             }
16885             if (crow.start.getY() == cells[i].getY()) {
16886                 // on same row.
16887                 crow.end = cells[i];
16888                 continue;
16889             }
16890             // different row.
16891             rows.push(crow);
16892             crow = {
16893                 start: cells[i],
16894                 end : cells[i]
16895             };
16896             
16897         }
16898         
16899         rows.push(crow);
16900         ev.els = [];
16901         ev.rows = rows;
16902         ev.cells = cells;
16903         
16904         cells[0].events.push(ev);
16905         
16906         this.calevents.push(ev);
16907     },
16908     
16909     clearEvents: function() {
16910         
16911         if(!this.calevents){
16912             return;
16913         }
16914         
16915         Roo.each(this.cells.elements, function(c){
16916             c.row = 0;
16917             c.events = [];
16918             c.more = [];
16919         });
16920         
16921         Roo.each(this.calevents, function(e) {
16922             Roo.each(e.els, function(el) {
16923                 el.un('mouseenter' ,this.onEventEnter, this);
16924                 el.un('mouseleave' ,this.onEventLeave, this);
16925                 el.remove();
16926             },this);
16927         },this);
16928         
16929         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16930             e.remove();
16931         });
16932         
16933     },
16934     
16935     renderEvents: function()
16936     {   
16937         var _this = this;
16938         
16939         this.cells.each(function(c) {
16940             
16941             if(c.row < 5){
16942                 return;
16943             }
16944             
16945             var ev = c.events;
16946             
16947             var r = 4;
16948             if(c.row != c.events.length){
16949                 r = 4 - (4 - (c.row - c.events.length));
16950             }
16951             
16952             c.events = ev.slice(0, r);
16953             c.more = ev.slice(r);
16954             
16955             if(c.more.length && c.more.length == 1){
16956                 c.events.push(c.more.pop());
16957             }
16958             
16959             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16960             
16961         });
16962             
16963         this.cells.each(function(c) {
16964             
16965             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16966             
16967             
16968             for (var e = 0; e < c.events.length; e++){
16969                 var ev = c.events[e];
16970                 var rows = ev.rows;
16971                 
16972                 for(var i = 0; i < rows.length; i++) {
16973                 
16974                     // how many rows should it span..
16975
16976                     var  cfg = {
16977                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16978                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16979
16980                         unselectable : "on",
16981                         cn : [
16982                             {
16983                                 cls: 'fc-event-inner',
16984                                 cn : [
16985     //                                {
16986     //                                  tag:'span',
16987     //                                  cls: 'fc-event-time',
16988     //                                  html : cells.length > 1 ? '' : ev.time
16989     //                                },
16990                                     {
16991                                       tag:'span',
16992                                       cls: 'fc-event-title',
16993                                       html : String.format('{0}', ev.title)
16994                                     }
16995
16996
16997                                 ]
16998                             },
16999                             {
17000                                 cls: 'ui-resizable-handle ui-resizable-e',
17001                                 html : '&nbsp;&nbsp;&nbsp'
17002                             }
17003
17004                         ]
17005                     };
17006
17007                     if (i == 0) {
17008                         cfg.cls += ' fc-event-start';
17009                     }
17010                     if ((i+1) == rows.length) {
17011                         cfg.cls += ' fc-event-end';
17012                     }
17013
17014                     var ctr = _this.el.select('.fc-event-container',true).first();
17015                     var cg = ctr.createChild(cfg);
17016
17017                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17018                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17019
17020                     var r = (c.more.length) ? 1 : 0;
17021                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17022                     cg.setWidth(ebox.right - sbox.x -2);
17023
17024                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17025                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17026                     cg.on('click', _this.onEventClick, _this, ev);
17027
17028                     ev.els.push(cg);
17029                     
17030                 }
17031                 
17032             }
17033             
17034             
17035             if(c.more.length){
17036                 var  cfg = {
17037                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17038                     style : 'position: absolute',
17039                     unselectable : "on",
17040                     cn : [
17041                         {
17042                             cls: 'fc-event-inner',
17043                             cn : [
17044                                 {
17045                                   tag:'span',
17046                                   cls: 'fc-event-title',
17047                                   html : 'More'
17048                                 }
17049
17050
17051                             ]
17052                         },
17053                         {
17054                             cls: 'ui-resizable-handle ui-resizable-e',
17055                             html : '&nbsp;&nbsp;&nbsp'
17056                         }
17057
17058                     ]
17059                 };
17060
17061                 var ctr = _this.el.select('.fc-event-container',true).first();
17062                 var cg = ctr.createChild(cfg);
17063
17064                 var sbox = c.select('.fc-day-content',true).first().getBox();
17065                 var ebox = c.select('.fc-day-content',true).first().getBox();
17066                 //Roo.log(cg);
17067                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17068                 cg.setWidth(ebox.right - sbox.x -2);
17069
17070                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17071                 
17072             }
17073             
17074         });
17075         
17076         
17077         
17078     },
17079     
17080     onEventEnter: function (e, el,event,d) {
17081         this.fireEvent('evententer', this, el, event);
17082     },
17083     
17084     onEventLeave: function (e, el,event,d) {
17085         this.fireEvent('eventleave', this, el, event);
17086     },
17087     
17088     onEventClick: function (e, el,event,d) {
17089         this.fireEvent('eventclick', this, el, event);
17090     },
17091     
17092     onMonthChange: function () {
17093         this.store.load();
17094     },
17095     
17096     onMoreEventClick: function(e, el, more)
17097     {
17098         var _this = this;
17099         
17100         this.calpopover.placement = 'right';
17101         this.calpopover.setTitle('More');
17102         
17103         this.calpopover.setContent('');
17104         
17105         var ctr = this.calpopover.el.select('.popover-content', true).first();
17106         
17107         Roo.each(more, function(m){
17108             var cfg = {
17109                 cls : 'fc-event-hori fc-event-draggable',
17110                 html : m.title
17111             };
17112             var cg = ctr.createChild(cfg);
17113             
17114             cg.on('click', _this.onEventClick, _this, m);
17115         });
17116         
17117         this.calpopover.show(el);
17118         
17119         
17120     },
17121     
17122     onLoad: function () 
17123     {   
17124         this.calevents = [];
17125         var cal = this;
17126         
17127         if(this.store.getCount() > 0){
17128             this.store.data.each(function(d){
17129                cal.addItem({
17130                     id : d.data.id,
17131                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17132                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17133                     time : d.data.start_time,
17134                     title : d.data.title,
17135                     description : d.data.description,
17136                     venue : d.data.venue
17137                 });
17138             });
17139         }
17140         
17141         this.renderEvents();
17142         
17143         if(this.calevents.length && this.loadMask){
17144             this.maskEl.hide();
17145         }
17146     },
17147     
17148     onBeforeLoad: function()
17149     {
17150         this.clearEvents();
17151         if(this.loadMask){
17152             this.maskEl.show();
17153         }
17154     }
17155 });
17156
17157  
17158  /*
17159  * - LGPL
17160  *
17161  * element
17162  * 
17163  */
17164
17165 /**
17166  * @class Roo.bootstrap.Popover
17167  * @extends Roo.bootstrap.Component
17168  * Bootstrap Popover class
17169  * @cfg {String} html contents of the popover   (or false to use children..)
17170  * @cfg {String} title of popover (or false to hide)
17171  * @cfg {String} placement how it is placed
17172  * @cfg {String} trigger click || hover (or false to trigger manually)
17173  * @cfg {String} over what (parent or false to trigger manually.)
17174  * @cfg {Number} delay - delay before showing
17175  
17176  * @constructor
17177  * Create a new Popover
17178  * @param {Object} config The config object
17179  */
17180
17181 Roo.bootstrap.Popover = function(config){
17182     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17183     
17184     this.addEvents({
17185         // raw events
17186          /**
17187          * @event show
17188          * After the popover show
17189          * 
17190          * @param {Roo.bootstrap.Popover} this
17191          */
17192         "show" : true,
17193         /**
17194          * @event hide
17195          * After the popover hide
17196          * 
17197          * @param {Roo.bootstrap.Popover} this
17198          */
17199         "hide" : true
17200     });
17201 };
17202
17203 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17204     
17205     title: 'Fill in a title',
17206     html: false,
17207     
17208     placement : 'right',
17209     trigger : 'hover', // hover
17210     
17211     delay : 0,
17212     
17213     over: 'parent',
17214     
17215     can_build_overlaid : false,
17216     
17217     getChildContainer : function()
17218     {
17219         return this.el.select('.popover-content',true).first();
17220     },
17221     
17222     getAutoCreate : function(){
17223          
17224         var cfg = {
17225            cls : 'popover roo-dynamic',
17226            style: 'display:block',
17227            cn : [
17228                 {
17229                     cls : 'arrow'
17230                 },
17231                 {
17232                     cls : 'popover-inner',
17233                     cn : [
17234                         {
17235                             tag: 'h3',
17236                             cls: 'popover-title',
17237                             html : this.title
17238                         },
17239                         {
17240                             cls : 'popover-content',
17241                             html : this.html
17242                         }
17243                     ]
17244                     
17245                 }
17246            ]
17247         };
17248         
17249         return cfg;
17250     },
17251     setTitle: function(str)
17252     {
17253         this.title = str;
17254         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17255     },
17256     setContent: function(str)
17257     {
17258         this.html = str;
17259         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17260     },
17261     // as it get's added to the bottom of the page.
17262     onRender : function(ct, position)
17263     {
17264         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17265         if(!this.el){
17266             var cfg = Roo.apply({},  this.getAutoCreate());
17267             cfg.id = Roo.id();
17268             
17269             if (this.cls) {
17270                 cfg.cls += ' ' + this.cls;
17271             }
17272             if (this.style) {
17273                 cfg.style = this.style;
17274             }
17275             //Roo.log("adding to ");
17276             this.el = Roo.get(document.body).createChild(cfg, position);
17277 //            Roo.log(this.el);
17278         }
17279         this.initEvents();
17280     },
17281     
17282     initEvents : function()
17283     {
17284         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17285         this.el.enableDisplayMode('block');
17286         this.el.hide();
17287         if (this.over === false) {
17288             return; 
17289         }
17290         if (this.triggers === false) {
17291             return;
17292         }
17293         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17294         var triggers = this.trigger ? this.trigger.split(' ') : [];
17295         Roo.each(triggers, function(trigger) {
17296         
17297             if (trigger == 'click') {
17298                 on_el.on('click', this.toggle, this);
17299             } else if (trigger != 'manual') {
17300                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17301                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17302       
17303                 on_el.on(eventIn  ,this.enter, this);
17304                 on_el.on(eventOut, this.leave, this);
17305             }
17306         }, this);
17307         
17308     },
17309     
17310     
17311     // private
17312     timeout : null,
17313     hoverState : null,
17314     
17315     toggle : function () {
17316         this.hoverState == 'in' ? this.leave() : this.enter();
17317     },
17318     
17319     enter : function () {
17320         
17321         clearTimeout(this.timeout);
17322     
17323         this.hoverState = 'in';
17324     
17325         if (!this.delay || !this.delay.show) {
17326             this.show();
17327             return;
17328         }
17329         var _t = this;
17330         this.timeout = setTimeout(function () {
17331             if (_t.hoverState == 'in') {
17332                 _t.show();
17333             }
17334         }, this.delay.show)
17335     },
17336     
17337     leave : function() {
17338         clearTimeout(this.timeout);
17339     
17340         this.hoverState = 'out';
17341     
17342         if (!this.delay || !this.delay.hide) {
17343             this.hide();
17344             return;
17345         }
17346         var _t = this;
17347         this.timeout = setTimeout(function () {
17348             if (_t.hoverState == 'out') {
17349                 _t.hide();
17350             }
17351         }, this.delay.hide)
17352     },
17353     
17354     show : function (on_el)
17355     {
17356         if (!on_el) {
17357             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17358         }
17359         
17360         // set content.
17361         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17362         if (this.html !== false) {
17363             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17364         }
17365         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17366         if (!this.title.length) {
17367             this.el.select('.popover-title',true).hide();
17368         }
17369         
17370         var placement = typeof this.placement == 'function' ?
17371             this.placement.call(this, this.el, on_el) :
17372             this.placement;
17373             
17374         var autoToken = /\s?auto?\s?/i;
17375         var autoPlace = autoToken.test(placement);
17376         if (autoPlace) {
17377             placement = placement.replace(autoToken, '') || 'top';
17378         }
17379         
17380         //this.el.detach()
17381         //this.el.setXY([0,0]);
17382         this.el.show();
17383         this.el.dom.style.display='block';
17384         this.el.addClass(placement);
17385         
17386         //this.el.appendTo(on_el);
17387         
17388         var p = this.getPosition();
17389         var box = this.el.getBox();
17390         
17391         if (autoPlace) {
17392             // fixme..
17393         }
17394         var align = Roo.bootstrap.Popover.alignment[placement];
17395         
17396 //        Roo.log(align);
17397         this.el.alignTo(on_el, align[0],align[1]);
17398         //var arrow = this.el.select('.arrow',true).first();
17399         //arrow.set(align[2], 
17400         
17401         this.el.addClass('in');
17402         
17403         
17404         if (this.el.hasClass('fade')) {
17405             // fade it?
17406         }
17407         
17408         this.hoverState = 'in';
17409         
17410         this.fireEvent('show', this);
17411         
17412     },
17413     hide : function()
17414     {
17415         this.el.setXY([0,0]);
17416         this.el.removeClass('in');
17417         this.el.hide();
17418         this.hoverState = null;
17419         
17420         this.fireEvent('hide', this);
17421     }
17422     
17423 });
17424
17425 Roo.bootstrap.Popover.alignment = {
17426     'left' : ['r-l', [-10,0], 'right'],
17427     'right' : ['l-r', [10,0], 'left'],
17428     'bottom' : ['t-b', [0,10], 'top'],
17429     'top' : [ 'b-t', [0,-10], 'bottom']
17430 };
17431
17432  /*
17433  * - LGPL
17434  *
17435  * Progress
17436  * 
17437  */
17438
17439 /**
17440  * @class Roo.bootstrap.Progress
17441  * @extends Roo.bootstrap.Component
17442  * Bootstrap Progress class
17443  * @cfg {Boolean} striped striped of the progress bar
17444  * @cfg {Boolean} active animated of the progress bar
17445  * 
17446  * 
17447  * @constructor
17448  * Create a new Progress
17449  * @param {Object} config The config object
17450  */
17451
17452 Roo.bootstrap.Progress = function(config){
17453     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17454 };
17455
17456 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17457     
17458     striped : false,
17459     active: false,
17460     
17461     getAutoCreate : function(){
17462         var cfg = {
17463             tag: 'div',
17464             cls: 'progress'
17465         };
17466         
17467         
17468         if(this.striped){
17469             cfg.cls += ' progress-striped';
17470         }
17471       
17472         if(this.active){
17473             cfg.cls += ' active';
17474         }
17475         
17476         
17477         return cfg;
17478     }
17479    
17480 });
17481
17482  
17483
17484  /*
17485  * - LGPL
17486  *
17487  * ProgressBar
17488  * 
17489  */
17490
17491 /**
17492  * @class Roo.bootstrap.ProgressBar
17493  * @extends Roo.bootstrap.Component
17494  * Bootstrap ProgressBar class
17495  * @cfg {Number} aria_valuenow aria-value now
17496  * @cfg {Number} aria_valuemin aria-value min
17497  * @cfg {Number} aria_valuemax aria-value max
17498  * @cfg {String} label label for the progress bar
17499  * @cfg {String} panel (success | info | warning | danger )
17500  * @cfg {String} role role of the progress bar
17501  * @cfg {String} sr_only text
17502  * 
17503  * 
17504  * @constructor
17505  * Create a new ProgressBar
17506  * @param {Object} config The config object
17507  */
17508
17509 Roo.bootstrap.ProgressBar = function(config){
17510     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17511 };
17512
17513 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17514     
17515     aria_valuenow : 0,
17516     aria_valuemin : 0,
17517     aria_valuemax : 100,
17518     label : false,
17519     panel : false,
17520     role : false,
17521     sr_only: false,
17522     
17523     getAutoCreate : function()
17524     {
17525         
17526         var cfg = {
17527             tag: 'div',
17528             cls: 'progress-bar',
17529             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17530         };
17531         
17532         if(this.sr_only){
17533             cfg.cn = {
17534                 tag: 'span',
17535                 cls: 'sr-only',
17536                 html: this.sr_only
17537             }
17538         }
17539         
17540         if(this.role){
17541             cfg.role = this.role;
17542         }
17543         
17544         if(this.aria_valuenow){
17545             cfg['aria-valuenow'] = this.aria_valuenow;
17546         }
17547         
17548         if(this.aria_valuemin){
17549             cfg['aria-valuemin'] = this.aria_valuemin;
17550         }
17551         
17552         if(this.aria_valuemax){
17553             cfg['aria-valuemax'] = this.aria_valuemax;
17554         }
17555         
17556         if(this.label && !this.sr_only){
17557             cfg.html = this.label;
17558         }
17559         
17560         if(this.panel){
17561             cfg.cls += ' progress-bar-' + this.panel;
17562         }
17563         
17564         return cfg;
17565     },
17566     
17567     update : function(aria_valuenow)
17568     {
17569         this.aria_valuenow = aria_valuenow;
17570         
17571         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17572     }
17573    
17574 });
17575
17576  
17577
17578  /*
17579  * - LGPL
17580  *
17581  * column
17582  * 
17583  */
17584
17585 /**
17586  * @class Roo.bootstrap.TabGroup
17587  * @extends Roo.bootstrap.Column
17588  * Bootstrap Column class
17589  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17590  * @cfg {Boolean} carousel true to make the group behave like a carousel
17591  * @cfg {Boolean} bullets show bullets for the panels
17592  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17593  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17594  * @cfg {Boolean} showarrow (true|false) show arrow default true
17595  * 
17596  * @constructor
17597  * Create a new TabGroup
17598  * @param {Object} config The config object
17599  */
17600
17601 Roo.bootstrap.TabGroup = function(config){
17602     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17603     if (!this.navId) {
17604         this.navId = Roo.id();
17605     }
17606     this.tabs = [];
17607     Roo.bootstrap.TabGroup.register(this);
17608     
17609 };
17610
17611 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17612     
17613     carousel : false,
17614     transition : false,
17615     bullets : 0,
17616     timer : 0,
17617     autoslide : false,
17618     slideFn : false,
17619     slideOnTouch : false,
17620     showarrow : true,
17621     
17622     getAutoCreate : function()
17623     {
17624         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17625         
17626         cfg.cls += ' tab-content';
17627         
17628         if (this.carousel) {
17629             cfg.cls += ' carousel slide';
17630             
17631             cfg.cn = [{
17632                cls : 'carousel-inner',
17633                cn : []
17634             }];
17635         
17636             if(this.bullets  && !Roo.isTouch){
17637                 
17638                 var bullets = {
17639                     cls : 'carousel-bullets',
17640                     cn : []
17641                 };
17642                
17643                 if(this.bullets_cls){
17644                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17645                 }
17646                 
17647                 bullets.cn.push({
17648                     cls : 'clear'
17649                 });
17650                 
17651                 cfg.cn[0].cn.push(bullets);
17652             }
17653             
17654             if(this.showarrow){
17655                 cfg.cn[0].cn.push({
17656                     tag : 'div',
17657                     class : 'carousel-arrow',
17658                     cn : [
17659                         {
17660                             tag : 'div',
17661                             class : 'carousel-prev',
17662                             cn : [
17663                                 {
17664                                     tag : 'i',
17665                                     class : 'fa fa-chevron-left'
17666                                 }
17667                             ]
17668                         },
17669                         {
17670                             tag : 'div',
17671                             class : 'carousel-next',
17672                             cn : [
17673                                 {
17674                                     tag : 'i',
17675                                     class : 'fa fa-chevron-right'
17676                                 }
17677                             ]
17678                         }
17679                     ]
17680                 });
17681             }
17682             
17683         }
17684         
17685         return cfg;
17686     },
17687     
17688     initEvents:  function()
17689     {
17690 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17691 //            this.el.on("touchstart", this.onTouchStart, this);
17692 //        }
17693         
17694         if(this.autoslide){
17695             var _this = this;
17696             
17697             this.slideFn = window.setInterval(function() {
17698                 _this.showPanelNext();
17699             }, this.timer);
17700         }
17701         
17702         if(this.showarrow){
17703             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17704             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17705         }
17706         
17707         
17708     },
17709     
17710 //    onTouchStart : function(e, el, o)
17711 //    {
17712 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17713 //            return;
17714 //        }
17715 //        
17716 //        this.showPanelNext();
17717 //    },
17718     
17719     
17720     getChildContainer : function()
17721     {
17722         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17723     },
17724     
17725     /**
17726     * register a Navigation item
17727     * @param {Roo.bootstrap.NavItem} the navitem to add
17728     */
17729     register : function(item)
17730     {
17731         this.tabs.push( item);
17732         item.navId = this.navId; // not really needed..
17733         this.addBullet();
17734     
17735     },
17736     
17737     getActivePanel : function()
17738     {
17739         var r = false;
17740         Roo.each(this.tabs, function(t) {
17741             if (t.active) {
17742                 r = t;
17743                 return false;
17744             }
17745             return null;
17746         });
17747         return r;
17748         
17749     },
17750     getPanelByName : function(n)
17751     {
17752         var r = false;
17753         Roo.each(this.tabs, function(t) {
17754             if (t.tabId == n) {
17755                 r = t;
17756                 return false;
17757             }
17758             return null;
17759         });
17760         return r;
17761     },
17762     indexOfPanel : function(p)
17763     {
17764         var r = false;
17765         Roo.each(this.tabs, function(t,i) {
17766             if (t.tabId == p.tabId) {
17767                 r = i;
17768                 return false;
17769             }
17770             return null;
17771         });
17772         return r;
17773     },
17774     /**
17775      * show a specific panel
17776      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17777      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17778      */
17779     showPanel : function (pan)
17780     {
17781         if(this.transition || typeof(pan) == 'undefined'){
17782             Roo.log("waiting for the transitionend");
17783             return;
17784         }
17785         
17786         if (typeof(pan) == 'number') {
17787             pan = this.tabs[pan];
17788         }
17789         
17790         if (typeof(pan) == 'string') {
17791             pan = this.getPanelByName(pan);
17792         }
17793         
17794         var cur = this.getActivePanel();
17795         
17796         if(!pan || !cur){
17797             Roo.log('pan or acitve pan is undefined');
17798             return false;
17799         }
17800         
17801         if (pan.tabId == this.getActivePanel().tabId) {
17802             return true;
17803         }
17804         
17805         if (false === cur.fireEvent('beforedeactivate')) {
17806             return false;
17807         }
17808         
17809         if(this.bullets > 0 && !Roo.isTouch){
17810             this.setActiveBullet(this.indexOfPanel(pan));
17811         }
17812         
17813         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17814             
17815             this.transition = true;
17816             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17817             var lr = dir == 'next' ? 'left' : 'right';
17818             pan.el.addClass(dir); // or prev
17819             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17820             cur.el.addClass(lr); // or right
17821             pan.el.addClass(lr);
17822             
17823             var _this = this;
17824             cur.el.on('transitionend', function() {
17825                 Roo.log("trans end?");
17826                 
17827                 pan.el.removeClass([lr,dir]);
17828                 pan.setActive(true);
17829                 
17830                 cur.el.removeClass([lr]);
17831                 cur.setActive(false);
17832                 
17833                 _this.transition = false;
17834                 
17835             }, this, { single:  true } );
17836             
17837             return true;
17838         }
17839         
17840         cur.setActive(false);
17841         pan.setActive(true);
17842         
17843         return true;
17844         
17845     },
17846     showPanelNext : function()
17847     {
17848         var i = this.indexOfPanel(this.getActivePanel());
17849         
17850         if (i >= this.tabs.length - 1 && !this.autoslide) {
17851             return;
17852         }
17853         
17854         if (i >= this.tabs.length - 1 && this.autoslide) {
17855             i = -1;
17856         }
17857         
17858         this.showPanel(this.tabs[i+1]);
17859     },
17860     
17861     showPanelPrev : function()
17862     {
17863         var i = this.indexOfPanel(this.getActivePanel());
17864         
17865         if (i  < 1 && !this.autoslide) {
17866             return;
17867         }
17868         
17869         if (i < 1 && this.autoslide) {
17870             i = this.tabs.length;
17871         }
17872         
17873         this.showPanel(this.tabs[i-1]);
17874     },
17875     
17876     
17877     addBullet: function()
17878     {
17879         if(!this.bullets || Roo.isTouch){
17880             return;
17881         }
17882         var ctr = this.el.select('.carousel-bullets',true).first();
17883         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17884         var bullet = ctr.createChild({
17885             cls : 'bullet bullet-' + i
17886         },ctr.dom.lastChild);
17887         
17888         
17889         var _this = this;
17890         
17891         bullet.on('click', (function(e, el, o, ii, t){
17892
17893             e.preventDefault();
17894
17895             this.showPanel(ii);
17896
17897             if(this.autoslide && this.slideFn){
17898                 clearInterval(this.slideFn);
17899                 this.slideFn = window.setInterval(function() {
17900                     _this.showPanelNext();
17901                 }, this.timer);
17902             }
17903
17904         }).createDelegate(this, [i, bullet], true));
17905                 
17906         
17907     },
17908      
17909     setActiveBullet : function(i)
17910     {
17911         if(Roo.isTouch){
17912             return;
17913         }
17914         
17915         Roo.each(this.el.select('.bullet', true).elements, function(el){
17916             el.removeClass('selected');
17917         });
17918
17919         var bullet = this.el.select('.bullet-' + i, true).first();
17920         
17921         if(!bullet){
17922             return;
17923         }
17924         
17925         bullet.addClass('selected');
17926     }
17927     
17928     
17929   
17930 });
17931
17932  
17933
17934  
17935  
17936 Roo.apply(Roo.bootstrap.TabGroup, {
17937     
17938     groups: {},
17939      /**
17940     * register a Navigation Group
17941     * @param {Roo.bootstrap.NavGroup} the navgroup to add
17942     */
17943     register : function(navgrp)
17944     {
17945         this.groups[navgrp.navId] = navgrp;
17946         
17947     },
17948     /**
17949     * fetch a Navigation Group based on the navigation ID
17950     * if one does not exist , it will get created.
17951     * @param {string} the navgroup to add
17952     * @returns {Roo.bootstrap.NavGroup} the navgroup 
17953     */
17954     get: function(navId) {
17955         if (typeof(this.groups[navId]) == 'undefined') {
17956             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17957         }
17958         return this.groups[navId] ;
17959     }
17960     
17961     
17962     
17963 });
17964
17965  /*
17966  * - LGPL
17967  *
17968  * TabPanel
17969  * 
17970  */
17971
17972 /**
17973  * @class Roo.bootstrap.TabPanel
17974  * @extends Roo.bootstrap.Component
17975  * Bootstrap TabPanel class
17976  * @cfg {Boolean} active panel active
17977  * @cfg {String} html panel content
17978  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17979  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17980  * @cfg {String} href click to link..
17981  * 
17982  * 
17983  * @constructor
17984  * Create a new TabPanel
17985  * @param {Object} config The config object
17986  */
17987
17988 Roo.bootstrap.TabPanel = function(config){
17989     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17990     this.addEvents({
17991         /**
17992              * @event changed
17993              * Fires when the active status changes
17994              * @param {Roo.bootstrap.TabPanel} this
17995              * @param {Boolean} state the new state
17996             
17997          */
17998         'changed': true,
17999         /**
18000              * @event beforedeactivate
18001              * Fires before a tab is de-activated - can be used to do validation on a form.
18002              * @param {Roo.bootstrap.TabPanel} this
18003              * @return {Boolean} false if there is an error
18004             
18005          */
18006         'beforedeactivate': true
18007      });
18008     
18009     this.tabId = this.tabId || Roo.id();
18010   
18011 };
18012
18013 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18014     
18015     active: false,
18016     html: false,
18017     tabId: false,
18018     navId : false,
18019     href : '',
18020     
18021     getAutoCreate : function(){
18022         var cfg = {
18023             tag: 'div',
18024             // item is needed for carousel - not sure if it has any effect otherwise
18025             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18026             html: this.html || ''
18027         };
18028         
18029         if(this.active){
18030             cfg.cls += ' active';
18031         }
18032         
18033         if(this.tabId){
18034             cfg.tabId = this.tabId;
18035         }
18036         
18037         
18038         return cfg;
18039     },
18040     
18041     initEvents:  function()
18042     {
18043         var p = this.parent();
18044         
18045         this.navId = this.navId || p.navId;
18046         
18047         if (typeof(this.navId) != 'undefined') {
18048             // not really needed.. but just in case.. parent should be a NavGroup.
18049             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18050             
18051             tg.register(this);
18052             
18053             var i = tg.tabs.length - 1;
18054             
18055             if(this.active && tg.bullets > 0 && i < tg.bullets){
18056                 tg.setActiveBullet(i);
18057             }
18058         }
18059         
18060         this.el.on('click', this.onClick, this);
18061         
18062         if(Roo.isTouch){
18063             this.el.on("touchstart", this.onTouchStart, this);
18064             this.el.on("touchmove", this.onTouchMove, this);
18065             this.el.on("touchend", this.onTouchEnd, this);
18066         }
18067         
18068     },
18069     
18070     onRender : function(ct, position)
18071     {
18072         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18073     },
18074     
18075     setActive : function(state)
18076     {
18077         Roo.log("panel - set active " + this.tabId + "=" + state);
18078         
18079         this.active = state;
18080         if (!state) {
18081             this.el.removeClass('active');
18082             
18083         } else  if (!this.el.hasClass('active')) {
18084             this.el.addClass('active');
18085         }
18086         
18087         this.fireEvent('changed', this, state);
18088     },
18089     
18090     onClick : function(e)
18091     {
18092         e.preventDefault();
18093         
18094         if(!this.href.length){
18095             return;
18096         }
18097         
18098         window.location.href = this.href;
18099     },
18100     
18101     startX : 0,
18102     startY : 0,
18103     endX : 0,
18104     endY : 0,
18105     swiping : false,
18106     
18107     onTouchStart : function(e)
18108     {
18109         this.swiping = false;
18110         
18111         this.startX = e.browserEvent.touches[0].clientX;
18112         this.startY = e.browserEvent.touches[0].clientY;
18113     },
18114     
18115     onTouchMove : function(e)
18116     {
18117         this.swiping = true;
18118         
18119         this.endX = e.browserEvent.touches[0].clientX;
18120         this.endY = e.browserEvent.touches[0].clientY;
18121     },
18122     
18123     onTouchEnd : function(e)
18124     {
18125         if(!this.swiping){
18126             this.onClick(e);
18127             return;
18128         }
18129         
18130         var tabGroup = this.parent();
18131         
18132         if(this.endX > this.startX){ // swiping right
18133             tabGroup.showPanelPrev();
18134             return;
18135         }
18136         
18137         if(this.startX > this.endX){ // swiping left
18138             tabGroup.showPanelNext();
18139             return;
18140         }
18141     }
18142     
18143     
18144 });
18145  
18146
18147  
18148
18149  /*
18150  * - LGPL
18151  *
18152  * DateField
18153  * 
18154  */
18155
18156 /**
18157  * @class Roo.bootstrap.DateField
18158  * @extends Roo.bootstrap.Input
18159  * Bootstrap DateField class
18160  * @cfg {Number} weekStart default 0
18161  * @cfg {String} viewMode default empty, (months|years)
18162  * @cfg {String} minViewMode default empty, (months|years)
18163  * @cfg {Number} startDate default -Infinity
18164  * @cfg {Number} endDate default Infinity
18165  * @cfg {Boolean} todayHighlight default false
18166  * @cfg {Boolean} todayBtn default false
18167  * @cfg {Boolean} calendarWeeks default false
18168  * @cfg {Object} daysOfWeekDisabled default empty
18169  * @cfg {Boolean} singleMode default false (true | false)
18170  * 
18171  * @cfg {Boolean} keyboardNavigation default true
18172  * @cfg {String} language default en
18173  * 
18174  * @constructor
18175  * Create a new DateField
18176  * @param {Object} config The config object
18177  */
18178
18179 Roo.bootstrap.DateField = function(config){
18180     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18181      this.addEvents({
18182             /**
18183              * @event show
18184              * Fires when this field show.
18185              * @param {Roo.bootstrap.DateField} this
18186              * @param {Mixed} date The date value
18187              */
18188             show : true,
18189             /**
18190              * @event show
18191              * Fires when this field hide.
18192              * @param {Roo.bootstrap.DateField} this
18193              * @param {Mixed} date The date value
18194              */
18195             hide : true,
18196             /**
18197              * @event select
18198              * Fires when select a date.
18199              * @param {Roo.bootstrap.DateField} this
18200              * @param {Mixed} date The date value
18201              */
18202             select : true,
18203             /**
18204              * @event beforeselect
18205              * Fires when before select a date.
18206              * @param {Roo.bootstrap.DateField} this
18207              * @param {Mixed} date The date value
18208              */
18209             beforeselect : true
18210         });
18211 };
18212
18213 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18214     
18215     /**
18216      * @cfg {String} format
18217      * The default date format string which can be overriden for localization support.  The format must be
18218      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18219      */
18220     format : "m/d/y",
18221     /**
18222      * @cfg {String} altFormats
18223      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18224      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18225      */
18226     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18227     
18228     weekStart : 0,
18229     
18230     viewMode : '',
18231     
18232     minViewMode : '',
18233     
18234     todayHighlight : false,
18235     
18236     todayBtn: false,
18237     
18238     language: 'en',
18239     
18240     keyboardNavigation: true,
18241     
18242     calendarWeeks: false,
18243     
18244     startDate: -Infinity,
18245     
18246     endDate: Infinity,
18247     
18248     daysOfWeekDisabled: [],
18249     
18250     _events: [],
18251     
18252     singleMode : false,
18253     
18254     UTCDate: function()
18255     {
18256         return new Date(Date.UTC.apply(Date, arguments));
18257     },
18258     
18259     UTCToday: function()
18260     {
18261         var today = new Date();
18262         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18263     },
18264     
18265     getDate: function() {
18266             var d = this.getUTCDate();
18267             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18268     },
18269     
18270     getUTCDate: function() {
18271             return this.date;
18272     },
18273     
18274     setDate: function(d) {
18275             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18276     },
18277     
18278     setUTCDate: function(d) {
18279             this.date = d;
18280             this.setValue(this.formatDate(this.date));
18281     },
18282         
18283     onRender: function(ct, position)
18284     {
18285         
18286         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18287         
18288         this.language = this.language || 'en';
18289         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18290         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18291         
18292         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18293         this.format = this.format || 'm/d/y';
18294         this.isInline = false;
18295         this.isInput = true;
18296         this.component = this.el.select('.add-on', true).first() || false;
18297         this.component = (this.component && this.component.length === 0) ? false : this.component;
18298         this.hasInput = this.component && this.inputEl().length;
18299         
18300         if (typeof(this.minViewMode === 'string')) {
18301             switch (this.minViewMode) {
18302                 case 'months':
18303                     this.minViewMode = 1;
18304                     break;
18305                 case 'years':
18306                     this.minViewMode = 2;
18307                     break;
18308                 default:
18309                     this.minViewMode = 0;
18310                     break;
18311             }
18312         }
18313         
18314         if (typeof(this.viewMode === 'string')) {
18315             switch (this.viewMode) {
18316                 case 'months':
18317                     this.viewMode = 1;
18318                     break;
18319                 case 'years':
18320                     this.viewMode = 2;
18321                     break;
18322                 default:
18323                     this.viewMode = 0;
18324                     break;
18325             }
18326         }
18327                 
18328         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18329         
18330 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18331         
18332         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18333         
18334         this.picker().on('mousedown', this.onMousedown, this);
18335         this.picker().on('click', this.onClick, this);
18336         
18337         this.picker().addClass('datepicker-dropdown');
18338         
18339         this.startViewMode = this.viewMode;
18340         
18341         if(this.singleMode){
18342             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18343                 v.setVisibilityMode(Roo.Element.DISPLAY);
18344                 v.hide();
18345             });
18346             
18347             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18348                 v.setStyle('width', '189px');
18349             });
18350         }
18351         
18352         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18353             if(!this.calendarWeeks){
18354                 v.remove();
18355                 return;
18356             }
18357             
18358             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18359             v.attr('colspan', function(i, val){
18360                 return parseInt(val) + 1;
18361             });
18362         });
18363                         
18364         
18365         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18366         
18367         this.setStartDate(this.startDate);
18368         this.setEndDate(this.endDate);
18369         
18370         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18371         
18372         this.fillDow();
18373         this.fillMonths();
18374         this.update();
18375         this.showMode();
18376         
18377         if(this.isInline) {
18378             this.show();
18379         }
18380     },
18381     
18382     picker : function()
18383     {
18384         return this.pickerEl;
18385 //        return this.el.select('.datepicker', true).first();
18386     },
18387     
18388     fillDow: function()
18389     {
18390         var dowCnt = this.weekStart;
18391         
18392         var dow = {
18393             tag: 'tr',
18394             cn: [
18395                 
18396             ]
18397         };
18398         
18399         if(this.calendarWeeks){
18400             dow.cn.push({
18401                 tag: 'th',
18402                 cls: 'cw',
18403                 html: '&nbsp;'
18404             })
18405         }
18406         
18407         while (dowCnt < this.weekStart + 7) {
18408             dow.cn.push({
18409                 tag: 'th',
18410                 cls: 'dow',
18411                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18412             });
18413         }
18414         
18415         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18416     },
18417     
18418     fillMonths: function()
18419     {    
18420         var i = 0;
18421         var months = this.picker().select('>.datepicker-months td', true).first();
18422         
18423         months.dom.innerHTML = '';
18424         
18425         while (i < 12) {
18426             var month = {
18427                 tag: 'span',
18428                 cls: 'month',
18429                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18430             };
18431             
18432             months.createChild(month);
18433         }
18434         
18435     },
18436     
18437     update: function()
18438     {
18439         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;
18440         
18441         if (this.date < this.startDate) {
18442             this.viewDate = new Date(this.startDate);
18443         } else if (this.date > this.endDate) {
18444             this.viewDate = new Date(this.endDate);
18445         } else {
18446             this.viewDate = new Date(this.date);
18447         }
18448         
18449         this.fill();
18450     },
18451     
18452     fill: function() 
18453     {
18454         var d = new Date(this.viewDate),
18455                 year = d.getUTCFullYear(),
18456                 month = d.getUTCMonth(),
18457                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18458                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18459                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18460                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18461                 currentDate = this.date && this.date.valueOf(),
18462                 today = this.UTCToday();
18463         
18464         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18465         
18466 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18467         
18468 //        this.picker.select('>tfoot th.today').
18469 //                                              .text(dates[this.language].today)
18470 //                                              .toggle(this.todayBtn !== false);
18471     
18472         this.updateNavArrows();
18473         this.fillMonths();
18474                                                 
18475         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18476         
18477         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18478          
18479         prevMonth.setUTCDate(day);
18480         
18481         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18482         
18483         var nextMonth = new Date(prevMonth);
18484         
18485         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18486         
18487         nextMonth = nextMonth.valueOf();
18488         
18489         var fillMonths = false;
18490         
18491         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18492         
18493         while(prevMonth.valueOf() < nextMonth) {
18494             var clsName = '';
18495             
18496             if (prevMonth.getUTCDay() === this.weekStart) {
18497                 if(fillMonths){
18498                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18499                 }
18500                     
18501                 fillMonths = {
18502                     tag: 'tr',
18503                     cn: []
18504                 };
18505                 
18506                 if(this.calendarWeeks){
18507                     // ISO 8601: First week contains first thursday.
18508                     // ISO also states week starts on Monday, but we can be more abstract here.
18509                     var
18510                     // Start of current week: based on weekstart/current date
18511                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18512                     // Thursday of this week
18513                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18514                     // First Thursday of year, year from thursday
18515                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18516                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18517                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18518                     
18519                     fillMonths.cn.push({
18520                         tag: 'td',
18521                         cls: 'cw',
18522                         html: calWeek
18523                     });
18524                 }
18525             }
18526             
18527             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18528                 clsName += ' old';
18529             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18530                 clsName += ' new';
18531             }
18532             if (this.todayHighlight &&
18533                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18534                 prevMonth.getUTCMonth() == today.getMonth() &&
18535                 prevMonth.getUTCDate() == today.getDate()) {
18536                 clsName += ' today';
18537             }
18538             
18539             if (currentDate && prevMonth.valueOf() === currentDate) {
18540                 clsName += ' active';
18541             }
18542             
18543             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18544                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18545                     clsName += ' disabled';
18546             }
18547             
18548             fillMonths.cn.push({
18549                 tag: 'td',
18550                 cls: 'day ' + clsName,
18551                 html: prevMonth.getDate()
18552             });
18553             
18554             prevMonth.setDate(prevMonth.getDate()+1);
18555         }
18556           
18557         var currentYear = this.date && this.date.getUTCFullYear();
18558         var currentMonth = this.date && this.date.getUTCMonth();
18559         
18560         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18561         
18562         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18563             v.removeClass('active');
18564             
18565             if(currentYear === year && k === currentMonth){
18566                 v.addClass('active');
18567             }
18568             
18569             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18570                 v.addClass('disabled');
18571             }
18572             
18573         });
18574         
18575         
18576         year = parseInt(year/10, 10) * 10;
18577         
18578         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18579         
18580         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18581         
18582         year -= 1;
18583         for (var i = -1; i < 11; i++) {
18584             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18585                 tag: 'span',
18586                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18587                 html: year
18588             });
18589             
18590             year += 1;
18591         }
18592     },
18593     
18594     showMode: function(dir) 
18595     {
18596         if (dir) {
18597             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18598         }
18599         
18600         Roo.each(this.picker().select('>div',true).elements, function(v){
18601             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18602             v.hide();
18603         });
18604         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18605     },
18606     
18607     place: function()
18608     {
18609         if(this.isInline) {
18610             return;
18611         }
18612         
18613         this.picker().removeClass(['bottom', 'top']);
18614         
18615         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18616             /*
18617              * place to the top of element!
18618              *
18619              */
18620             
18621             this.picker().addClass('top');
18622             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18623             
18624             return;
18625         }
18626         
18627         this.picker().addClass('bottom');
18628         
18629         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18630     },
18631     
18632     parseDate : function(value)
18633     {
18634         if(!value || value instanceof Date){
18635             return value;
18636         }
18637         var v = Date.parseDate(value, this.format);
18638         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18639             v = Date.parseDate(value, 'Y-m-d');
18640         }
18641         if(!v && this.altFormats){
18642             if(!this.altFormatsArray){
18643                 this.altFormatsArray = this.altFormats.split("|");
18644             }
18645             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18646                 v = Date.parseDate(value, this.altFormatsArray[i]);
18647             }
18648         }
18649         return v;
18650     },
18651     
18652     formatDate : function(date, fmt)
18653     {   
18654         return (!date || !(date instanceof Date)) ?
18655         date : date.dateFormat(fmt || this.format);
18656     },
18657     
18658     onFocus : function()
18659     {
18660         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18661         this.show();
18662     },
18663     
18664     onBlur : function()
18665     {
18666         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18667         
18668         var d = this.inputEl().getValue();
18669         
18670         this.setValue(d);
18671                 
18672         this.hide();
18673     },
18674     
18675     show : function()
18676     {
18677         this.picker().show();
18678         this.update();
18679         this.place();
18680         
18681         this.fireEvent('show', this, this.date);
18682     },
18683     
18684     hide : function()
18685     {
18686         if(this.isInline) {
18687             return;
18688         }
18689         this.picker().hide();
18690         this.viewMode = this.startViewMode;
18691         this.showMode();
18692         
18693         this.fireEvent('hide', this, this.date);
18694         
18695     },
18696     
18697     onMousedown: function(e)
18698     {
18699         e.stopPropagation();
18700         e.preventDefault();
18701     },
18702     
18703     keyup: function(e)
18704     {
18705         Roo.bootstrap.DateField.superclass.keyup.call(this);
18706         this.update();
18707     },
18708
18709     setValue: function(v)
18710     {
18711         if(this.fireEvent('beforeselect', this, v) !== false){
18712             var d = new Date(this.parseDate(v) ).clearTime();
18713         
18714             if(isNaN(d.getTime())){
18715                 this.date = this.viewDate = '';
18716                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18717                 return;
18718             }
18719
18720             v = this.formatDate(d);
18721
18722             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18723
18724             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18725
18726             this.update();
18727
18728             this.fireEvent('select', this, this.date);
18729         }
18730     },
18731     
18732     getValue: function()
18733     {
18734         return this.formatDate(this.date);
18735     },
18736     
18737     fireKey: function(e)
18738     {
18739         if (!this.picker().isVisible()){
18740             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18741                 this.show();
18742             }
18743             return;
18744         }
18745         
18746         var dateChanged = false,
18747         dir, day, month,
18748         newDate, newViewDate;
18749         
18750         switch(e.keyCode){
18751             case 27: // escape
18752                 this.hide();
18753                 e.preventDefault();
18754                 break;
18755             case 37: // left
18756             case 39: // right
18757                 if (!this.keyboardNavigation) {
18758                     break;
18759                 }
18760                 dir = e.keyCode == 37 ? -1 : 1;
18761                 
18762                 if (e.ctrlKey){
18763                     newDate = this.moveYear(this.date, dir);
18764                     newViewDate = this.moveYear(this.viewDate, dir);
18765                 } else if (e.shiftKey){
18766                     newDate = this.moveMonth(this.date, dir);
18767                     newViewDate = this.moveMonth(this.viewDate, dir);
18768                 } else {
18769                     newDate = new Date(this.date);
18770                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18771                     newViewDate = new Date(this.viewDate);
18772                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18773                 }
18774                 if (this.dateWithinRange(newDate)){
18775                     this.date = newDate;
18776                     this.viewDate = newViewDate;
18777                     this.setValue(this.formatDate(this.date));
18778 //                    this.update();
18779                     e.preventDefault();
18780                     dateChanged = true;
18781                 }
18782                 break;
18783             case 38: // up
18784             case 40: // down
18785                 if (!this.keyboardNavigation) {
18786                     break;
18787                 }
18788                 dir = e.keyCode == 38 ? -1 : 1;
18789                 if (e.ctrlKey){
18790                     newDate = this.moveYear(this.date, dir);
18791                     newViewDate = this.moveYear(this.viewDate, dir);
18792                 } else if (e.shiftKey){
18793                     newDate = this.moveMonth(this.date, dir);
18794                     newViewDate = this.moveMonth(this.viewDate, dir);
18795                 } else {
18796                     newDate = new Date(this.date);
18797                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18798                     newViewDate = new Date(this.viewDate);
18799                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18800                 }
18801                 if (this.dateWithinRange(newDate)){
18802                     this.date = newDate;
18803                     this.viewDate = newViewDate;
18804                     this.setValue(this.formatDate(this.date));
18805 //                    this.update();
18806                     e.preventDefault();
18807                     dateChanged = true;
18808                 }
18809                 break;
18810             case 13: // enter
18811                 this.setValue(this.formatDate(this.date));
18812                 this.hide();
18813                 e.preventDefault();
18814                 break;
18815             case 9: // tab
18816                 this.setValue(this.formatDate(this.date));
18817                 this.hide();
18818                 break;
18819             case 16: // shift
18820             case 17: // ctrl
18821             case 18: // alt
18822                 break;
18823             default :
18824                 this.hide();
18825                 
18826         }
18827     },
18828     
18829     
18830     onClick: function(e) 
18831     {
18832         e.stopPropagation();
18833         e.preventDefault();
18834         
18835         var target = e.getTarget();
18836         
18837         if(target.nodeName.toLowerCase() === 'i'){
18838             target = Roo.get(target).dom.parentNode;
18839         }
18840         
18841         var nodeName = target.nodeName;
18842         var className = target.className;
18843         var html = target.innerHTML;
18844         //Roo.log(nodeName);
18845         
18846         switch(nodeName.toLowerCase()) {
18847             case 'th':
18848                 switch(className) {
18849                     case 'switch':
18850                         this.showMode(1);
18851                         break;
18852                     case 'prev':
18853                     case 'next':
18854                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18855                         switch(this.viewMode){
18856                                 case 0:
18857                                         this.viewDate = this.moveMonth(this.viewDate, dir);
18858                                         break;
18859                                 case 1:
18860                                 case 2:
18861                                         this.viewDate = this.moveYear(this.viewDate, dir);
18862                                         break;
18863                         }
18864                         this.fill();
18865                         break;
18866                     case 'today':
18867                         var date = new Date();
18868                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18869 //                        this.fill()
18870                         this.setValue(this.formatDate(this.date));
18871                         
18872                         this.hide();
18873                         break;
18874                 }
18875                 break;
18876             case 'span':
18877                 if (className.indexOf('disabled') < 0) {
18878                     this.viewDate.setUTCDate(1);
18879                     if (className.indexOf('month') > -1) {
18880                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18881                     } else {
18882                         var year = parseInt(html, 10) || 0;
18883                         this.viewDate.setUTCFullYear(year);
18884                         
18885                     }
18886                     
18887                     if(this.singleMode){
18888                         this.setValue(this.formatDate(this.viewDate));
18889                         this.hide();
18890                         return;
18891                     }
18892                     
18893                     this.showMode(-1);
18894                     this.fill();
18895                 }
18896                 break;
18897                 
18898             case 'td':
18899                 //Roo.log(className);
18900                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18901                     var day = parseInt(html, 10) || 1;
18902                     var year = this.viewDate.getUTCFullYear(),
18903                         month = this.viewDate.getUTCMonth();
18904
18905                     if (className.indexOf('old') > -1) {
18906                         if(month === 0 ){
18907                             month = 11;
18908                             year -= 1;
18909                         }else{
18910                             month -= 1;
18911                         }
18912                     } else if (className.indexOf('new') > -1) {
18913                         if (month == 11) {
18914                             month = 0;
18915                             year += 1;
18916                         } else {
18917                             month += 1;
18918                         }
18919                     }
18920                     //Roo.log([year,month,day]);
18921                     this.date = this.UTCDate(year, month, day,0,0,0,0);
18922                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18923 //                    this.fill();
18924                     //Roo.log(this.formatDate(this.date));
18925                     this.setValue(this.formatDate(this.date));
18926                     this.hide();
18927                 }
18928                 break;
18929         }
18930     },
18931     
18932     setStartDate: function(startDate)
18933     {
18934         this.startDate = startDate || -Infinity;
18935         if (this.startDate !== -Infinity) {
18936             this.startDate = this.parseDate(this.startDate);
18937         }
18938         this.update();
18939         this.updateNavArrows();
18940     },
18941
18942     setEndDate: function(endDate)
18943     {
18944         this.endDate = endDate || Infinity;
18945         if (this.endDate !== Infinity) {
18946             this.endDate = this.parseDate(this.endDate);
18947         }
18948         this.update();
18949         this.updateNavArrows();
18950     },
18951     
18952     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18953     {
18954         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18955         if (typeof(this.daysOfWeekDisabled) !== 'object') {
18956             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18957         }
18958         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18959             return parseInt(d, 10);
18960         });
18961         this.update();
18962         this.updateNavArrows();
18963     },
18964     
18965     updateNavArrows: function() 
18966     {
18967         if(this.singleMode){
18968             return;
18969         }
18970         
18971         var d = new Date(this.viewDate),
18972         year = d.getUTCFullYear(),
18973         month = d.getUTCMonth();
18974         
18975         Roo.each(this.picker().select('.prev', true).elements, function(v){
18976             v.show();
18977             switch (this.viewMode) {
18978                 case 0:
18979
18980                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18981                         v.hide();
18982                     }
18983                     break;
18984                 case 1:
18985                 case 2:
18986                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18987                         v.hide();
18988                     }
18989                     break;
18990             }
18991         });
18992         
18993         Roo.each(this.picker().select('.next', true).elements, function(v){
18994             v.show();
18995             switch (this.viewMode) {
18996                 case 0:
18997
18998                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18999                         v.hide();
19000                     }
19001                     break;
19002                 case 1:
19003                 case 2:
19004                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19005                         v.hide();
19006                     }
19007                     break;
19008             }
19009         })
19010     },
19011     
19012     moveMonth: function(date, dir)
19013     {
19014         if (!dir) {
19015             return date;
19016         }
19017         var new_date = new Date(date.valueOf()),
19018         day = new_date.getUTCDate(),
19019         month = new_date.getUTCMonth(),
19020         mag = Math.abs(dir),
19021         new_month, test;
19022         dir = dir > 0 ? 1 : -1;
19023         if (mag == 1){
19024             test = dir == -1
19025             // If going back one month, make sure month is not current month
19026             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19027             ? function(){
19028                 return new_date.getUTCMonth() == month;
19029             }
19030             // If going forward one month, make sure month is as expected
19031             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19032             : function(){
19033                 return new_date.getUTCMonth() != new_month;
19034             };
19035             new_month = month + dir;
19036             new_date.setUTCMonth(new_month);
19037             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19038             if (new_month < 0 || new_month > 11) {
19039                 new_month = (new_month + 12) % 12;
19040             }
19041         } else {
19042             // For magnitudes >1, move one month at a time...
19043             for (var i=0; i<mag; i++) {
19044                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19045                 new_date = this.moveMonth(new_date, dir);
19046             }
19047             // ...then reset the day, keeping it in the new month
19048             new_month = new_date.getUTCMonth();
19049             new_date.setUTCDate(day);
19050             test = function(){
19051                 return new_month != new_date.getUTCMonth();
19052             };
19053         }
19054         // Common date-resetting loop -- if date is beyond end of month, make it
19055         // end of month
19056         while (test()){
19057             new_date.setUTCDate(--day);
19058             new_date.setUTCMonth(new_month);
19059         }
19060         return new_date;
19061     },
19062
19063     moveYear: function(date, dir)
19064     {
19065         return this.moveMonth(date, dir*12);
19066     },
19067
19068     dateWithinRange: function(date)
19069     {
19070         return date >= this.startDate && date <= this.endDate;
19071     },
19072
19073     
19074     remove: function() 
19075     {
19076         this.picker().remove();
19077     },
19078     
19079     validateValue : function(value)
19080     {
19081         if(value.length < 1)  {
19082             if(this.allowBlank){
19083                 return true;
19084             }
19085             return false;
19086         }
19087         
19088         if(value.length < this.minLength){
19089             return false;
19090         }
19091         if(value.length > this.maxLength){
19092             return false;
19093         }
19094         if(this.vtype){
19095             var vt = Roo.form.VTypes;
19096             if(!vt[this.vtype](value, this)){
19097                 return false;
19098             }
19099         }
19100         if(typeof this.validator == "function"){
19101             var msg = this.validator(value);
19102             if(msg !== true){
19103                 return false;
19104             }
19105         }
19106         
19107         if(this.regex && !this.regex.test(value)){
19108             return false;
19109         }
19110         
19111         if(typeof(this.parseDate(value)) == 'undefined'){
19112             return false;
19113         }
19114         
19115         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19116             return false;
19117         }      
19118         
19119         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19120             return false;
19121         } 
19122         
19123         
19124         return true;
19125     }
19126    
19127 });
19128
19129 Roo.apply(Roo.bootstrap.DateField,  {
19130     
19131     head : {
19132         tag: 'thead',
19133         cn: [
19134         {
19135             tag: 'tr',
19136             cn: [
19137             {
19138                 tag: 'th',
19139                 cls: 'prev',
19140                 html: '<i class="fa fa-arrow-left"/>'
19141             },
19142             {
19143                 tag: 'th',
19144                 cls: 'switch',
19145                 colspan: '5'
19146             },
19147             {
19148                 tag: 'th',
19149                 cls: 'next',
19150                 html: '<i class="fa fa-arrow-right"/>'
19151             }
19152
19153             ]
19154         }
19155         ]
19156     },
19157     
19158     content : {
19159         tag: 'tbody',
19160         cn: [
19161         {
19162             tag: 'tr',
19163             cn: [
19164             {
19165                 tag: 'td',
19166                 colspan: '7'
19167             }
19168             ]
19169         }
19170         ]
19171     },
19172     
19173     footer : {
19174         tag: 'tfoot',
19175         cn: [
19176         {
19177             tag: 'tr',
19178             cn: [
19179             {
19180                 tag: 'th',
19181                 colspan: '7',
19182                 cls: 'today'
19183             }
19184                     
19185             ]
19186         }
19187         ]
19188     },
19189     
19190     dates:{
19191         en: {
19192             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19193             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19194             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19195             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19196             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19197             today: "Today"
19198         }
19199     },
19200     
19201     modes: [
19202     {
19203         clsName: 'days',
19204         navFnc: 'Month',
19205         navStep: 1
19206     },
19207     {
19208         clsName: 'months',
19209         navFnc: 'FullYear',
19210         navStep: 1
19211     },
19212     {
19213         clsName: 'years',
19214         navFnc: 'FullYear',
19215         navStep: 10
19216     }]
19217 });
19218
19219 Roo.apply(Roo.bootstrap.DateField,  {
19220   
19221     template : {
19222         tag: 'div',
19223         cls: 'datepicker dropdown-menu roo-dynamic',
19224         cn: [
19225         {
19226             tag: 'div',
19227             cls: 'datepicker-days',
19228             cn: [
19229             {
19230                 tag: 'table',
19231                 cls: 'table-condensed',
19232                 cn:[
19233                 Roo.bootstrap.DateField.head,
19234                 {
19235                     tag: 'tbody'
19236                 },
19237                 Roo.bootstrap.DateField.footer
19238                 ]
19239             }
19240             ]
19241         },
19242         {
19243             tag: 'div',
19244             cls: 'datepicker-months',
19245             cn: [
19246             {
19247                 tag: 'table',
19248                 cls: 'table-condensed',
19249                 cn:[
19250                 Roo.bootstrap.DateField.head,
19251                 Roo.bootstrap.DateField.content,
19252                 Roo.bootstrap.DateField.footer
19253                 ]
19254             }
19255             ]
19256         },
19257         {
19258             tag: 'div',
19259             cls: 'datepicker-years',
19260             cn: [
19261             {
19262                 tag: 'table',
19263                 cls: 'table-condensed',
19264                 cn:[
19265                 Roo.bootstrap.DateField.head,
19266                 Roo.bootstrap.DateField.content,
19267                 Roo.bootstrap.DateField.footer
19268                 ]
19269             }
19270             ]
19271         }
19272         ]
19273     }
19274 });
19275
19276  
19277
19278  /*
19279  * - LGPL
19280  *
19281  * TimeField
19282  * 
19283  */
19284
19285 /**
19286  * @class Roo.bootstrap.TimeField
19287  * @extends Roo.bootstrap.Input
19288  * Bootstrap DateField class
19289  * 
19290  * 
19291  * @constructor
19292  * Create a new TimeField
19293  * @param {Object} config The config object
19294  */
19295
19296 Roo.bootstrap.TimeField = function(config){
19297     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19298     this.addEvents({
19299             /**
19300              * @event show
19301              * Fires when this field show.
19302              * @param {Roo.bootstrap.DateField} thisthis
19303              * @param {Mixed} date The date value
19304              */
19305             show : true,
19306             /**
19307              * @event show
19308              * Fires when this field hide.
19309              * @param {Roo.bootstrap.DateField} this
19310              * @param {Mixed} date The date value
19311              */
19312             hide : true,
19313             /**
19314              * @event select
19315              * Fires when select a date.
19316              * @param {Roo.bootstrap.DateField} this
19317              * @param {Mixed} date The date value
19318              */
19319             select : true
19320         });
19321 };
19322
19323 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19324     
19325     /**
19326      * @cfg {String} format
19327      * The default time format string which can be overriden for localization support.  The format must be
19328      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19329      */
19330     format : "H:i",
19331        
19332     onRender: function(ct, position)
19333     {
19334         
19335         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19336                 
19337         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19338         
19339         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19340         
19341         this.pop = this.picker().select('>.datepicker-time',true).first();
19342         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19343         
19344         this.picker().on('mousedown', this.onMousedown, this);
19345         this.picker().on('click', this.onClick, this);
19346         
19347         this.picker().addClass('datepicker-dropdown');
19348     
19349         this.fillTime();
19350         this.update();
19351             
19352         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19353         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19354         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19355         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19356         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19357         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19358
19359     },
19360     
19361     fireKey: function(e){
19362         if (!this.picker().isVisible()){
19363             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19364                 this.show();
19365             }
19366             return;
19367         }
19368
19369         e.preventDefault();
19370         
19371         switch(e.keyCode){
19372             case 27: // escape
19373                 this.hide();
19374                 break;
19375             case 37: // left
19376             case 39: // right
19377                 this.onTogglePeriod();
19378                 break;
19379             case 38: // up
19380                 this.onIncrementMinutes();
19381                 break;
19382             case 40: // down
19383                 this.onDecrementMinutes();
19384                 break;
19385             case 13: // enter
19386             case 9: // tab
19387                 this.setTime();
19388                 break;
19389         }
19390     },
19391     
19392     onClick: function(e) {
19393         e.stopPropagation();
19394         e.preventDefault();
19395     },
19396     
19397     picker : function()
19398     {
19399         return this.el.select('.datepicker', true).first();
19400     },
19401     
19402     fillTime: function()
19403     {    
19404         var time = this.pop.select('tbody', true).first();
19405         
19406         time.dom.innerHTML = '';
19407         
19408         time.createChild({
19409             tag: 'tr',
19410             cn: [
19411                 {
19412                     tag: 'td',
19413                     cn: [
19414                         {
19415                             tag: 'a',
19416                             href: '#',
19417                             cls: 'btn',
19418                             cn: [
19419                                 {
19420                                     tag: 'span',
19421                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19422                                 }
19423                             ]
19424                         } 
19425                     ]
19426                 },
19427                 {
19428                     tag: 'td',
19429                     cls: 'separator'
19430                 },
19431                 {
19432                     tag: 'td',
19433                     cn: [
19434                         {
19435                             tag: 'a',
19436                             href: '#',
19437                             cls: 'btn',
19438                             cn: [
19439                                 {
19440                                     tag: 'span',
19441                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19442                                 }
19443                             ]
19444                         }
19445                     ]
19446                 },
19447                 {
19448                     tag: 'td',
19449                     cls: 'separator'
19450                 }
19451             ]
19452         });
19453         
19454         time.createChild({
19455             tag: 'tr',
19456             cn: [
19457                 {
19458                     tag: 'td',
19459                     cn: [
19460                         {
19461                             tag: 'span',
19462                             cls: 'timepicker-hour',
19463                             html: '00'
19464                         }  
19465                     ]
19466                 },
19467                 {
19468                     tag: 'td',
19469                     cls: 'separator',
19470                     html: ':'
19471                 },
19472                 {
19473                     tag: 'td',
19474                     cn: [
19475                         {
19476                             tag: 'span',
19477                             cls: 'timepicker-minute',
19478                             html: '00'
19479                         }  
19480                     ]
19481                 },
19482                 {
19483                     tag: 'td',
19484                     cls: 'separator'
19485                 },
19486                 {
19487                     tag: 'td',
19488                     cn: [
19489                         {
19490                             tag: 'button',
19491                             type: 'button',
19492                             cls: 'btn btn-primary period',
19493                             html: 'AM'
19494                             
19495                         }
19496                     ]
19497                 }
19498             ]
19499         });
19500         
19501         time.createChild({
19502             tag: 'tr',
19503             cn: [
19504                 {
19505                     tag: 'td',
19506                     cn: [
19507                         {
19508                             tag: 'a',
19509                             href: '#',
19510                             cls: 'btn',
19511                             cn: [
19512                                 {
19513                                     tag: 'span',
19514                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19515                                 }
19516                             ]
19517                         }
19518                     ]
19519                 },
19520                 {
19521                     tag: 'td',
19522                     cls: 'separator'
19523                 },
19524                 {
19525                     tag: 'td',
19526                     cn: [
19527                         {
19528                             tag: 'a',
19529                             href: '#',
19530                             cls: 'btn',
19531                             cn: [
19532                                 {
19533                                     tag: 'span',
19534                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19535                                 }
19536                             ]
19537                         }
19538                     ]
19539                 },
19540                 {
19541                     tag: 'td',
19542                     cls: 'separator'
19543                 }
19544             ]
19545         });
19546         
19547     },
19548     
19549     update: function()
19550     {
19551         
19552         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19553         
19554         this.fill();
19555     },
19556     
19557     fill: function() 
19558     {
19559         var hours = this.time.getHours();
19560         var minutes = this.time.getMinutes();
19561         var period = 'AM';
19562         
19563         if(hours > 11){
19564             period = 'PM';
19565         }
19566         
19567         if(hours == 0){
19568             hours = 12;
19569         }
19570         
19571         
19572         if(hours > 12){
19573             hours = hours - 12;
19574         }
19575         
19576         if(hours < 10){
19577             hours = '0' + hours;
19578         }
19579         
19580         if(minutes < 10){
19581             minutes = '0' + minutes;
19582         }
19583         
19584         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19585         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19586         this.pop.select('button', true).first().dom.innerHTML = period;
19587         
19588     },
19589     
19590     place: function()
19591     {   
19592         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19593         
19594         var cls = ['bottom'];
19595         
19596         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19597             cls.pop();
19598             cls.push('top');
19599         }
19600         
19601         cls.push('right');
19602         
19603         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19604             cls.pop();
19605             cls.push('left');
19606         }
19607         
19608         this.picker().addClass(cls.join('-'));
19609         
19610         var _this = this;
19611         
19612         Roo.each(cls, function(c){
19613             if(c == 'bottom'){
19614                 _this.picker().setTop(_this.inputEl().getHeight());
19615                 return;
19616             }
19617             if(c == 'top'){
19618                 _this.picker().setTop(0 - _this.picker().getHeight());
19619                 return;
19620             }
19621             
19622             if(c == 'left'){
19623                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19624                 return;
19625             }
19626             if(c == 'right'){
19627                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19628                 return;
19629             }
19630         });
19631         
19632     },
19633   
19634     onFocus : function()
19635     {
19636         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19637         this.show();
19638     },
19639     
19640     onBlur : function()
19641     {
19642         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19643         this.hide();
19644     },
19645     
19646     show : function()
19647     {
19648         this.picker().show();
19649         this.pop.show();
19650         this.update();
19651         this.place();
19652         
19653         this.fireEvent('show', this, this.date);
19654     },
19655     
19656     hide : function()
19657     {
19658         this.picker().hide();
19659         this.pop.hide();
19660         
19661         this.fireEvent('hide', this, this.date);
19662     },
19663     
19664     setTime : function()
19665     {
19666         this.hide();
19667         this.setValue(this.time.format(this.format));
19668         
19669         this.fireEvent('select', this, this.date);
19670         
19671         
19672     },
19673     
19674     onMousedown: function(e){
19675         e.stopPropagation();
19676         e.preventDefault();
19677     },
19678     
19679     onIncrementHours: function()
19680     {
19681         Roo.log('onIncrementHours');
19682         this.time = this.time.add(Date.HOUR, 1);
19683         this.update();
19684         
19685     },
19686     
19687     onDecrementHours: function()
19688     {
19689         Roo.log('onDecrementHours');
19690         this.time = this.time.add(Date.HOUR, -1);
19691         this.update();
19692     },
19693     
19694     onIncrementMinutes: function()
19695     {
19696         Roo.log('onIncrementMinutes');
19697         this.time = this.time.add(Date.MINUTE, 1);
19698         this.update();
19699     },
19700     
19701     onDecrementMinutes: function()
19702     {
19703         Roo.log('onDecrementMinutes');
19704         this.time = this.time.add(Date.MINUTE, -1);
19705         this.update();
19706     },
19707     
19708     onTogglePeriod: function()
19709     {
19710         Roo.log('onTogglePeriod');
19711         this.time = this.time.add(Date.HOUR, 12);
19712         this.update();
19713     }
19714     
19715    
19716 });
19717
19718 Roo.apply(Roo.bootstrap.TimeField,  {
19719     
19720     content : {
19721         tag: 'tbody',
19722         cn: [
19723             {
19724                 tag: 'tr',
19725                 cn: [
19726                 {
19727                     tag: 'td',
19728                     colspan: '7'
19729                 }
19730                 ]
19731             }
19732         ]
19733     },
19734     
19735     footer : {
19736         tag: 'tfoot',
19737         cn: [
19738             {
19739                 tag: 'tr',
19740                 cn: [
19741                 {
19742                     tag: 'th',
19743                     colspan: '7',
19744                     cls: '',
19745                     cn: [
19746                         {
19747                             tag: 'button',
19748                             cls: 'btn btn-info ok',
19749                             html: 'OK'
19750                         }
19751                     ]
19752                 }
19753
19754                 ]
19755             }
19756         ]
19757     }
19758 });
19759
19760 Roo.apply(Roo.bootstrap.TimeField,  {
19761   
19762     template : {
19763         tag: 'div',
19764         cls: 'datepicker dropdown-menu',
19765         cn: [
19766             {
19767                 tag: 'div',
19768                 cls: 'datepicker-time',
19769                 cn: [
19770                 {
19771                     tag: 'table',
19772                     cls: 'table-condensed',
19773                     cn:[
19774                     Roo.bootstrap.TimeField.content,
19775                     Roo.bootstrap.TimeField.footer
19776                     ]
19777                 }
19778                 ]
19779             }
19780         ]
19781     }
19782 });
19783
19784  
19785
19786  /*
19787  * - LGPL
19788  *
19789  * MonthField
19790  * 
19791  */
19792
19793 /**
19794  * @class Roo.bootstrap.MonthField
19795  * @extends Roo.bootstrap.Input
19796  * Bootstrap MonthField class
19797  * 
19798  * @cfg {String} language default en
19799  * 
19800  * @constructor
19801  * Create a new MonthField
19802  * @param {Object} config The config object
19803  */
19804
19805 Roo.bootstrap.MonthField = function(config){
19806     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19807     
19808     this.addEvents({
19809         /**
19810          * @event show
19811          * Fires when this field show.
19812          * @param {Roo.bootstrap.MonthField} this
19813          * @param {Mixed} date The date value
19814          */
19815         show : true,
19816         /**
19817          * @event show
19818          * Fires when this field hide.
19819          * @param {Roo.bootstrap.MonthField} this
19820          * @param {Mixed} date The date value
19821          */
19822         hide : true,
19823         /**
19824          * @event select
19825          * Fires when select a date.
19826          * @param {Roo.bootstrap.MonthField} this
19827          * @param {String} oldvalue The old value
19828          * @param {String} newvalue The new value
19829          */
19830         select : true
19831     });
19832 };
19833
19834 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
19835     
19836     onRender: function(ct, position)
19837     {
19838         
19839         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19840         
19841         this.language = this.language || 'en';
19842         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19843         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19844         
19845         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19846         this.isInline = false;
19847         this.isInput = true;
19848         this.component = this.el.select('.add-on', true).first() || false;
19849         this.component = (this.component && this.component.length === 0) ? false : this.component;
19850         this.hasInput = this.component && this.inputEL().length;
19851         
19852         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19853         
19854         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19855         
19856         this.picker().on('mousedown', this.onMousedown, this);
19857         this.picker().on('click', this.onClick, this);
19858         
19859         this.picker().addClass('datepicker-dropdown');
19860         
19861         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19862             v.setStyle('width', '189px');
19863         });
19864         
19865         this.fillMonths();
19866         
19867         this.update();
19868         
19869         if(this.isInline) {
19870             this.show();
19871         }
19872         
19873     },
19874     
19875     setValue: function(v, suppressEvent)
19876     {   
19877         var o = this.getValue();
19878         
19879         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19880         
19881         this.update();
19882
19883         if(suppressEvent !== true){
19884             this.fireEvent('select', this, o, v);
19885         }
19886         
19887     },
19888     
19889     getValue: function()
19890     {
19891         return this.value;
19892     },
19893     
19894     onClick: function(e) 
19895     {
19896         e.stopPropagation();
19897         e.preventDefault();
19898         
19899         var target = e.getTarget();
19900         
19901         if(target.nodeName.toLowerCase() === 'i'){
19902             target = Roo.get(target).dom.parentNode;
19903         }
19904         
19905         var nodeName = target.nodeName;
19906         var className = target.className;
19907         var html = target.innerHTML;
19908         
19909         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19910             return;
19911         }
19912         
19913         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19914         
19915         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19916         
19917         this.hide();
19918                         
19919     },
19920     
19921     picker : function()
19922     {
19923         return this.pickerEl;
19924     },
19925     
19926     fillMonths: function()
19927     {    
19928         var i = 0;
19929         var months = this.picker().select('>.datepicker-months td', true).first();
19930         
19931         months.dom.innerHTML = '';
19932         
19933         while (i < 12) {
19934             var month = {
19935                 tag: 'span',
19936                 cls: 'month',
19937                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19938             };
19939             
19940             months.createChild(month);
19941         }
19942         
19943     },
19944     
19945     update: function()
19946     {
19947         var _this = this;
19948         
19949         if(typeof(this.vIndex) == 'undefined' && this.value.length){
19950             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19951         }
19952         
19953         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19954             e.removeClass('active');
19955             
19956             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19957                 e.addClass('active');
19958             }
19959         })
19960     },
19961     
19962     place: function()
19963     {
19964         if(this.isInline) {
19965             return;
19966         }
19967         
19968         this.picker().removeClass(['bottom', 'top']);
19969         
19970         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19971             /*
19972              * place to the top of element!
19973              *
19974              */
19975             
19976             this.picker().addClass('top');
19977             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19978             
19979             return;
19980         }
19981         
19982         this.picker().addClass('bottom');
19983         
19984         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19985     },
19986     
19987     onFocus : function()
19988     {
19989         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19990         this.show();
19991     },
19992     
19993     onBlur : function()
19994     {
19995         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19996         
19997         var d = this.inputEl().getValue();
19998         
19999         this.setValue(d);
20000                 
20001         this.hide();
20002     },
20003     
20004     show : function()
20005     {
20006         this.picker().show();
20007         this.picker().select('>.datepicker-months', true).first().show();
20008         this.update();
20009         this.place();
20010         
20011         this.fireEvent('show', this, this.date);
20012     },
20013     
20014     hide : function()
20015     {
20016         if(this.isInline) {
20017             return;
20018         }
20019         this.picker().hide();
20020         this.fireEvent('hide', this, this.date);
20021         
20022     },
20023     
20024     onMousedown: function(e)
20025     {
20026         e.stopPropagation();
20027         e.preventDefault();
20028     },
20029     
20030     keyup: function(e)
20031     {
20032         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20033         this.update();
20034     },
20035
20036     fireKey: function(e)
20037     {
20038         if (!this.picker().isVisible()){
20039             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20040                 this.show();
20041             }
20042             return;
20043         }
20044         
20045         var dir;
20046         
20047         switch(e.keyCode){
20048             case 27: // escape
20049                 this.hide();
20050                 e.preventDefault();
20051                 break;
20052             case 37: // left
20053             case 39: // right
20054                 dir = e.keyCode == 37 ? -1 : 1;
20055                 
20056                 this.vIndex = this.vIndex + dir;
20057                 
20058                 if(this.vIndex < 0){
20059                     this.vIndex = 0;
20060                 }
20061                 
20062                 if(this.vIndex > 11){
20063                     this.vIndex = 11;
20064                 }
20065                 
20066                 if(isNaN(this.vIndex)){
20067                     this.vIndex = 0;
20068                 }
20069                 
20070                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20071                 
20072                 break;
20073             case 38: // up
20074             case 40: // down
20075                 
20076                 dir = e.keyCode == 38 ? -1 : 1;
20077                 
20078                 this.vIndex = this.vIndex + dir * 4;
20079                 
20080                 if(this.vIndex < 0){
20081                     this.vIndex = 0;
20082                 }
20083                 
20084                 if(this.vIndex > 11){
20085                     this.vIndex = 11;
20086                 }
20087                 
20088                 if(isNaN(this.vIndex)){
20089                     this.vIndex = 0;
20090                 }
20091                 
20092                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20093                 break;
20094                 
20095             case 13: // enter
20096                 
20097                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20098                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20099                 }
20100                 
20101                 this.hide();
20102                 e.preventDefault();
20103                 break;
20104             case 9: // tab
20105                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20106                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20107                 }
20108                 this.hide();
20109                 break;
20110             case 16: // shift
20111             case 17: // ctrl
20112             case 18: // alt
20113                 break;
20114             default :
20115                 this.hide();
20116                 
20117         }
20118     },
20119     
20120     remove: function() 
20121     {
20122         this.picker().remove();
20123     }
20124    
20125 });
20126
20127 Roo.apply(Roo.bootstrap.MonthField,  {
20128     
20129     content : {
20130         tag: 'tbody',
20131         cn: [
20132         {
20133             tag: 'tr',
20134             cn: [
20135             {
20136                 tag: 'td',
20137                 colspan: '7'
20138             }
20139             ]
20140         }
20141         ]
20142     },
20143     
20144     dates:{
20145         en: {
20146             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20147             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20148         }
20149     }
20150 });
20151
20152 Roo.apply(Roo.bootstrap.MonthField,  {
20153   
20154     template : {
20155         tag: 'div',
20156         cls: 'datepicker dropdown-menu roo-dynamic',
20157         cn: [
20158             {
20159                 tag: 'div',
20160                 cls: 'datepicker-months',
20161                 cn: [
20162                 {
20163                     tag: 'table',
20164                     cls: 'table-condensed',
20165                     cn:[
20166                         Roo.bootstrap.DateField.content
20167                     ]
20168                 }
20169                 ]
20170             }
20171         ]
20172     }
20173 });
20174
20175  
20176
20177  
20178  /*
20179  * - LGPL
20180  *
20181  * CheckBox
20182  * 
20183  */
20184
20185 /**
20186  * @class Roo.bootstrap.CheckBox
20187  * @extends Roo.bootstrap.Input
20188  * Bootstrap CheckBox class
20189  * 
20190  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20191  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20192  * @cfg {String} boxLabel The text that appears beside the checkbox
20193  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20194  * @cfg {Boolean} checked initnal the element
20195  * @cfg {Boolean} inline inline the element (default false)
20196  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20197  * @cfg {String} tooltip label tooltip
20198  * 
20199  * @constructor
20200  * Create a new CheckBox
20201  * @param {Object} config The config object
20202  */
20203
20204 Roo.bootstrap.CheckBox = function(config){
20205     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20206    
20207     this.addEvents({
20208         /**
20209         * @event check
20210         * Fires when the element is checked or unchecked.
20211         * @param {Roo.bootstrap.CheckBox} this This input
20212         * @param {Boolean} checked The new checked value
20213         */
20214        check : true,
20215        /**
20216         * @event click
20217         * Fires when the element is click.
20218         * @param {Roo.bootstrap.CheckBox} this This input
20219         */
20220        click : true
20221     });
20222     
20223 };
20224
20225 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20226   
20227     inputType: 'checkbox',
20228     inputValue: 1,
20229     valueOff: 0,
20230     boxLabel: false,
20231     checked: false,
20232     weight : false,
20233     inline: false,
20234     tooltip : '',
20235     
20236     getAutoCreate : function()
20237     {
20238         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20239         
20240         var id = Roo.id();
20241         
20242         var cfg = {};
20243         
20244         cfg.cls = 'form-group ' + this.inputType; //input-group
20245         
20246         if(this.inline){
20247             cfg.cls += ' ' + this.inputType + '-inline';
20248         }
20249         
20250         var input =  {
20251             tag: 'input',
20252             id : id,
20253             type : this.inputType,
20254             value : this.inputValue,
20255             cls : 'roo-' + this.inputType, //'form-box',
20256             placeholder : this.placeholder || ''
20257             
20258         };
20259         
20260         if(this.inputType != 'radio'){
20261             var hidden =  {
20262                 tag: 'input',
20263                 type : 'hidden',
20264                 cls : 'roo-hidden-value',
20265                 value : this.checked ? this.inputValue : this.valueOff
20266             };
20267         }
20268         
20269             
20270         if (this.weight) { // Validity check?
20271             cfg.cls += " " + this.inputType + "-" + this.weight;
20272         }
20273         
20274         if (this.disabled) {
20275             input.disabled=true;
20276         }
20277         
20278         if(this.checked){
20279             input.checked = this.checked;
20280         }
20281         
20282         if (this.name) {
20283             
20284             input.name = this.name;
20285             
20286             if(this.inputType != 'radio'){
20287                 hidden.name = this.name;
20288                 input.name = '_hidden_' + this.name;
20289             }
20290         }
20291         
20292         if (this.size) {
20293             input.cls += ' input-' + this.size;
20294         }
20295         
20296         var settings=this;
20297         
20298         ['xs','sm','md','lg'].map(function(size){
20299             if (settings[size]) {
20300                 cfg.cls += ' col-' + size + '-' + settings[size];
20301             }
20302         });
20303         
20304         var inputblock = input;
20305          
20306         if (this.before || this.after) {
20307             
20308             inputblock = {
20309                 cls : 'input-group',
20310                 cn :  [] 
20311             };
20312             
20313             if (this.before) {
20314                 inputblock.cn.push({
20315                     tag :'span',
20316                     cls : 'input-group-addon',
20317                     html : this.before
20318                 });
20319             }
20320             
20321             inputblock.cn.push(input);
20322             
20323             if(this.inputType != 'radio'){
20324                 inputblock.cn.push(hidden);
20325             }
20326             
20327             if (this.after) {
20328                 inputblock.cn.push({
20329                     tag :'span',
20330                     cls : 'input-group-addon',
20331                     html : this.after
20332                 });
20333             }
20334             
20335         }
20336         
20337         if (align ==='left' && this.fieldLabel.length) {
20338 //                Roo.log("left and has label");
20339             cfg.cn = [
20340                 {
20341                     tag: 'label',
20342                     'for' :  id,
20343                     cls : 'control-label',
20344                     html : this.fieldLabel
20345                 },
20346                 {
20347                     cls : "", 
20348                     cn: [
20349                         inputblock
20350                     ]
20351                 }
20352             ];
20353             
20354             if(this.labelWidth > 12){
20355                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20356             }
20357             
20358             if(this.labelWidth < 13 && this.labelmd == 0){
20359                 this.labelmd = this.labelWidth;
20360             }
20361             
20362             if(this.labellg > 0){
20363                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20364                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20365             }
20366             
20367             if(this.labelmd > 0){
20368                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20369                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20370             }
20371             
20372             if(this.labelsm > 0){
20373                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20374                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20375             }
20376             
20377             if(this.labelxs > 0){
20378                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20379                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20380             }
20381             
20382         } else if ( this.fieldLabel.length) {
20383 //                Roo.log(" label");
20384                 cfg.cn = [
20385                    
20386                     {
20387                         tag: this.boxLabel ? 'span' : 'label',
20388                         'for': id,
20389                         cls: 'control-label box-input-label',
20390                         //cls : 'input-group-addon',
20391                         html : this.fieldLabel
20392                     },
20393                     
20394                     inputblock
20395                     
20396                 ];
20397
20398         } else {
20399             
20400 //                Roo.log(" no label && no align");
20401                 cfg.cn = [  inputblock ] ;
20402                 
20403                 
20404         }
20405         
20406         if(this.boxLabel){
20407              var boxLabelCfg = {
20408                 tag: 'label',
20409                 //'for': id, // box label is handled by onclick - so no for...
20410                 cls: 'box-label',
20411                 html: this.boxLabel
20412             };
20413             
20414             if(this.tooltip){
20415                 boxLabelCfg.tooltip = this.tooltip;
20416             }
20417              
20418             cfg.cn.push(boxLabelCfg);
20419         }
20420         
20421         if(this.inputType != 'radio'){
20422             cfg.cn.push(hidden);
20423         }
20424         
20425         return cfg;
20426         
20427     },
20428     
20429     /**
20430      * return the real input element.
20431      */
20432     inputEl: function ()
20433     {
20434         return this.el.select('input.roo-' + this.inputType,true).first();
20435     },
20436     hiddenEl: function ()
20437     {
20438         return this.el.select('input.roo-hidden-value',true).first();
20439     },
20440     
20441     labelEl: function()
20442     {
20443         return this.el.select('label.control-label',true).first();
20444     },
20445     /* depricated... */
20446     
20447     label: function()
20448     {
20449         return this.labelEl();
20450     },
20451     
20452     boxLabelEl: function()
20453     {
20454         return this.el.select('label.box-label',true).first();
20455     },
20456     
20457     initEvents : function()
20458     {
20459 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20460         
20461         this.inputEl().on('click', this.onClick,  this);
20462         
20463         if (this.boxLabel) { 
20464             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20465         }
20466         
20467         this.startValue = this.getValue();
20468         
20469         if(this.groupId){
20470             Roo.bootstrap.CheckBox.register(this);
20471         }
20472     },
20473     
20474     onClick : function(e)
20475     {   
20476         if(this.fireEvent('click', this, e) !== false){
20477             this.setChecked(!this.checked);
20478         }
20479         
20480     },
20481     
20482     setChecked : function(state,suppressEvent)
20483     {
20484         this.startValue = this.getValue();
20485
20486         if(this.inputType == 'radio'){
20487             
20488             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20489                 e.dom.checked = false;
20490             });
20491             
20492             this.inputEl().dom.checked = true;
20493             
20494             this.inputEl().dom.value = this.inputValue;
20495             
20496             if(suppressEvent !== true){
20497                 this.fireEvent('check', this, true);
20498             }
20499             
20500             this.validate();
20501             
20502             return;
20503         }
20504         
20505         this.checked = state;
20506         
20507         this.inputEl().dom.checked = state;
20508         
20509         
20510         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20511         
20512         if(suppressEvent !== true){
20513             this.fireEvent('check', this, state);
20514         }
20515         
20516         this.validate();
20517     },
20518     
20519     getValue : function()
20520     {
20521         if(this.inputType == 'radio'){
20522             return this.getGroupValue();
20523         }
20524         
20525         return this.hiddenEl().dom.value;
20526         
20527     },
20528     
20529     getGroupValue : function()
20530     {
20531         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20532             return '';
20533         }
20534         
20535         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20536     },
20537     
20538     setValue : function(v,suppressEvent)
20539     {
20540         if(this.inputType == 'radio'){
20541             this.setGroupValue(v, suppressEvent);
20542             return;
20543         }
20544         
20545         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20546         
20547         this.validate();
20548     },
20549     
20550     setGroupValue : function(v, suppressEvent)
20551     {
20552         this.startValue = this.getValue();
20553         
20554         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20555             e.dom.checked = false;
20556             
20557             if(e.dom.value == v){
20558                 e.dom.checked = true;
20559             }
20560         });
20561         
20562         if(suppressEvent !== true){
20563             this.fireEvent('check', this, true);
20564         }
20565
20566         this.validate();
20567         
20568         return;
20569     },
20570     
20571     validate : function()
20572     {
20573         if(
20574                 this.disabled || 
20575                 (this.inputType == 'radio' && this.validateRadio()) ||
20576                 (this.inputType == 'checkbox' && this.validateCheckbox())
20577         ){
20578             this.markValid();
20579             return true;
20580         }
20581         
20582         this.markInvalid();
20583         return false;
20584     },
20585     
20586     validateRadio : function()
20587     {
20588         if(this.allowBlank){
20589             return true;
20590         }
20591         
20592         var valid = false;
20593         
20594         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20595             if(!e.dom.checked){
20596                 return;
20597             }
20598             
20599             valid = true;
20600             
20601             return false;
20602         });
20603         
20604         return valid;
20605     },
20606     
20607     validateCheckbox : function()
20608     {
20609         if(!this.groupId){
20610             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20611             //return (this.getValue() == this.inputValue) ? true : false;
20612         }
20613         
20614         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20615         
20616         if(!group){
20617             return false;
20618         }
20619         
20620         var r = false;
20621         
20622         for(var i in group){
20623             if(group[i].el.isVisible(true)){
20624                 r = false;
20625                 break;
20626             }
20627             
20628             r = true;
20629         }
20630         
20631         for(var i in group){
20632             if(r){
20633                 break;
20634             }
20635             
20636             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20637         }
20638         
20639         return r;
20640     },
20641     
20642     /**
20643      * Mark this field as valid
20644      */
20645     markValid : function()
20646     {
20647         var _this = this;
20648         
20649         this.fireEvent('valid', this);
20650         
20651         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20652         
20653         if(this.groupId){
20654             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20655         }
20656         
20657         if(label){
20658             label.markValid();
20659         }
20660
20661         if(this.inputType == 'radio'){
20662             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20663                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20664                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20665             });
20666             
20667             return;
20668         }
20669
20670         if(!this.groupId){
20671             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20672             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20673             return;
20674         }
20675         
20676         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20677         
20678         if(!group){
20679             return;
20680         }
20681         
20682         for(var i in group){
20683             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20684             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20685         }
20686     },
20687     
20688      /**
20689      * Mark this field as invalid
20690      * @param {String} msg The validation message
20691      */
20692     markInvalid : function(msg)
20693     {
20694         if(this.allowBlank){
20695             return;
20696         }
20697         
20698         var _this = this;
20699         
20700         this.fireEvent('invalid', this, msg);
20701         
20702         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20703         
20704         if(this.groupId){
20705             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20706         }
20707         
20708         if(label){
20709             label.markInvalid();
20710         }
20711             
20712         if(this.inputType == 'radio'){
20713             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20714                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20715                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20716             });
20717             
20718             return;
20719         }
20720         
20721         if(!this.groupId){
20722             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20723             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20724             return;
20725         }
20726         
20727         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20728         
20729         if(!group){
20730             return;
20731         }
20732         
20733         for(var i in group){
20734             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20735             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20736         }
20737         
20738     },
20739     
20740     clearInvalid : function()
20741     {
20742         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20743         
20744         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20745         
20746         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20747         
20748         if (label && label.iconEl) {
20749             label.iconEl.removeClass(label.validClass);
20750             label.iconEl.removeClass(label.invalidClass);
20751         }
20752     },
20753     
20754     disable : function()
20755     {
20756         if(this.inputType != 'radio'){
20757             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20758             return;
20759         }
20760         
20761         var _this = this;
20762         
20763         if(this.rendered){
20764             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20765                 _this.getActionEl().addClass(this.disabledClass);
20766                 e.dom.disabled = true;
20767             });
20768         }
20769         
20770         this.disabled = true;
20771         this.fireEvent("disable", this);
20772         return this;
20773     },
20774
20775     enable : function()
20776     {
20777         if(this.inputType != 'radio'){
20778             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20779             return;
20780         }
20781         
20782         var _this = this;
20783         
20784         if(this.rendered){
20785             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20786                 _this.getActionEl().removeClass(this.disabledClass);
20787                 e.dom.disabled = false;
20788             });
20789         }
20790         
20791         this.disabled = false;
20792         this.fireEvent("enable", this);
20793         return this;
20794     },
20795     
20796     setBoxLabel : function(v)
20797     {
20798         this.boxLabel = v;
20799         
20800         if(this.rendered){
20801             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
20802         }
20803     }
20804
20805 });
20806
20807 Roo.apply(Roo.bootstrap.CheckBox, {
20808     
20809     groups: {},
20810     
20811      /**
20812     * register a CheckBox Group
20813     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20814     */
20815     register : function(checkbox)
20816     {
20817         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20818             this.groups[checkbox.groupId] = {};
20819         }
20820         
20821         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20822             return;
20823         }
20824         
20825         this.groups[checkbox.groupId][checkbox.name] = checkbox;
20826         
20827     },
20828     /**
20829     * fetch a CheckBox Group based on the group ID
20830     * @param {string} the group ID
20831     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20832     */
20833     get: function(groupId) {
20834         if (typeof(this.groups[groupId]) == 'undefined') {
20835             return false;
20836         }
20837         
20838         return this.groups[groupId] ;
20839     }
20840     
20841     
20842 });
20843 /*
20844  * - LGPL
20845  *
20846  * RadioItem
20847  * 
20848  */
20849
20850 /**
20851  * @class Roo.bootstrap.Radio
20852  * @extends Roo.bootstrap.Component
20853  * Bootstrap Radio class
20854  * @cfg {String} boxLabel - the label associated
20855  * @cfg {String} value - the value of radio
20856  * 
20857  * @constructor
20858  * Create a new Radio
20859  * @param {Object} config The config object
20860  */
20861 Roo.bootstrap.Radio = function(config){
20862     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20863     
20864 };
20865
20866 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
20867     
20868     boxLabel : '',
20869     
20870     value : '',
20871     
20872     getAutoCreate : function()
20873     {
20874         var cfg = {
20875             tag : 'div',
20876             cls : 'form-group radio',
20877             cn : [
20878                 {
20879                     tag : 'label',
20880                     cls : 'box-label',
20881                     html : this.boxLabel
20882                 }
20883             ]
20884         };
20885         
20886         return cfg;
20887     },
20888     
20889     initEvents : function() 
20890     {
20891         this.parent().register(this);
20892         
20893         this.el.on('click', this.onClick, this);
20894         
20895     },
20896     
20897     onClick : function()
20898     {
20899         this.setChecked(true);
20900     },
20901     
20902     setChecked : function(state, suppressEvent)
20903     {
20904         this.parent().setValue(this.value, suppressEvent);
20905         
20906     },
20907     
20908     setBoxLabel : function(v)
20909     {
20910         this.boxLabel = v;
20911         
20912         if(this.rendered){
20913             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
20914         }
20915     }
20916     
20917 });
20918  
20919
20920  /*
20921  * - LGPL
20922  *
20923  * Input
20924  * 
20925  */
20926
20927 /**
20928  * @class Roo.bootstrap.SecurePass
20929  * @extends Roo.bootstrap.Input
20930  * Bootstrap SecurePass class
20931  *
20932  * 
20933  * @constructor
20934  * Create a new SecurePass
20935  * @param {Object} config The config object
20936  */
20937  
20938 Roo.bootstrap.SecurePass = function (config) {
20939     // these go here, so the translation tool can replace them..
20940     this.errors = {
20941         PwdEmpty: "Please type a password, and then retype it to confirm.",
20942         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20943         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20944         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20945         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20946         FNInPwd: "Your password can't contain your first name. Please type a different password.",
20947         LNInPwd: "Your password can't contain your last name. Please type a different password.",
20948         TooWeak: "Your password is Too Weak."
20949     },
20950     this.meterLabel = "Password strength:";
20951     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
20952     this.meterClass = [
20953         "roo-password-meter-tooweak", 
20954         "roo-password-meter-weak", 
20955         "roo-password-meter-medium", 
20956         "roo-password-meter-strong", 
20957         "roo-password-meter-grey"
20958     ];
20959     
20960     this.errors = {};
20961     
20962     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
20963 }
20964
20965 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
20966     /**
20967      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
20968      * {
20969      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
20970      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20971      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20972      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20973      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20974      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
20975      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
20976      * })
20977      */
20978     // private
20979     
20980     meterWidth: 300,
20981     errorMsg :'',    
20982     errors: false,
20983     imageRoot: '/',
20984     /**
20985      * @cfg {String/Object} Label for the strength meter (defaults to
20986      * 'Password strength:')
20987      */
20988     // private
20989     meterLabel: '',
20990     /**
20991      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
20992      * ['Weak', 'Medium', 'Strong'])
20993      */
20994     // private    
20995     pwdStrengths: false,    
20996     // private
20997     strength: 0,
20998     // private
20999     _lastPwd: null,
21000     // private
21001     kCapitalLetter: 0,
21002     kSmallLetter: 1,
21003     kDigit: 2,
21004     kPunctuation: 3,
21005     
21006     insecure: false,
21007     // private
21008     initEvents: function ()
21009     {
21010         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21011
21012         if (this.el.is('input[type=password]') && Roo.isSafari) {
21013             this.el.on('keydown', this.SafariOnKeyDown, this);
21014         }
21015
21016         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21017     },
21018     // private
21019     onRender: function (ct, position)
21020     {
21021         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21022         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21023         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21024
21025         this.trigger.createChild({
21026                    cn: [
21027                     {
21028                     //id: 'PwdMeter',
21029                     tag: 'div',
21030                     cls: 'roo-password-meter-grey col-xs-12',
21031                     style: {
21032                         //width: 0,
21033                         //width: this.meterWidth + 'px'                                                
21034                         }
21035                     },
21036                     {                            
21037                          cls: 'roo-password-meter-text'                          
21038                     }
21039                 ]            
21040         });
21041
21042          
21043         if (this.hideTrigger) {
21044             this.trigger.setDisplayed(false);
21045         }
21046         this.setSize(this.width || '', this.height || '');
21047     },
21048     // private
21049     onDestroy: function ()
21050     {
21051         if (this.trigger) {
21052             this.trigger.removeAllListeners();
21053             this.trigger.remove();
21054         }
21055         if (this.wrap) {
21056             this.wrap.remove();
21057         }
21058         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21059     },
21060     // private
21061     checkStrength: function ()
21062     {
21063         var pwd = this.inputEl().getValue();
21064         if (pwd == this._lastPwd) {
21065             return;
21066         }
21067
21068         var strength;
21069         if (this.ClientSideStrongPassword(pwd)) {
21070             strength = 3;
21071         } else if (this.ClientSideMediumPassword(pwd)) {
21072             strength = 2;
21073         } else if (this.ClientSideWeakPassword(pwd)) {
21074             strength = 1;
21075         } else {
21076             strength = 0;
21077         }
21078         
21079         Roo.log('strength1: ' + strength);
21080         
21081         //var pm = this.trigger.child('div/div/div').dom;
21082         var pm = this.trigger.child('div/div');
21083         pm.removeClass(this.meterClass);
21084         pm.addClass(this.meterClass[strength]);
21085                 
21086         
21087         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21088                 
21089         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21090         
21091         this._lastPwd = pwd;
21092     },
21093     reset: function ()
21094     {
21095         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21096         
21097         this._lastPwd = '';
21098         
21099         var pm = this.trigger.child('div/div');
21100         pm.removeClass(this.meterClass);
21101         pm.addClass('roo-password-meter-grey');        
21102         
21103         
21104         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21105         
21106         pt.innerHTML = '';
21107         this.inputEl().dom.type='password';
21108     },
21109     // private
21110     validateValue: function (value)
21111     {
21112         
21113         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21114             return false;
21115         }
21116         if (value.length == 0) {
21117             if (this.allowBlank) {
21118                 this.clearInvalid();
21119                 return true;
21120             }
21121
21122             this.markInvalid(this.errors.PwdEmpty);
21123             this.errorMsg = this.errors.PwdEmpty;
21124             return false;
21125         }
21126         
21127         if(this.insecure){
21128             return true;
21129         }
21130         
21131         if ('[\x21-\x7e]*'.match(value)) {
21132             this.markInvalid(this.errors.PwdBadChar);
21133             this.errorMsg = this.errors.PwdBadChar;
21134             return false;
21135         }
21136         if (value.length < 6) {
21137             this.markInvalid(this.errors.PwdShort);
21138             this.errorMsg = this.errors.PwdShort;
21139             return false;
21140         }
21141         if (value.length > 16) {
21142             this.markInvalid(this.errors.PwdLong);
21143             this.errorMsg = this.errors.PwdLong;
21144             return false;
21145         }
21146         var strength;
21147         if (this.ClientSideStrongPassword(value)) {
21148             strength = 3;
21149         } else if (this.ClientSideMediumPassword(value)) {
21150             strength = 2;
21151         } else if (this.ClientSideWeakPassword(value)) {
21152             strength = 1;
21153         } else {
21154             strength = 0;
21155         }
21156
21157         
21158         if (strength < 2) {
21159             //this.markInvalid(this.errors.TooWeak);
21160             this.errorMsg = this.errors.TooWeak;
21161             //return false;
21162         }
21163         
21164         
21165         console.log('strength2: ' + strength);
21166         
21167         //var pm = this.trigger.child('div/div/div').dom;
21168         
21169         var pm = this.trigger.child('div/div');
21170         pm.removeClass(this.meterClass);
21171         pm.addClass(this.meterClass[strength]);
21172                 
21173         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21174                 
21175         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21176         
21177         this.errorMsg = ''; 
21178         return true;
21179     },
21180     // private
21181     CharacterSetChecks: function (type)
21182     {
21183         this.type = type;
21184         this.fResult = false;
21185     },
21186     // private
21187     isctype: function (character, type)
21188     {
21189         switch (type) {  
21190             case this.kCapitalLetter:
21191                 if (character >= 'A' && character <= 'Z') {
21192                     return true;
21193                 }
21194                 break;
21195             
21196             case this.kSmallLetter:
21197                 if (character >= 'a' && character <= 'z') {
21198                     return true;
21199                 }
21200                 break;
21201             
21202             case this.kDigit:
21203                 if (character >= '0' && character <= '9') {
21204                     return true;
21205                 }
21206                 break;
21207             
21208             case this.kPunctuation:
21209                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21210                     return true;
21211                 }
21212                 break;
21213             
21214             default:
21215                 return false;
21216         }
21217
21218     },
21219     // private
21220     IsLongEnough: function (pwd, size)
21221     {
21222         return !(pwd == null || isNaN(size) || pwd.length < size);
21223     },
21224     // private
21225     SpansEnoughCharacterSets: function (word, nb)
21226     {
21227         if (!this.IsLongEnough(word, nb))
21228         {
21229             return false;
21230         }
21231
21232         var characterSetChecks = new Array(
21233             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21234             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21235         );
21236         
21237         for (var index = 0; index < word.length; ++index) {
21238             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21239                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21240                     characterSetChecks[nCharSet].fResult = true;
21241                     break;
21242                 }
21243             }
21244         }
21245
21246         var nCharSets = 0;
21247         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21248             if (characterSetChecks[nCharSet].fResult) {
21249                 ++nCharSets;
21250             }
21251         }
21252
21253         if (nCharSets < nb) {
21254             return false;
21255         }
21256         return true;
21257     },
21258     // private
21259     ClientSideStrongPassword: function (pwd)
21260     {
21261         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21262     },
21263     // private
21264     ClientSideMediumPassword: function (pwd)
21265     {
21266         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21267     },
21268     // private
21269     ClientSideWeakPassword: function (pwd)
21270     {
21271         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21272     }
21273           
21274 })//<script type="text/javascript">
21275
21276 /*
21277  * Based  Ext JS Library 1.1.1
21278  * Copyright(c) 2006-2007, Ext JS, LLC.
21279  * LGPL
21280  *
21281  */
21282  
21283 /**
21284  * @class Roo.HtmlEditorCore
21285  * @extends Roo.Component
21286  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21287  *
21288  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21289  */
21290
21291 Roo.HtmlEditorCore = function(config){
21292     
21293     
21294     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21295     
21296     
21297     this.addEvents({
21298         /**
21299          * @event initialize
21300          * Fires when the editor is fully initialized (including the iframe)
21301          * @param {Roo.HtmlEditorCore} this
21302          */
21303         initialize: true,
21304         /**
21305          * @event activate
21306          * Fires when the editor is first receives the focus. Any insertion must wait
21307          * until after this event.
21308          * @param {Roo.HtmlEditorCore} this
21309          */
21310         activate: true,
21311          /**
21312          * @event beforesync
21313          * Fires before the textarea is updated with content from the editor iframe. Return false
21314          * to cancel the sync.
21315          * @param {Roo.HtmlEditorCore} this
21316          * @param {String} html
21317          */
21318         beforesync: true,
21319          /**
21320          * @event beforepush
21321          * Fires before the iframe editor is updated with content from the textarea. Return false
21322          * to cancel the push.
21323          * @param {Roo.HtmlEditorCore} this
21324          * @param {String} html
21325          */
21326         beforepush: true,
21327          /**
21328          * @event sync
21329          * Fires when the textarea is updated with content from the editor iframe.
21330          * @param {Roo.HtmlEditorCore} this
21331          * @param {String} html
21332          */
21333         sync: true,
21334          /**
21335          * @event push
21336          * Fires when the iframe editor is updated with content from the textarea.
21337          * @param {Roo.HtmlEditorCore} this
21338          * @param {String} html
21339          */
21340         push: true,
21341         
21342         /**
21343          * @event editorevent
21344          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21345          * @param {Roo.HtmlEditorCore} this
21346          */
21347         editorevent: true
21348         
21349     });
21350     
21351     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21352     
21353     // defaults : white / black...
21354     this.applyBlacklists();
21355     
21356     
21357     
21358 };
21359
21360
21361 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21362
21363
21364      /**
21365      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21366      */
21367     
21368     owner : false,
21369     
21370      /**
21371      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21372      *                        Roo.resizable.
21373      */
21374     resizable : false,
21375      /**
21376      * @cfg {Number} height (in pixels)
21377      */   
21378     height: 300,
21379    /**
21380      * @cfg {Number} width (in pixels)
21381      */   
21382     width: 500,
21383     
21384     /**
21385      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21386      * 
21387      */
21388     stylesheets: false,
21389     
21390     // id of frame..
21391     frameId: false,
21392     
21393     // private properties
21394     validationEvent : false,
21395     deferHeight: true,
21396     initialized : false,
21397     activated : false,
21398     sourceEditMode : false,
21399     onFocus : Roo.emptyFn,
21400     iframePad:3,
21401     hideMode:'offsets',
21402     
21403     clearUp: true,
21404     
21405     // blacklist + whitelisted elements..
21406     black: false,
21407     white: false,
21408      
21409     bodyCls : '',
21410
21411     /**
21412      * Protected method that will not generally be called directly. It
21413      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21414      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21415      */
21416     getDocMarkup : function(){
21417         // body styles..
21418         var st = '';
21419         
21420         // inherit styels from page...?? 
21421         if (this.stylesheets === false) {
21422             
21423             Roo.get(document.head).select('style').each(function(node) {
21424                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21425             });
21426             
21427             Roo.get(document.head).select('link').each(function(node) { 
21428                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21429             });
21430             
21431         } else if (!this.stylesheets.length) {
21432                 // simple..
21433                 st = '<style type="text/css">' +
21434                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21435                    '</style>';
21436         } else { 
21437             st = '<style type="text/css">' +
21438                     this.stylesheets +
21439                 '</style>';
21440         }
21441         
21442         st +=  '<style type="text/css">' +
21443             'IMG { cursor: pointer } ' +
21444         '</style>';
21445
21446         var cls = 'roo-htmleditor-body';
21447         
21448         if(this.bodyCls.length){
21449             cls += ' ' + this.bodyCls;
21450         }
21451         
21452         return '<html><head>' + st  +
21453             //<style type="text/css">' +
21454             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21455             //'</style>' +
21456             ' </head><body class="' +  cls + '"></body></html>';
21457     },
21458
21459     // private
21460     onRender : function(ct, position)
21461     {
21462         var _t = this;
21463         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21464         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21465         
21466         
21467         this.el.dom.style.border = '0 none';
21468         this.el.dom.setAttribute('tabIndex', -1);
21469         this.el.addClass('x-hidden hide');
21470         
21471         
21472         
21473         if(Roo.isIE){ // fix IE 1px bogus margin
21474             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21475         }
21476        
21477         
21478         this.frameId = Roo.id();
21479         
21480          
21481         
21482         var iframe = this.owner.wrap.createChild({
21483             tag: 'iframe',
21484             cls: 'form-control', // bootstrap..
21485             id: this.frameId,
21486             name: this.frameId,
21487             frameBorder : 'no',
21488             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21489         }, this.el
21490         );
21491         
21492         
21493         this.iframe = iframe.dom;
21494
21495          this.assignDocWin();
21496         
21497         this.doc.designMode = 'on';
21498        
21499         this.doc.open();
21500         this.doc.write(this.getDocMarkup());
21501         this.doc.close();
21502
21503         
21504         var task = { // must defer to wait for browser to be ready
21505             run : function(){
21506                 //console.log("run task?" + this.doc.readyState);
21507                 this.assignDocWin();
21508                 if(this.doc.body || this.doc.readyState == 'complete'){
21509                     try {
21510                         this.doc.designMode="on";
21511                     } catch (e) {
21512                         return;
21513                     }
21514                     Roo.TaskMgr.stop(task);
21515                     this.initEditor.defer(10, this);
21516                 }
21517             },
21518             interval : 10,
21519             duration: 10000,
21520             scope: this
21521         };
21522         Roo.TaskMgr.start(task);
21523
21524     },
21525
21526     // private
21527     onResize : function(w, h)
21528     {
21529          Roo.log('resize: ' +w + ',' + h );
21530         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21531         if(!this.iframe){
21532             return;
21533         }
21534         if(typeof w == 'number'){
21535             
21536             this.iframe.style.width = w + 'px';
21537         }
21538         if(typeof h == 'number'){
21539             
21540             this.iframe.style.height = h + 'px';
21541             if(this.doc){
21542                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21543             }
21544         }
21545         
21546     },
21547
21548     /**
21549      * Toggles the editor between standard and source edit mode.
21550      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21551      */
21552     toggleSourceEdit : function(sourceEditMode){
21553         
21554         this.sourceEditMode = sourceEditMode === true;
21555         
21556         if(this.sourceEditMode){
21557  
21558             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21559             
21560         }else{
21561             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21562             //this.iframe.className = '';
21563             this.deferFocus();
21564         }
21565         //this.setSize(this.owner.wrap.getSize());
21566         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21567     },
21568
21569     
21570   
21571
21572     /**
21573      * Protected method that will not generally be called directly. If you need/want
21574      * custom HTML cleanup, this is the method you should override.
21575      * @param {String} html The HTML to be cleaned
21576      * return {String} The cleaned HTML
21577      */
21578     cleanHtml : function(html){
21579         html = String(html);
21580         if(html.length > 5){
21581             if(Roo.isSafari){ // strip safari nonsense
21582                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21583             }
21584         }
21585         if(html == '&nbsp;'){
21586             html = '';
21587         }
21588         return html;
21589     },
21590
21591     /**
21592      * HTML Editor -> Textarea
21593      * Protected method that will not generally be called directly. Syncs the contents
21594      * of the editor iframe with the textarea.
21595      */
21596     syncValue : function(){
21597         if(this.initialized){
21598             var bd = (this.doc.body || this.doc.documentElement);
21599             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21600             var html = bd.innerHTML;
21601             if(Roo.isSafari){
21602                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21603                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21604                 if(m && m[1]){
21605                     html = '<div style="'+m[0]+'">' + html + '</div>';
21606                 }
21607             }
21608             html = this.cleanHtml(html);
21609             // fix up the special chars.. normaly like back quotes in word...
21610             // however we do not want to do this with chinese..
21611             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21612                 var cc = b.charCodeAt();
21613                 if (
21614                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21615                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21616                     (cc >= 0xf900 && cc < 0xfb00 )
21617                 ) {
21618                         return b;
21619                 }
21620                 return "&#"+cc+";" 
21621             });
21622             if(this.owner.fireEvent('beforesync', this, html) !== false){
21623                 this.el.dom.value = html;
21624                 this.owner.fireEvent('sync', this, html);
21625             }
21626         }
21627     },
21628
21629     /**
21630      * Protected method that will not generally be called directly. Pushes the value of the textarea
21631      * into the iframe editor.
21632      */
21633     pushValue : function(){
21634         if(this.initialized){
21635             var v = this.el.dom.value.trim();
21636             
21637 //            if(v.length < 1){
21638 //                v = '&#160;';
21639 //            }
21640             
21641             if(this.owner.fireEvent('beforepush', this, v) !== false){
21642                 var d = (this.doc.body || this.doc.documentElement);
21643                 d.innerHTML = v;
21644                 this.cleanUpPaste();
21645                 this.el.dom.value = d.innerHTML;
21646                 this.owner.fireEvent('push', this, v);
21647             }
21648         }
21649     },
21650
21651     // private
21652     deferFocus : function(){
21653         this.focus.defer(10, this);
21654     },
21655
21656     // doc'ed in Field
21657     focus : function(){
21658         if(this.win && !this.sourceEditMode){
21659             this.win.focus();
21660         }else{
21661             this.el.focus();
21662         }
21663     },
21664     
21665     assignDocWin: function()
21666     {
21667         var iframe = this.iframe;
21668         
21669          if(Roo.isIE){
21670             this.doc = iframe.contentWindow.document;
21671             this.win = iframe.contentWindow;
21672         } else {
21673 //            if (!Roo.get(this.frameId)) {
21674 //                return;
21675 //            }
21676 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21677 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21678             
21679             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21680                 return;
21681             }
21682             
21683             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21684             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21685         }
21686     },
21687     
21688     // private
21689     initEditor : function(){
21690         //console.log("INIT EDITOR");
21691         this.assignDocWin();
21692         
21693         
21694         
21695         this.doc.designMode="on";
21696         this.doc.open();
21697         this.doc.write(this.getDocMarkup());
21698         this.doc.close();
21699         
21700         var dbody = (this.doc.body || this.doc.documentElement);
21701         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21702         // this copies styles from the containing element into thsi one..
21703         // not sure why we need all of this..
21704         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21705         
21706         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21707         //ss['background-attachment'] = 'fixed'; // w3c
21708         dbody.bgProperties = 'fixed'; // ie
21709         //Roo.DomHelper.applyStyles(dbody, ss);
21710         Roo.EventManager.on(this.doc, {
21711             //'mousedown': this.onEditorEvent,
21712             'mouseup': this.onEditorEvent,
21713             'dblclick': this.onEditorEvent,
21714             'click': this.onEditorEvent,
21715             'keyup': this.onEditorEvent,
21716             buffer:100,
21717             scope: this
21718         });
21719         if(Roo.isGecko){
21720             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21721         }
21722         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21723             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21724         }
21725         this.initialized = true;
21726
21727         this.owner.fireEvent('initialize', this);
21728         this.pushValue();
21729     },
21730
21731     // private
21732     onDestroy : function(){
21733         
21734         
21735         
21736         if(this.rendered){
21737             
21738             //for (var i =0; i < this.toolbars.length;i++) {
21739             //    // fixme - ask toolbars for heights?
21740             //    this.toolbars[i].onDestroy();
21741            // }
21742             
21743             //this.wrap.dom.innerHTML = '';
21744             //this.wrap.remove();
21745         }
21746     },
21747
21748     // private
21749     onFirstFocus : function(){
21750         
21751         this.assignDocWin();
21752         
21753         
21754         this.activated = true;
21755          
21756     
21757         if(Roo.isGecko){ // prevent silly gecko errors
21758             this.win.focus();
21759             var s = this.win.getSelection();
21760             if(!s.focusNode || s.focusNode.nodeType != 3){
21761                 var r = s.getRangeAt(0);
21762                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21763                 r.collapse(true);
21764                 this.deferFocus();
21765             }
21766             try{
21767                 this.execCmd('useCSS', true);
21768                 this.execCmd('styleWithCSS', false);
21769             }catch(e){}
21770         }
21771         this.owner.fireEvent('activate', this);
21772     },
21773
21774     // private
21775     adjustFont: function(btn){
21776         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21777         //if(Roo.isSafari){ // safari
21778         //    adjust *= 2;
21779        // }
21780         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21781         if(Roo.isSafari){ // safari
21782             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21783             v =  (v < 10) ? 10 : v;
21784             v =  (v > 48) ? 48 : v;
21785             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21786             
21787         }
21788         
21789         
21790         v = Math.max(1, v+adjust);
21791         
21792         this.execCmd('FontSize', v  );
21793     },
21794
21795     onEditorEvent : function(e)
21796     {
21797         this.owner.fireEvent('editorevent', this, e);
21798       //  this.updateToolbar();
21799         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21800     },
21801
21802     insertTag : function(tg)
21803     {
21804         // could be a bit smarter... -> wrap the current selected tRoo..
21805         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21806             
21807             range = this.createRange(this.getSelection());
21808             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21809             wrappingNode.appendChild(range.extractContents());
21810             range.insertNode(wrappingNode);
21811
21812             return;
21813             
21814             
21815             
21816         }
21817         this.execCmd("formatblock",   tg);
21818         
21819     },
21820     
21821     insertText : function(txt)
21822     {
21823         
21824         
21825         var range = this.createRange();
21826         range.deleteContents();
21827                //alert(Sender.getAttribute('label'));
21828                
21829         range.insertNode(this.doc.createTextNode(txt));
21830     } ,
21831     
21832      
21833
21834     /**
21835      * Executes a Midas editor command on the editor document and performs necessary focus and
21836      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21837      * @param {String} cmd The Midas command
21838      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21839      */
21840     relayCmd : function(cmd, value){
21841         this.win.focus();
21842         this.execCmd(cmd, value);
21843         this.owner.fireEvent('editorevent', this);
21844         //this.updateToolbar();
21845         this.owner.deferFocus();
21846     },
21847
21848     /**
21849      * Executes a Midas editor command directly on the editor document.
21850      * For visual commands, you should use {@link #relayCmd} instead.
21851      * <b>This should only be called after the editor is initialized.</b>
21852      * @param {String} cmd The Midas command
21853      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21854      */
21855     execCmd : function(cmd, value){
21856         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21857         this.syncValue();
21858     },
21859  
21860  
21861    
21862     /**
21863      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21864      * to insert tRoo.
21865      * @param {String} text | dom node.. 
21866      */
21867     insertAtCursor : function(text)
21868     {
21869         
21870         if(!this.activated){
21871             return;
21872         }
21873         /*
21874         if(Roo.isIE){
21875             this.win.focus();
21876             var r = this.doc.selection.createRange();
21877             if(r){
21878                 r.collapse(true);
21879                 r.pasteHTML(text);
21880                 this.syncValue();
21881                 this.deferFocus();
21882             
21883             }
21884             return;
21885         }
21886         */
21887         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21888             this.win.focus();
21889             
21890             
21891             // from jquery ui (MIT licenced)
21892             var range, node;
21893             var win = this.win;
21894             
21895             if (win.getSelection && win.getSelection().getRangeAt) {
21896                 range = win.getSelection().getRangeAt(0);
21897                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21898                 range.insertNode(node);
21899             } else if (win.document.selection && win.document.selection.createRange) {
21900                 // no firefox support
21901                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21902                 win.document.selection.createRange().pasteHTML(txt);
21903             } else {
21904                 // no firefox support
21905                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21906                 this.execCmd('InsertHTML', txt);
21907             } 
21908             
21909             this.syncValue();
21910             
21911             this.deferFocus();
21912         }
21913     },
21914  // private
21915     mozKeyPress : function(e){
21916         if(e.ctrlKey){
21917             var c = e.getCharCode(), cmd;
21918           
21919             if(c > 0){
21920                 c = String.fromCharCode(c).toLowerCase();
21921                 switch(c){
21922                     case 'b':
21923                         cmd = 'bold';
21924                         break;
21925                     case 'i':
21926                         cmd = 'italic';
21927                         break;
21928                     
21929                     case 'u':
21930                         cmd = 'underline';
21931                         break;
21932                     
21933                     case 'v':
21934                         this.cleanUpPaste.defer(100, this);
21935                         return;
21936                         
21937                 }
21938                 if(cmd){
21939                     this.win.focus();
21940                     this.execCmd(cmd);
21941                     this.deferFocus();
21942                     e.preventDefault();
21943                 }
21944                 
21945             }
21946         }
21947     },
21948
21949     // private
21950     fixKeys : function(){ // load time branching for fastest keydown performance
21951         if(Roo.isIE){
21952             return function(e){
21953                 var k = e.getKey(), r;
21954                 if(k == e.TAB){
21955                     e.stopEvent();
21956                     r = this.doc.selection.createRange();
21957                     if(r){
21958                         r.collapse(true);
21959                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21960                         this.deferFocus();
21961                     }
21962                     return;
21963                 }
21964                 
21965                 if(k == e.ENTER){
21966                     r = this.doc.selection.createRange();
21967                     if(r){
21968                         var target = r.parentElement();
21969                         if(!target || target.tagName.toLowerCase() != 'li'){
21970                             e.stopEvent();
21971                             r.pasteHTML('<br />');
21972                             r.collapse(false);
21973                             r.select();
21974                         }
21975                     }
21976                 }
21977                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21978                     this.cleanUpPaste.defer(100, this);
21979                     return;
21980                 }
21981                 
21982                 
21983             };
21984         }else if(Roo.isOpera){
21985             return function(e){
21986                 var k = e.getKey();
21987                 if(k == e.TAB){
21988                     e.stopEvent();
21989                     this.win.focus();
21990                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21991                     this.deferFocus();
21992                 }
21993                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21994                     this.cleanUpPaste.defer(100, this);
21995                     return;
21996                 }
21997                 
21998             };
21999         }else if(Roo.isSafari){
22000             return function(e){
22001                 var k = e.getKey();
22002                 
22003                 if(k == e.TAB){
22004                     e.stopEvent();
22005                     this.execCmd('InsertText','\t');
22006                     this.deferFocus();
22007                     return;
22008                 }
22009                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22010                     this.cleanUpPaste.defer(100, this);
22011                     return;
22012                 }
22013                 
22014              };
22015         }
22016     }(),
22017     
22018     getAllAncestors: function()
22019     {
22020         var p = this.getSelectedNode();
22021         var a = [];
22022         if (!p) {
22023             a.push(p); // push blank onto stack..
22024             p = this.getParentElement();
22025         }
22026         
22027         
22028         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22029             a.push(p);
22030             p = p.parentNode;
22031         }
22032         a.push(this.doc.body);
22033         return a;
22034     },
22035     lastSel : false,
22036     lastSelNode : false,
22037     
22038     
22039     getSelection : function() 
22040     {
22041         this.assignDocWin();
22042         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22043     },
22044     
22045     getSelectedNode: function() 
22046     {
22047         // this may only work on Gecko!!!
22048         
22049         // should we cache this!!!!
22050         
22051         
22052         
22053          
22054         var range = this.createRange(this.getSelection()).cloneRange();
22055         
22056         if (Roo.isIE) {
22057             var parent = range.parentElement();
22058             while (true) {
22059                 var testRange = range.duplicate();
22060                 testRange.moveToElementText(parent);
22061                 if (testRange.inRange(range)) {
22062                     break;
22063                 }
22064                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22065                     break;
22066                 }
22067                 parent = parent.parentElement;
22068             }
22069             return parent;
22070         }
22071         
22072         // is ancestor a text element.
22073         var ac =  range.commonAncestorContainer;
22074         if (ac.nodeType == 3) {
22075             ac = ac.parentNode;
22076         }
22077         
22078         var ar = ac.childNodes;
22079          
22080         var nodes = [];
22081         var other_nodes = [];
22082         var has_other_nodes = false;
22083         for (var i=0;i<ar.length;i++) {
22084             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22085                 continue;
22086             }
22087             // fullly contained node.
22088             
22089             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22090                 nodes.push(ar[i]);
22091                 continue;
22092             }
22093             
22094             // probably selected..
22095             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22096                 other_nodes.push(ar[i]);
22097                 continue;
22098             }
22099             // outer..
22100             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22101                 continue;
22102             }
22103             
22104             
22105             has_other_nodes = true;
22106         }
22107         if (!nodes.length && other_nodes.length) {
22108             nodes= other_nodes;
22109         }
22110         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22111             return false;
22112         }
22113         
22114         return nodes[0];
22115     },
22116     createRange: function(sel)
22117     {
22118         // this has strange effects when using with 
22119         // top toolbar - not sure if it's a great idea.
22120         //this.editor.contentWindow.focus();
22121         if (typeof sel != "undefined") {
22122             try {
22123                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22124             } catch(e) {
22125                 return this.doc.createRange();
22126             }
22127         } else {
22128             return this.doc.createRange();
22129         }
22130     },
22131     getParentElement: function()
22132     {
22133         
22134         this.assignDocWin();
22135         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22136         
22137         var range = this.createRange(sel);
22138          
22139         try {
22140             var p = range.commonAncestorContainer;
22141             while (p.nodeType == 3) { // text node
22142                 p = p.parentNode;
22143             }
22144             return p;
22145         } catch (e) {
22146             return null;
22147         }
22148     
22149     },
22150     /***
22151      *
22152      * Range intersection.. the hard stuff...
22153      *  '-1' = before
22154      *  '0' = hits..
22155      *  '1' = after.
22156      *         [ -- selected range --- ]
22157      *   [fail]                        [fail]
22158      *
22159      *    basically..
22160      *      if end is before start or  hits it. fail.
22161      *      if start is after end or hits it fail.
22162      *
22163      *   if either hits (but other is outside. - then it's not 
22164      *   
22165      *    
22166      **/
22167     
22168     
22169     // @see http://www.thismuchiknow.co.uk/?p=64.
22170     rangeIntersectsNode : function(range, node)
22171     {
22172         var nodeRange = node.ownerDocument.createRange();
22173         try {
22174             nodeRange.selectNode(node);
22175         } catch (e) {
22176             nodeRange.selectNodeContents(node);
22177         }
22178     
22179         var rangeStartRange = range.cloneRange();
22180         rangeStartRange.collapse(true);
22181     
22182         var rangeEndRange = range.cloneRange();
22183         rangeEndRange.collapse(false);
22184     
22185         var nodeStartRange = nodeRange.cloneRange();
22186         nodeStartRange.collapse(true);
22187     
22188         var nodeEndRange = nodeRange.cloneRange();
22189         nodeEndRange.collapse(false);
22190     
22191         return rangeStartRange.compareBoundaryPoints(
22192                  Range.START_TO_START, nodeEndRange) == -1 &&
22193                rangeEndRange.compareBoundaryPoints(
22194                  Range.START_TO_START, nodeStartRange) == 1;
22195         
22196          
22197     },
22198     rangeCompareNode : function(range, node)
22199     {
22200         var nodeRange = node.ownerDocument.createRange();
22201         try {
22202             nodeRange.selectNode(node);
22203         } catch (e) {
22204             nodeRange.selectNodeContents(node);
22205         }
22206         
22207         
22208         range.collapse(true);
22209     
22210         nodeRange.collapse(true);
22211      
22212         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22213         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22214          
22215         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22216         
22217         var nodeIsBefore   =  ss == 1;
22218         var nodeIsAfter    = ee == -1;
22219         
22220         if (nodeIsBefore && nodeIsAfter) {
22221             return 0; // outer
22222         }
22223         if (!nodeIsBefore && nodeIsAfter) {
22224             return 1; //right trailed.
22225         }
22226         
22227         if (nodeIsBefore && !nodeIsAfter) {
22228             return 2;  // left trailed.
22229         }
22230         // fully contined.
22231         return 3;
22232     },
22233
22234     // private? - in a new class?
22235     cleanUpPaste :  function()
22236     {
22237         // cleans up the whole document..
22238         Roo.log('cleanuppaste');
22239         
22240         this.cleanUpChildren(this.doc.body);
22241         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22242         if (clean != this.doc.body.innerHTML) {
22243             this.doc.body.innerHTML = clean;
22244         }
22245         
22246     },
22247     
22248     cleanWordChars : function(input) {// change the chars to hex code
22249         var he = Roo.HtmlEditorCore;
22250         
22251         var output = input;
22252         Roo.each(he.swapCodes, function(sw) { 
22253             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22254             
22255             output = output.replace(swapper, sw[1]);
22256         });
22257         
22258         return output;
22259     },
22260     
22261     
22262     cleanUpChildren : function (n)
22263     {
22264         if (!n.childNodes.length) {
22265             return;
22266         }
22267         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22268            this.cleanUpChild(n.childNodes[i]);
22269         }
22270     },
22271     
22272     
22273         
22274     
22275     cleanUpChild : function (node)
22276     {
22277         var ed = this;
22278         //console.log(node);
22279         if (node.nodeName == "#text") {
22280             // clean up silly Windows -- stuff?
22281             return; 
22282         }
22283         if (node.nodeName == "#comment") {
22284             node.parentNode.removeChild(node);
22285             // clean up silly Windows -- stuff?
22286             return; 
22287         }
22288         var lcname = node.tagName.toLowerCase();
22289         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22290         // whitelist of tags..
22291         
22292         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22293             // remove node.
22294             node.parentNode.removeChild(node);
22295             return;
22296             
22297         }
22298         
22299         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22300         
22301         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22302         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22303         
22304         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22305         //    remove_keep_children = true;
22306         //}
22307         
22308         if (remove_keep_children) {
22309             this.cleanUpChildren(node);
22310             // inserts everything just before this node...
22311             while (node.childNodes.length) {
22312                 var cn = node.childNodes[0];
22313                 node.removeChild(cn);
22314                 node.parentNode.insertBefore(cn, node);
22315             }
22316             node.parentNode.removeChild(node);
22317             return;
22318         }
22319         
22320         if (!node.attributes || !node.attributes.length) {
22321             this.cleanUpChildren(node);
22322             return;
22323         }
22324         
22325         function cleanAttr(n,v)
22326         {
22327             
22328             if (v.match(/^\./) || v.match(/^\//)) {
22329                 return;
22330             }
22331             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
22332                 return;
22333             }
22334             if (v.match(/^#/)) {
22335                 return;
22336             }
22337 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22338             node.removeAttribute(n);
22339             
22340         }
22341         
22342         var cwhite = this.cwhite;
22343         var cblack = this.cblack;
22344             
22345         function cleanStyle(n,v)
22346         {
22347             if (v.match(/expression/)) { //XSS?? should we even bother..
22348                 node.removeAttribute(n);
22349                 return;
22350             }
22351             
22352             var parts = v.split(/;/);
22353             var clean = [];
22354             
22355             Roo.each(parts, function(p) {
22356                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22357                 if (!p.length) {
22358                     return true;
22359                 }
22360                 var l = p.split(':').shift().replace(/\s+/g,'');
22361                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22362                 
22363                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22364 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22365                     //node.removeAttribute(n);
22366                     return true;
22367                 }
22368                 //Roo.log()
22369                 // only allow 'c whitelisted system attributes'
22370                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22371 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22372                     //node.removeAttribute(n);
22373                     return true;
22374                 }
22375                 
22376                 
22377                  
22378                 
22379                 clean.push(p);
22380                 return true;
22381             });
22382             if (clean.length) { 
22383                 node.setAttribute(n, clean.join(';'));
22384             } else {
22385                 node.removeAttribute(n);
22386             }
22387             
22388         }
22389         
22390         
22391         for (var i = node.attributes.length-1; i > -1 ; i--) {
22392             var a = node.attributes[i];
22393             //console.log(a);
22394             
22395             if (a.name.toLowerCase().substr(0,2)=='on')  {
22396                 node.removeAttribute(a.name);
22397                 continue;
22398             }
22399             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22400                 node.removeAttribute(a.name);
22401                 continue;
22402             }
22403             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22404                 cleanAttr(a.name,a.value); // fixme..
22405                 continue;
22406             }
22407             if (a.name == 'style') {
22408                 cleanStyle(a.name,a.value);
22409                 continue;
22410             }
22411             /// clean up MS crap..
22412             // tecnically this should be a list of valid class'es..
22413             
22414             
22415             if (a.name == 'class') {
22416                 if (a.value.match(/^Mso/)) {
22417                     node.className = '';
22418                 }
22419                 
22420                 if (a.value.match(/^body$/)) {
22421                     node.className = '';
22422                 }
22423                 continue;
22424             }
22425             
22426             // style cleanup!?
22427             // class cleanup?
22428             
22429         }
22430         
22431         
22432         this.cleanUpChildren(node);
22433         
22434         
22435     },
22436     
22437     /**
22438      * Clean up MS wordisms...
22439      */
22440     cleanWord : function(node)
22441     {
22442         
22443         
22444         if (!node) {
22445             this.cleanWord(this.doc.body);
22446             return;
22447         }
22448         if (node.nodeName == "#text") {
22449             // clean up silly Windows -- stuff?
22450             return; 
22451         }
22452         if (node.nodeName == "#comment") {
22453             node.parentNode.removeChild(node);
22454             // clean up silly Windows -- stuff?
22455             return; 
22456         }
22457         
22458         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22459             node.parentNode.removeChild(node);
22460             return;
22461         }
22462         
22463         // remove - but keep children..
22464         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22465             while (node.childNodes.length) {
22466                 var cn = node.childNodes[0];
22467                 node.removeChild(cn);
22468                 node.parentNode.insertBefore(cn, node);
22469             }
22470             node.parentNode.removeChild(node);
22471             this.iterateChildren(node, this.cleanWord);
22472             return;
22473         }
22474         // clean styles
22475         if (node.className.length) {
22476             
22477             var cn = node.className.split(/\W+/);
22478             var cna = [];
22479             Roo.each(cn, function(cls) {
22480                 if (cls.match(/Mso[a-zA-Z]+/)) {
22481                     return;
22482                 }
22483                 cna.push(cls);
22484             });
22485             node.className = cna.length ? cna.join(' ') : '';
22486             if (!cna.length) {
22487                 node.removeAttribute("class");
22488             }
22489         }
22490         
22491         if (node.hasAttribute("lang")) {
22492             node.removeAttribute("lang");
22493         }
22494         
22495         if (node.hasAttribute("style")) {
22496             
22497             var styles = node.getAttribute("style").split(";");
22498             var nstyle = [];
22499             Roo.each(styles, function(s) {
22500                 if (!s.match(/:/)) {
22501                     return;
22502                 }
22503                 var kv = s.split(":");
22504                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22505                     return;
22506                 }
22507                 // what ever is left... we allow.
22508                 nstyle.push(s);
22509             });
22510             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22511             if (!nstyle.length) {
22512                 node.removeAttribute('style');
22513             }
22514         }
22515         this.iterateChildren(node, this.cleanWord);
22516         
22517         
22518         
22519     },
22520     /**
22521      * iterateChildren of a Node, calling fn each time, using this as the scole..
22522      * @param {DomNode} node node to iterate children of.
22523      * @param {Function} fn method of this class to call on each item.
22524      */
22525     iterateChildren : function(node, fn)
22526     {
22527         if (!node.childNodes.length) {
22528                 return;
22529         }
22530         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22531            fn.call(this, node.childNodes[i])
22532         }
22533     },
22534     
22535     
22536     /**
22537      * cleanTableWidths.
22538      *
22539      * Quite often pasting from word etc.. results in tables with column and widths.
22540      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22541      *
22542      */
22543     cleanTableWidths : function(node)
22544     {
22545          
22546          
22547         if (!node) {
22548             this.cleanTableWidths(this.doc.body);
22549             return;
22550         }
22551         
22552         // ignore list...
22553         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22554             return; 
22555         }
22556         Roo.log(node.tagName);
22557         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22558             this.iterateChildren(node, this.cleanTableWidths);
22559             return;
22560         }
22561         if (node.hasAttribute('width')) {
22562             node.removeAttribute('width');
22563         }
22564         
22565          
22566         if (node.hasAttribute("style")) {
22567             // pretty basic...
22568             
22569             var styles = node.getAttribute("style").split(";");
22570             var nstyle = [];
22571             Roo.each(styles, function(s) {
22572                 if (!s.match(/:/)) {
22573                     return;
22574                 }
22575                 var kv = s.split(":");
22576                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22577                     return;
22578                 }
22579                 // what ever is left... we allow.
22580                 nstyle.push(s);
22581             });
22582             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22583             if (!nstyle.length) {
22584                 node.removeAttribute('style');
22585             }
22586         }
22587         
22588         this.iterateChildren(node, this.cleanTableWidths);
22589         
22590         
22591     },
22592     
22593     
22594     
22595     
22596     domToHTML : function(currentElement, depth, nopadtext) {
22597         
22598         depth = depth || 0;
22599         nopadtext = nopadtext || false;
22600     
22601         if (!currentElement) {
22602             return this.domToHTML(this.doc.body);
22603         }
22604         
22605         //Roo.log(currentElement);
22606         var j;
22607         var allText = false;
22608         var nodeName = currentElement.nodeName;
22609         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22610         
22611         if  (nodeName == '#text') {
22612             
22613             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22614         }
22615         
22616         
22617         var ret = '';
22618         if (nodeName != 'BODY') {
22619              
22620             var i = 0;
22621             // Prints the node tagName, such as <A>, <IMG>, etc
22622             if (tagName) {
22623                 var attr = [];
22624                 for(i = 0; i < currentElement.attributes.length;i++) {
22625                     // quoting?
22626                     var aname = currentElement.attributes.item(i).name;
22627                     if (!currentElement.attributes.item(i).value.length) {
22628                         continue;
22629                     }
22630                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22631                 }
22632                 
22633                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22634             } 
22635             else {
22636                 
22637                 // eack
22638             }
22639         } else {
22640             tagName = false;
22641         }
22642         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22643             return ret;
22644         }
22645         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22646             nopadtext = true;
22647         }
22648         
22649         
22650         // Traverse the tree
22651         i = 0;
22652         var currentElementChild = currentElement.childNodes.item(i);
22653         var allText = true;
22654         var innerHTML  = '';
22655         lastnode = '';
22656         while (currentElementChild) {
22657             // Formatting code (indent the tree so it looks nice on the screen)
22658             var nopad = nopadtext;
22659             if (lastnode == 'SPAN') {
22660                 nopad  = true;
22661             }
22662             // text
22663             if  (currentElementChild.nodeName == '#text') {
22664                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22665                 toadd = nopadtext ? toadd : toadd.trim();
22666                 if (!nopad && toadd.length > 80) {
22667                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22668                 }
22669                 innerHTML  += toadd;
22670                 
22671                 i++;
22672                 currentElementChild = currentElement.childNodes.item(i);
22673                 lastNode = '';
22674                 continue;
22675             }
22676             allText = false;
22677             
22678             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22679                 
22680             // Recursively traverse the tree structure of the child node
22681             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22682             lastnode = currentElementChild.nodeName;
22683             i++;
22684             currentElementChild=currentElement.childNodes.item(i);
22685         }
22686         
22687         ret += innerHTML;
22688         
22689         if (!allText) {
22690                 // The remaining code is mostly for formatting the tree
22691             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22692         }
22693         
22694         
22695         if (tagName) {
22696             ret+= "</"+tagName+">";
22697         }
22698         return ret;
22699         
22700     },
22701         
22702     applyBlacklists : function()
22703     {
22704         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22705         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22706         
22707         this.white = [];
22708         this.black = [];
22709         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22710             if (b.indexOf(tag) > -1) {
22711                 return;
22712             }
22713             this.white.push(tag);
22714             
22715         }, this);
22716         
22717         Roo.each(w, function(tag) {
22718             if (b.indexOf(tag) > -1) {
22719                 return;
22720             }
22721             if (this.white.indexOf(tag) > -1) {
22722                 return;
22723             }
22724             this.white.push(tag);
22725             
22726         }, this);
22727         
22728         
22729         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22730             if (w.indexOf(tag) > -1) {
22731                 return;
22732             }
22733             this.black.push(tag);
22734             
22735         }, this);
22736         
22737         Roo.each(b, function(tag) {
22738             if (w.indexOf(tag) > -1) {
22739                 return;
22740             }
22741             if (this.black.indexOf(tag) > -1) {
22742                 return;
22743             }
22744             this.black.push(tag);
22745             
22746         }, this);
22747         
22748         
22749         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22750         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22751         
22752         this.cwhite = [];
22753         this.cblack = [];
22754         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22755             if (b.indexOf(tag) > -1) {
22756                 return;
22757             }
22758             this.cwhite.push(tag);
22759             
22760         }, this);
22761         
22762         Roo.each(w, function(tag) {
22763             if (b.indexOf(tag) > -1) {
22764                 return;
22765             }
22766             if (this.cwhite.indexOf(tag) > -1) {
22767                 return;
22768             }
22769             this.cwhite.push(tag);
22770             
22771         }, this);
22772         
22773         
22774         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22775             if (w.indexOf(tag) > -1) {
22776                 return;
22777             }
22778             this.cblack.push(tag);
22779             
22780         }, this);
22781         
22782         Roo.each(b, function(tag) {
22783             if (w.indexOf(tag) > -1) {
22784                 return;
22785             }
22786             if (this.cblack.indexOf(tag) > -1) {
22787                 return;
22788             }
22789             this.cblack.push(tag);
22790             
22791         }, this);
22792     },
22793     
22794     setStylesheets : function(stylesheets)
22795     {
22796         if(typeof(stylesheets) == 'string'){
22797             Roo.get(this.iframe.contentDocument.head).createChild({
22798                 tag : 'link',
22799                 rel : 'stylesheet',
22800                 type : 'text/css',
22801                 href : stylesheets
22802             });
22803             
22804             return;
22805         }
22806         var _this = this;
22807      
22808         Roo.each(stylesheets, function(s) {
22809             if(!s.length){
22810                 return;
22811             }
22812             
22813             Roo.get(_this.iframe.contentDocument.head).createChild({
22814                 tag : 'link',
22815                 rel : 'stylesheet',
22816                 type : 'text/css',
22817                 href : s
22818             });
22819         });
22820
22821         
22822     },
22823     
22824     removeStylesheets : function()
22825     {
22826         var _this = this;
22827         
22828         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22829             s.remove();
22830         });
22831     },
22832     
22833     setStyle : function(style)
22834     {
22835         Roo.get(this.iframe.contentDocument.head).createChild({
22836             tag : 'style',
22837             type : 'text/css',
22838             html : style
22839         });
22840
22841         return;
22842     }
22843     
22844     // hide stuff that is not compatible
22845     /**
22846      * @event blur
22847      * @hide
22848      */
22849     /**
22850      * @event change
22851      * @hide
22852      */
22853     /**
22854      * @event focus
22855      * @hide
22856      */
22857     /**
22858      * @event specialkey
22859      * @hide
22860      */
22861     /**
22862      * @cfg {String} fieldClass @hide
22863      */
22864     /**
22865      * @cfg {String} focusClass @hide
22866      */
22867     /**
22868      * @cfg {String} autoCreate @hide
22869      */
22870     /**
22871      * @cfg {String} inputType @hide
22872      */
22873     /**
22874      * @cfg {String} invalidClass @hide
22875      */
22876     /**
22877      * @cfg {String} invalidText @hide
22878      */
22879     /**
22880      * @cfg {String} msgFx @hide
22881      */
22882     /**
22883      * @cfg {String} validateOnBlur @hide
22884      */
22885 });
22886
22887 Roo.HtmlEditorCore.white = [
22888         'area', 'br', 'img', 'input', 'hr', 'wbr',
22889         
22890        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22891        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22892        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22893        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22894        'table',   'ul',         'xmp', 
22895        
22896        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22897       'thead',   'tr', 
22898      
22899       'dir', 'menu', 'ol', 'ul', 'dl',
22900        
22901       'embed',  'object'
22902 ];
22903
22904
22905 Roo.HtmlEditorCore.black = [
22906     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22907         'applet', // 
22908         'base',   'basefont', 'bgsound', 'blink',  'body', 
22909         'frame',  'frameset', 'head',    'html',   'ilayer', 
22910         'iframe', 'layer',  'link',     'meta',    'object',   
22911         'script', 'style' ,'title',  'xml' // clean later..
22912 ];
22913 Roo.HtmlEditorCore.clean = [
22914     'script', 'style', 'title', 'xml'
22915 ];
22916 Roo.HtmlEditorCore.remove = [
22917     'font'
22918 ];
22919 // attributes..
22920
22921 Roo.HtmlEditorCore.ablack = [
22922     'on'
22923 ];
22924     
22925 Roo.HtmlEditorCore.aclean = [ 
22926     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22927 ];
22928
22929 // protocols..
22930 Roo.HtmlEditorCore.pwhite= [
22931         'http',  'https',  'mailto'
22932 ];
22933
22934 // white listed style attributes.
22935 Roo.HtmlEditorCore.cwhite= [
22936       //  'text-align', /// default is to allow most things..
22937       
22938          
22939 //        'font-size'//??
22940 ];
22941
22942 // black listed style attributes.
22943 Roo.HtmlEditorCore.cblack= [
22944       //  'font-size' -- this can be set by the project 
22945 ];
22946
22947
22948 Roo.HtmlEditorCore.swapCodes   =[ 
22949     [    8211, "--" ], 
22950     [    8212, "--" ], 
22951     [    8216,  "'" ],  
22952     [    8217, "'" ],  
22953     [    8220, '"' ],  
22954     [    8221, '"' ],  
22955     [    8226, "*" ],  
22956     [    8230, "..." ]
22957 ]; 
22958
22959     /*
22960  * - LGPL
22961  *
22962  * HtmlEditor
22963  * 
22964  */
22965
22966 /**
22967  * @class Roo.bootstrap.HtmlEditor
22968  * @extends Roo.bootstrap.TextArea
22969  * Bootstrap HtmlEditor class
22970
22971  * @constructor
22972  * Create a new HtmlEditor
22973  * @param {Object} config The config object
22974  */
22975
22976 Roo.bootstrap.HtmlEditor = function(config){
22977     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
22978     if (!this.toolbars) {
22979         this.toolbars = [];
22980     }
22981     
22982     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22983     this.addEvents({
22984             /**
22985              * @event initialize
22986              * Fires when the editor is fully initialized (including the iframe)
22987              * @param {HtmlEditor} this
22988              */
22989             initialize: true,
22990             /**
22991              * @event activate
22992              * Fires when the editor is first receives the focus. Any insertion must wait
22993              * until after this event.
22994              * @param {HtmlEditor} this
22995              */
22996             activate: true,
22997              /**
22998              * @event beforesync
22999              * Fires before the textarea is updated with content from the editor iframe. Return false
23000              * to cancel the sync.
23001              * @param {HtmlEditor} this
23002              * @param {String} html
23003              */
23004             beforesync: true,
23005              /**
23006              * @event beforepush
23007              * Fires before the iframe editor is updated with content from the textarea. Return false
23008              * to cancel the push.
23009              * @param {HtmlEditor} this
23010              * @param {String} html
23011              */
23012             beforepush: true,
23013              /**
23014              * @event sync
23015              * Fires when the textarea is updated with content from the editor iframe.
23016              * @param {HtmlEditor} this
23017              * @param {String} html
23018              */
23019             sync: true,
23020              /**
23021              * @event push
23022              * Fires when the iframe editor is updated with content from the textarea.
23023              * @param {HtmlEditor} this
23024              * @param {String} html
23025              */
23026             push: true,
23027              /**
23028              * @event editmodechange
23029              * Fires when the editor switches edit modes
23030              * @param {HtmlEditor} this
23031              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23032              */
23033             editmodechange: true,
23034             /**
23035              * @event editorevent
23036              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23037              * @param {HtmlEditor} this
23038              */
23039             editorevent: true,
23040             /**
23041              * @event firstfocus
23042              * Fires when on first focus - needed by toolbars..
23043              * @param {HtmlEditor} this
23044              */
23045             firstfocus: true,
23046             /**
23047              * @event autosave
23048              * Auto save the htmlEditor value as a file into Events
23049              * @param {HtmlEditor} this
23050              */
23051             autosave: true,
23052             /**
23053              * @event savedpreview
23054              * preview the saved version of htmlEditor
23055              * @param {HtmlEditor} this
23056              */
23057             savedpreview: true
23058         });
23059 };
23060
23061
23062 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23063     
23064     
23065       /**
23066      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23067      */
23068     toolbars : false,
23069     
23070      /**
23071     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23072     */
23073     btns : [],
23074    
23075      /**
23076      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23077      *                        Roo.resizable.
23078      */
23079     resizable : false,
23080      /**
23081      * @cfg {Number} height (in pixels)
23082      */   
23083     height: 300,
23084    /**
23085      * @cfg {Number} width (in pixels)
23086      */   
23087     width: false,
23088     
23089     /**
23090      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23091      * 
23092      */
23093     stylesheets: false,
23094     
23095     // id of frame..
23096     frameId: false,
23097     
23098     // private properties
23099     validationEvent : false,
23100     deferHeight: true,
23101     initialized : false,
23102     activated : false,
23103     
23104     onFocus : Roo.emptyFn,
23105     iframePad:3,
23106     hideMode:'offsets',
23107     
23108     tbContainer : false,
23109     
23110     bodyCls : '',
23111     
23112     toolbarContainer :function() {
23113         return this.wrap.select('.x-html-editor-tb',true).first();
23114     },
23115
23116     /**
23117      * Protected method that will not generally be called directly. It
23118      * is called when the editor creates its toolbar. Override this method if you need to
23119      * add custom toolbar buttons.
23120      * @param {HtmlEditor} editor
23121      */
23122     createToolbar : function(){
23123         Roo.log('renewing');
23124         Roo.log("create toolbars");
23125         
23126         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23127         this.toolbars[0].render(this.toolbarContainer());
23128         
23129         return;
23130         
23131 //        if (!editor.toolbars || !editor.toolbars.length) {
23132 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23133 //        }
23134 //        
23135 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23136 //            editor.toolbars[i] = Roo.factory(
23137 //                    typeof(editor.toolbars[i]) == 'string' ?
23138 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23139 //                Roo.bootstrap.HtmlEditor);
23140 //            editor.toolbars[i].init(editor);
23141 //        }
23142     },
23143
23144      
23145     // private
23146     onRender : function(ct, position)
23147     {
23148        // Roo.log("Call onRender: " + this.xtype);
23149         var _t = this;
23150         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23151       
23152         this.wrap = this.inputEl().wrap({
23153             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23154         });
23155         
23156         this.editorcore.onRender(ct, position);
23157          
23158         if (this.resizable) {
23159             this.resizeEl = new Roo.Resizable(this.wrap, {
23160                 pinned : true,
23161                 wrap: true,
23162                 dynamic : true,
23163                 minHeight : this.height,
23164                 height: this.height,
23165                 handles : this.resizable,
23166                 width: this.width,
23167                 listeners : {
23168                     resize : function(r, w, h) {
23169                         _t.onResize(w,h); // -something
23170                     }
23171                 }
23172             });
23173             
23174         }
23175         this.createToolbar(this);
23176        
23177         
23178         if(!this.width && this.resizable){
23179             this.setSize(this.wrap.getSize());
23180         }
23181         if (this.resizeEl) {
23182             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23183             // should trigger onReize..
23184         }
23185         
23186     },
23187
23188     // private
23189     onResize : function(w, h)
23190     {
23191         Roo.log('resize: ' +w + ',' + h );
23192         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23193         var ew = false;
23194         var eh = false;
23195         
23196         if(this.inputEl() ){
23197             if(typeof w == 'number'){
23198                 var aw = w - this.wrap.getFrameWidth('lr');
23199                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23200                 ew = aw;
23201             }
23202             if(typeof h == 'number'){
23203                  var tbh = -11;  // fixme it needs to tool bar size!
23204                 for (var i =0; i < this.toolbars.length;i++) {
23205                     // fixme - ask toolbars for heights?
23206                     tbh += this.toolbars[i].el.getHeight();
23207                     //if (this.toolbars[i].footer) {
23208                     //    tbh += this.toolbars[i].footer.el.getHeight();
23209                     //}
23210                 }
23211               
23212                 
23213                 
23214                 
23215                 
23216                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23217                 ah -= 5; // knock a few pixes off for look..
23218                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23219                 var eh = ah;
23220             }
23221         }
23222         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23223         this.editorcore.onResize(ew,eh);
23224         
23225     },
23226
23227     /**
23228      * Toggles the editor between standard and source edit mode.
23229      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23230      */
23231     toggleSourceEdit : function(sourceEditMode)
23232     {
23233         this.editorcore.toggleSourceEdit(sourceEditMode);
23234         
23235         if(this.editorcore.sourceEditMode){
23236             Roo.log('editor - showing textarea');
23237             
23238 //            Roo.log('in');
23239 //            Roo.log(this.syncValue());
23240             this.syncValue();
23241             this.inputEl().removeClass(['hide', 'x-hidden']);
23242             this.inputEl().dom.removeAttribute('tabIndex');
23243             this.inputEl().focus();
23244         }else{
23245             Roo.log('editor - hiding textarea');
23246 //            Roo.log('out')
23247 //            Roo.log(this.pushValue()); 
23248             this.pushValue();
23249             
23250             this.inputEl().addClass(['hide', 'x-hidden']);
23251             this.inputEl().dom.setAttribute('tabIndex', -1);
23252             //this.deferFocus();
23253         }
23254          
23255         if(this.resizable){
23256             this.setSize(this.wrap.getSize());
23257         }
23258         
23259         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23260     },
23261  
23262     // private (for BoxComponent)
23263     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23264
23265     // private (for BoxComponent)
23266     getResizeEl : function(){
23267         return this.wrap;
23268     },
23269
23270     // private (for BoxComponent)
23271     getPositionEl : function(){
23272         return this.wrap;
23273     },
23274
23275     // private
23276     initEvents : function(){
23277         this.originalValue = this.getValue();
23278     },
23279
23280 //    /**
23281 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23282 //     * @method
23283 //     */
23284 //    markInvalid : Roo.emptyFn,
23285 //    /**
23286 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23287 //     * @method
23288 //     */
23289 //    clearInvalid : Roo.emptyFn,
23290
23291     setValue : function(v){
23292         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23293         this.editorcore.pushValue();
23294     },
23295
23296      
23297     // private
23298     deferFocus : function(){
23299         this.focus.defer(10, this);
23300     },
23301
23302     // doc'ed in Field
23303     focus : function(){
23304         this.editorcore.focus();
23305         
23306     },
23307       
23308
23309     // private
23310     onDestroy : function(){
23311         
23312         
23313         
23314         if(this.rendered){
23315             
23316             for (var i =0; i < this.toolbars.length;i++) {
23317                 // fixme - ask toolbars for heights?
23318                 this.toolbars[i].onDestroy();
23319             }
23320             
23321             this.wrap.dom.innerHTML = '';
23322             this.wrap.remove();
23323         }
23324     },
23325
23326     // private
23327     onFirstFocus : function(){
23328         //Roo.log("onFirstFocus");
23329         this.editorcore.onFirstFocus();
23330          for (var i =0; i < this.toolbars.length;i++) {
23331             this.toolbars[i].onFirstFocus();
23332         }
23333         
23334     },
23335     
23336     // private
23337     syncValue : function()
23338     {   
23339         this.editorcore.syncValue();
23340     },
23341     
23342     pushValue : function()
23343     {   
23344         this.editorcore.pushValue();
23345     }
23346      
23347     
23348     // hide stuff that is not compatible
23349     /**
23350      * @event blur
23351      * @hide
23352      */
23353     /**
23354      * @event change
23355      * @hide
23356      */
23357     /**
23358      * @event focus
23359      * @hide
23360      */
23361     /**
23362      * @event specialkey
23363      * @hide
23364      */
23365     /**
23366      * @cfg {String} fieldClass @hide
23367      */
23368     /**
23369      * @cfg {String} focusClass @hide
23370      */
23371     /**
23372      * @cfg {String} autoCreate @hide
23373      */
23374     /**
23375      * @cfg {String} inputType @hide
23376      */
23377     /**
23378      * @cfg {String} invalidClass @hide
23379      */
23380     /**
23381      * @cfg {String} invalidText @hide
23382      */
23383     /**
23384      * @cfg {String} msgFx @hide
23385      */
23386     /**
23387      * @cfg {String} validateOnBlur @hide
23388      */
23389 });
23390  
23391     
23392    
23393    
23394    
23395       
23396 Roo.namespace('Roo.bootstrap.htmleditor');
23397 /**
23398  * @class Roo.bootstrap.HtmlEditorToolbar1
23399  * Basic Toolbar
23400  * 
23401  * Usage:
23402  *
23403  new Roo.bootstrap.HtmlEditor({
23404     ....
23405     toolbars : [
23406         new Roo.bootstrap.HtmlEditorToolbar1({
23407             disable : { fonts: 1 , format: 1, ..., ... , ...],
23408             btns : [ .... ]
23409         })
23410     }
23411      
23412  * 
23413  * @cfg {Object} disable List of elements to disable..
23414  * @cfg {Array} btns List of additional buttons.
23415  * 
23416  * 
23417  * NEEDS Extra CSS? 
23418  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23419  */
23420  
23421 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23422 {
23423     
23424     Roo.apply(this, config);
23425     
23426     // default disabled, based on 'good practice'..
23427     this.disable = this.disable || {};
23428     Roo.applyIf(this.disable, {
23429         fontSize : true,
23430         colors : true,
23431         specialElements : true
23432     });
23433     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23434     
23435     this.editor = config.editor;
23436     this.editorcore = config.editor.editorcore;
23437     
23438     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23439     
23440     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23441     // dont call parent... till later.
23442 }
23443 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23444      
23445     bar : true,
23446     
23447     editor : false,
23448     editorcore : false,
23449     
23450     
23451     formats : [
23452         "p" ,  
23453         "h1","h2","h3","h4","h5","h6", 
23454         "pre", "code", 
23455         "abbr", "acronym", "address", "cite", "samp", "var",
23456         'div','span'
23457     ],
23458     
23459     onRender : function(ct, position)
23460     {
23461        // Roo.log("Call onRender: " + this.xtype);
23462         
23463        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23464        Roo.log(this.el);
23465        this.el.dom.style.marginBottom = '0';
23466        var _this = this;
23467        var editorcore = this.editorcore;
23468        var editor= this.editor;
23469        
23470        var children = [];
23471        var btn = function(id,cmd , toggle, handler, html){
23472        
23473             var  event = toggle ? 'toggle' : 'click';
23474        
23475             var a = {
23476                 size : 'sm',
23477                 xtype: 'Button',
23478                 xns: Roo.bootstrap,
23479                 glyphicon : id,
23480                 cmd : id || cmd,
23481                 enableToggle:toggle !== false,
23482                 html : html || '',
23483                 pressed : toggle ? false : null,
23484                 listeners : {}
23485             };
23486             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23487                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23488             };
23489             children.push(a);
23490             return a;
23491        }
23492        
23493     //    var cb_box = function...
23494         
23495         var style = {
23496                 xtype: 'Button',
23497                 size : 'sm',
23498                 xns: Roo.bootstrap,
23499                 glyphicon : 'font',
23500                 //html : 'submit'
23501                 menu : {
23502                     xtype: 'Menu',
23503                     xns: Roo.bootstrap,
23504                     items:  []
23505                 }
23506         };
23507         Roo.each(this.formats, function(f) {
23508             style.menu.items.push({
23509                 xtype :'MenuItem',
23510                 xns: Roo.bootstrap,
23511                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23512                 tagname : f,
23513                 listeners : {
23514                     click : function()
23515                     {
23516                         editorcore.insertTag(this.tagname);
23517                         editor.focus();
23518                     }
23519                 }
23520                 
23521             });
23522         });
23523         children.push(style);   
23524         
23525         btn('bold',false,true);
23526         btn('italic',false,true);
23527         btn('align-left', 'justifyleft',true);
23528         btn('align-center', 'justifycenter',true);
23529         btn('align-right' , 'justifyright',true);
23530         btn('link', false, false, function(btn) {
23531             //Roo.log("create link?");
23532             var url = prompt(this.createLinkText, this.defaultLinkValue);
23533             if(url && url != 'http:/'+'/'){
23534                 this.editorcore.relayCmd('createlink', url);
23535             }
23536         }),
23537         btn('list','insertunorderedlist',true);
23538         btn('pencil', false,true, function(btn){
23539                 Roo.log(this);
23540                 this.toggleSourceEdit(btn.pressed);
23541         });
23542         
23543         if (this.editor.btns.length > 0) {
23544             for (var i = 0; i<this.editor.btns.length; i++) {
23545                 children.push(this.editor.btns[i]);
23546             }
23547         }
23548         
23549         /*
23550         var cog = {
23551                 xtype: 'Button',
23552                 size : 'sm',
23553                 xns: Roo.bootstrap,
23554                 glyphicon : 'cog',
23555                 //html : 'submit'
23556                 menu : {
23557                     xtype: 'Menu',
23558                     xns: Roo.bootstrap,
23559                     items:  []
23560                 }
23561         };
23562         
23563         cog.menu.items.push({
23564             xtype :'MenuItem',
23565             xns: Roo.bootstrap,
23566             html : Clean styles,
23567             tagname : f,
23568             listeners : {
23569                 click : function()
23570                 {
23571                     editorcore.insertTag(this.tagname);
23572                     editor.focus();
23573                 }
23574             }
23575             
23576         });
23577        */
23578         
23579          
23580        this.xtype = 'NavSimplebar';
23581         
23582         for(var i=0;i< children.length;i++) {
23583             
23584             this.buttons.add(this.addxtypeChild(children[i]));
23585             
23586         }
23587         
23588         editor.on('editorevent', this.updateToolbar, this);
23589     },
23590     onBtnClick : function(id)
23591     {
23592        this.editorcore.relayCmd(id);
23593        this.editorcore.focus();
23594     },
23595     
23596     /**
23597      * Protected method that will not generally be called directly. It triggers
23598      * a toolbar update by reading the markup state of the current selection in the editor.
23599      */
23600     updateToolbar: function(){
23601
23602         if(!this.editorcore.activated){
23603             this.editor.onFirstFocus(); // is this neeed?
23604             return;
23605         }
23606
23607         var btns = this.buttons; 
23608         var doc = this.editorcore.doc;
23609         btns.get('bold').setActive(doc.queryCommandState('bold'));
23610         btns.get('italic').setActive(doc.queryCommandState('italic'));
23611         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23612         
23613         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23614         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23615         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23616         
23617         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23618         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23619          /*
23620         
23621         var ans = this.editorcore.getAllAncestors();
23622         if (this.formatCombo) {
23623             
23624             
23625             var store = this.formatCombo.store;
23626             this.formatCombo.setValue("");
23627             for (var i =0; i < ans.length;i++) {
23628                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23629                     // select it..
23630                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23631                     break;
23632                 }
23633             }
23634         }
23635         
23636         
23637         
23638         // hides menus... - so this cant be on a menu...
23639         Roo.bootstrap.MenuMgr.hideAll();
23640         */
23641         Roo.bootstrap.MenuMgr.hideAll();
23642         //this.editorsyncValue();
23643     },
23644     onFirstFocus: function() {
23645         this.buttons.each(function(item){
23646            item.enable();
23647         });
23648     },
23649     toggleSourceEdit : function(sourceEditMode){
23650         
23651           
23652         if(sourceEditMode){
23653             Roo.log("disabling buttons");
23654            this.buttons.each( function(item){
23655                 if(item.cmd != 'pencil'){
23656                     item.disable();
23657                 }
23658             });
23659           
23660         }else{
23661             Roo.log("enabling buttons");
23662             if(this.editorcore.initialized){
23663                 this.buttons.each( function(item){
23664                     item.enable();
23665                 });
23666             }
23667             
23668         }
23669         Roo.log("calling toggole on editor");
23670         // tell the editor that it's been pressed..
23671         this.editor.toggleSourceEdit(sourceEditMode);
23672        
23673     }
23674 });
23675
23676
23677
23678
23679
23680 /**
23681  * @class Roo.bootstrap.Table.AbstractSelectionModel
23682  * @extends Roo.util.Observable
23683  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23684  * implemented by descendant classes.  This class should not be directly instantiated.
23685  * @constructor
23686  */
23687 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23688     this.locked = false;
23689     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23690 };
23691
23692
23693 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23694     /** @ignore Called by the grid automatically. Do not call directly. */
23695     init : function(grid){
23696         this.grid = grid;
23697         this.initEvents();
23698     },
23699
23700     /**
23701      * Locks the selections.
23702      */
23703     lock : function(){
23704         this.locked = true;
23705     },
23706
23707     /**
23708      * Unlocks the selections.
23709      */
23710     unlock : function(){
23711         this.locked = false;
23712     },
23713
23714     /**
23715      * Returns true if the selections are locked.
23716      * @return {Boolean}
23717      */
23718     isLocked : function(){
23719         return this.locked;
23720     }
23721 });
23722 /**
23723  * @extends Roo.bootstrap.Table.AbstractSelectionModel
23724  * @class Roo.bootstrap.Table.RowSelectionModel
23725  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23726  * It supports multiple selections and keyboard selection/navigation. 
23727  * @constructor
23728  * @param {Object} config
23729  */
23730
23731 Roo.bootstrap.Table.RowSelectionModel = function(config){
23732     Roo.apply(this, config);
23733     this.selections = new Roo.util.MixedCollection(false, function(o){
23734         return o.id;
23735     });
23736
23737     this.last = false;
23738     this.lastActive = false;
23739
23740     this.addEvents({
23741         /**
23742              * @event selectionchange
23743              * Fires when the selection changes
23744              * @param {SelectionModel} this
23745              */
23746             "selectionchange" : true,
23747         /**
23748              * @event afterselectionchange
23749              * Fires after the selection changes (eg. by key press or clicking)
23750              * @param {SelectionModel} this
23751              */
23752             "afterselectionchange" : true,
23753         /**
23754              * @event beforerowselect
23755              * Fires when a row is selected being selected, return false to cancel.
23756              * @param {SelectionModel} this
23757              * @param {Number} rowIndex The selected index
23758              * @param {Boolean} keepExisting False if other selections will be cleared
23759              */
23760             "beforerowselect" : true,
23761         /**
23762              * @event rowselect
23763              * Fires when a row is selected.
23764              * @param {SelectionModel} this
23765              * @param {Number} rowIndex The selected index
23766              * @param {Roo.data.Record} r The record
23767              */
23768             "rowselect" : true,
23769         /**
23770              * @event rowdeselect
23771              * Fires when a row is deselected.
23772              * @param {SelectionModel} this
23773              * @param {Number} rowIndex The selected index
23774              */
23775         "rowdeselect" : true
23776     });
23777     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23778     this.locked = false;
23779  };
23780
23781 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
23782     /**
23783      * @cfg {Boolean} singleSelect
23784      * True to allow selection of only one row at a time (defaults to false)
23785      */
23786     singleSelect : false,
23787
23788     // private
23789     initEvents : function()
23790     {
23791
23792         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23793         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
23794         //}else{ // allow click to work like normal
23795          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
23796         //}
23797         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23798         this.grid.on("rowclick", this.handleMouseDown, this);
23799         
23800         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23801             "up" : function(e){
23802                 if(!e.shiftKey){
23803                     this.selectPrevious(e.shiftKey);
23804                 }else if(this.last !== false && this.lastActive !== false){
23805                     var last = this.last;
23806                     this.selectRange(this.last,  this.lastActive-1);
23807                     this.grid.getView().focusRow(this.lastActive);
23808                     if(last !== false){
23809                         this.last = last;
23810                     }
23811                 }else{
23812                     this.selectFirstRow();
23813                 }
23814                 this.fireEvent("afterselectionchange", this);
23815             },
23816             "down" : function(e){
23817                 if(!e.shiftKey){
23818                     this.selectNext(e.shiftKey);
23819                 }else if(this.last !== false && this.lastActive !== false){
23820                     var last = this.last;
23821                     this.selectRange(this.last,  this.lastActive+1);
23822                     this.grid.getView().focusRow(this.lastActive);
23823                     if(last !== false){
23824                         this.last = last;
23825                     }
23826                 }else{
23827                     this.selectFirstRow();
23828                 }
23829                 this.fireEvent("afterselectionchange", this);
23830             },
23831             scope: this
23832         });
23833         this.grid.store.on('load', function(){
23834             this.selections.clear();
23835         },this);
23836         /*
23837         var view = this.grid.view;
23838         view.on("refresh", this.onRefresh, this);
23839         view.on("rowupdated", this.onRowUpdated, this);
23840         view.on("rowremoved", this.onRemove, this);
23841         */
23842     },
23843
23844     // private
23845     onRefresh : function()
23846     {
23847         var ds = this.grid.store, i, v = this.grid.view;
23848         var s = this.selections;
23849         s.each(function(r){
23850             if((i = ds.indexOfId(r.id)) != -1){
23851                 v.onRowSelect(i);
23852             }else{
23853                 s.remove(r);
23854             }
23855         });
23856     },
23857
23858     // private
23859     onRemove : function(v, index, r){
23860         this.selections.remove(r);
23861     },
23862
23863     // private
23864     onRowUpdated : function(v, index, r){
23865         if(this.isSelected(r)){
23866             v.onRowSelect(index);
23867         }
23868     },
23869
23870     /**
23871      * Select records.
23872      * @param {Array} records The records to select
23873      * @param {Boolean} keepExisting (optional) True to keep existing selections
23874      */
23875     selectRecords : function(records, keepExisting)
23876     {
23877         if(!keepExisting){
23878             this.clearSelections();
23879         }
23880             var ds = this.grid.store;
23881         for(var i = 0, len = records.length; i < len; i++){
23882             this.selectRow(ds.indexOf(records[i]), true);
23883         }
23884     },
23885
23886     /**
23887      * Gets the number of selected rows.
23888      * @return {Number}
23889      */
23890     getCount : function(){
23891         return this.selections.length;
23892     },
23893
23894     /**
23895      * Selects the first row in the grid.
23896      */
23897     selectFirstRow : function(){
23898         this.selectRow(0);
23899     },
23900
23901     /**
23902      * Select the last row.
23903      * @param {Boolean} keepExisting (optional) True to keep existing selections
23904      */
23905     selectLastRow : function(keepExisting){
23906         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
23907         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
23908     },
23909
23910     /**
23911      * Selects the row immediately following the last selected row.
23912      * @param {Boolean} keepExisting (optional) True to keep existing selections
23913      */
23914     selectNext : function(keepExisting)
23915     {
23916             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
23917             this.selectRow(this.last+1, keepExisting);
23918             this.grid.getView().focusRow(this.last);
23919         }
23920     },
23921
23922     /**
23923      * Selects the row that precedes the last selected row.
23924      * @param {Boolean} keepExisting (optional) True to keep existing selections
23925      */
23926     selectPrevious : function(keepExisting){
23927         if(this.last){
23928             this.selectRow(this.last-1, keepExisting);
23929             this.grid.getView().focusRow(this.last);
23930         }
23931     },
23932
23933     /**
23934      * Returns the selected records
23935      * @return {Array} Array of selected records
23936      */
23937     getSelections : function(){
23938         return [].concat(this.selections.items);
23939     },
23940
23941     /**
23942      * Returns the first selected record.
23943      * @return {Record}
23944      */
23945     getSelected : function(){
23946         return this.selections.itemAt(0);
23947     },
23948
23949
23950     /**
23951      * Clears all selections.
23952      */
23953     clearSelections : function(fast)
23954     {
23955         if(this.locked) {
23956             return;
23957         }
23958         if(fast !== true){
23959                 var ds = this.grid.store;
23960             var s = this.selections;
23961             s.each(function(r){
23962                 this.deselectRow(ds.indexOfId(r.id));
23963             }, this);
23964             s.clear();
23965         }else{
23966             this.selections.clear();
23967         }
23968         this.last = false;
23969     },
23970
23971
23972     /**
23973      * Selects all rows.
23974      */
23975     selectAll : function(){
23976         if(this.locked) {
23977             return;
23978         }
23979         this.selections.clear();
23980         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
23981             this.selectRow(i, true);
23982         }
23983     },
23984
23985     /**
23986      * Returns True if there is a selection.
23987      * @return {Boolean}
23988      */
23989     hasSelection : function(){
23990         return this.selections.length > 0;
23991     },
23992
23993     /**
23994      * Returns True if the specified row is selected.
23995      * @param {Number/Record} record The record or index of the record to check
23996      * @return {Boolean}
23997      */
23998     isSelected : function(index){
23999             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24000         return (r && this.selections.key(r.id) ? true : false);
24001     },
24002
24003     /**
24004      * Returns True if the specified record id is selected.
24005      * @param {String} id The id of record to check
24006      * @return {Boolean}
24007      */
24008     isIdSelected : function(id){
24009         return (this.selections.key(id) ? true : false);
24010     },
24011
24012
24013     // private
24014     handleMouseDBClick : function(e, t){
24015         
24016     },
24017     // private
24018     handleMouseDown : function(e, t)
24019     {
24020             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24021         if(this.isLocked() || rowIndex < 0 ){
24022             return;
24023         };
24024         if(e.shiftKey && this.last !== false){
24025             var last = this.last;
24026             this.selectRange(last, rowIndex, e.ctrlKey);
24027             this.last = last; // reset the last
24028             t.focus();
24029     
24030         }else{
24031             var isSelected = this.isSelected(rowIndex);
24032             //Roo.log("select row:" + rowIndex);
24033             if(isSelected){
24034                 this.deselectRow(rowIndex);
24035             } else {
24036                         this.selectRow(rowIndex, true);
24037             }
24038     
24039             /*
24040                 if(e.button !== 0 && isSelected){
24041                 alert('rowIndex 2: ' + rowIndex);
24042                     view.focusRow(rowIndex);
24043                 }else if(e.ctrlKey && isSelected){
24044                     this.deselectRow(rowIndex);
24045                 }else if(!isSelected){
24046                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24047                     view.focusRow(rowIndex);
24048                 }
24049             */
24050         }
24051         this.fireEvent("afterselectionchange", this);
24052     },
24053     // private
24054     handleDragableRowClick :  function(grid, rowIndex, e) 
24055     {
24056         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24057             this.selectRow(rowIndex, false);
24058             grid.view.focusRow(rowIndex);
24059              this.fireEvent("afterselectionchange", this);
24060         }
24061     },
24062     
24063     /**
24064      * Selects multiple rows.
24065      * @param {Array} rows Array of the indexes of the row to select
24066      * @param {Boolean} keepExisting (optional) True to keep existing selections
24067      */
24068     selectRows : function(rows, keepExisting){
24069         if(!keepExisting){
24070             this.clearSelections();
24071         }
24072         for(var i = 0, len = rows.length; i < len; i++){
24073             this.selectRow(rows[i], true);
24074         }
24075     },
24076
24077     /**
24078      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24079      * @param {Number} startRow The index of the first row in the range
24080      * @param {Number} endRow The index of the last row in the range
24081      * @param {Boolean} keepExisting (optional) True to retain existing selections
24082      */
24083     selectRange : function(startRow, endRow, keepExisting){
24084         if(this.locked) {
24085             return;
24086         }
24087         if(!keepExisting){
24088             this.clearSelections();
24089         }
24090         if(startRow <= endRow){
24091             for(var i = startRow; i <= endRow; i++){
24092                 this.selectRow(i, true);
24093             }
24094         }else{
24095             for(var i = startRow; i >= endRow; i--){
24096                 this.selectRow(i, true);
24097             }
24098         }
24099     },
24100
24101     /**
24102      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24103      * @param {Number} startRow The index of the first row in the range
24104      * @param {Number} endRow The index of the last row in the range
24105      */
24106     deselectRange : function(startRow, endRow, preventViewNotify){
24107         if(this.locked) {
24108             return;
24109         }
24110         for(var i = startRow; i <= endRow; i++){
24111             this.deselectRow(i, preventViewNotify);
24112         }
24113     },
24114
24115     /**
24116      * Selects a row.
24117      * @param {Number} row The index of the row to select
24118      * @param {Boolean} keepExisting (optional) True to keep existing selections
24119      */
24120     selectRow : function(index, keepExisting, preventViewNotify)
24121     {
24122             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24123             return;
24124         }
24125         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24126             if(!keepExisting || this.singleSelect){
24127                 this.clearSelections();
24128             }
24129             
24130             var r = this.grid.store.getAt(index);
24131             //console.log('selectRow - record id :' + r.id);
24132             
24133             this.selections.add(r);
24134             this.last = this.lastActive = index;
24135             if(!preventViewNotify){
24136                 var proxy = new Roo.Element(
24137                                 this.grid.getRowDom(index)
24138                 );
24139                 proxy.addClass('bg-info info');
24140             }
24141             this.fireEvent("rowselect", this, index, r);
24142             this.fireEvent("selectionchange", this);
24143         }
24144     },
24145
24146     /**
24147      * Deselects a row.
24148      * @param {Number} row The index of the row to deselect
24149      */
24150     deselectRow : function(index, preventViewNotify)
24151     {
24152         if(this.locked) {
24153             return;
24154         }
24155         if(this.last == index){
24156             this.last = false;
24157         }
24158         if(this.lastActive == index){
24159             this.lastActive = false;
24160         }
24161         
24162         var r = this.grid.store.getAt(index);
24163         if (!r) {
24164             return;
24165         }
24166         
24167         this.selections.remove(r);
24168         //.console.log('deselectRow - record id :' + r.id);
24169         if(!preventViewNotify){
24170         
24171             var proxy = new Roo.Element(
24172                 this.grid.getRowDom(index)
24173             );
24174             proxy.removeClass('bg-info info');
24175         }
24176         this.fireEvent("rowdeselect", this, index);
24177         this.fireEvent("selectionchange", this);
24178     },
24179
24180     // private
24181     restoreLast : function(){
24182         if(this._last){
24183             this.last = this._last;
24184         }
24185     },
24186
24187     // private
24188     acceptsNav : function(row, col, cm){
24189         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24190     },
24191
24192     // private
24193     onEditorKey : function(field, e){
24194         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24195         if(k == e.TAB){
24196             e.stopEvent();
24197             ed.completeEdit();
24198             if(e.shiftKey){
24199                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24200             }else{
24201                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24202             }
24203         }else if(k == e.ENTER && !e.ctrlKey){
24204             e.stopEvent();
24205             ed.completeEdit();
24206             if(e.shiftKey){
24207                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24208             }else{
24209                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24210             }
24211         }else if(k == e.ESC){
24212             ed.cancelEdit();
24213         }
24214         if(newCell){
24215             g.startEditing(newCell[0], newCell[1]);
24216         }
24217     }
24218 });
24219 /*
24220  * Based on:
24221  * Ext JS Library 1.1.1
24222  * Copyright(c) 2006-2007, Ext JS, LLC.
24223  *
24224  * Originally Released Under LGPL - original licence link has changed is not relivant.
24225  *
24226  * Fork - LGPL
24227  * <script type="text/javascript">
24228  */
24229  
24230 /**
24231  * @class Roo.bootstrap.PagingToolbar
24232  * @extends Roo.bootstrap.NavSimplebar
24233  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24234  * @constructor
24235  * Create a new PagingToolbar
24236  * @param {Object} config The config object
24237  * @param {Roo.data.Store} store
24238  */
24239 Roo.bootstrap.PagingToolbar = function(config)
24240 {
24241     // old args format still supported... - xtype is prefered..
24242         // created from xtype...
24243     
24244     this.ds = config.dataSource;
24245     
24246     if (config.store && !this.ds) {
24247         this.store= Roo.factory(config.store, Roo.data);
24248         this.ds = this.store;
24249         this.ds.xmodule = this.xmodule || false;
24250     }
24251     
24252     this.toolbarItems = [];
24253     if (config.items) {
24254         this.toolbarItems = config.items;
24255     }
24256     
24257     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24258     
24259     this.cursor = 0;
24260     
24261     if (this.ds) { 
24262         this.bind(this.ds);
24263     }
24264     
24265     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24266     
24267 };
24268
24269 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24270     /**
24271      * @cfg {Roo.data.Store} dataSource
24272      * The underlying data store providing the paged data
24273      */
24274     /**
24275      * @cfg {String/HTMLElement/Element} container
24276      * container The id or element that will contain the toolbar
24277      */
24278     /**
24279      * @cfg {Boolean} displayInfo
24280      * True to display the displayMsg (defaults to false)
24281      */
24282     /**
24283      * @cfg {Number} pageSize
24284      * The number of records to display per page (defaults to 20)
24285      */
24286     pageSize: 20,
24287     /**
24288      * @cfg {String} displayMsg
24289      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24290      */
24291     displayMsg : 'Displaying {0} - {1} of {2}',
24292     /**
24293      * @cfg {String} emptyMsg
24294      * The message to display when no records are found (defaults to "No data to display")
24295      */
24296     emptyMsg : 'No data to display',
24297     /**
24298      * Customizable piece of the default paging text (defaults to "Page")
24299      * @type String
24300      */
24301     beforePageText : "Page",
24302     /**
24303      * Customizable piece of the default paging text (defaults to "of %0")
24304      * @type String
24305      */
24306     afterPageText : "of {0}",
24307     /**
24308      * Customizable piece of the default paging text (defaults to "First Page")
24309      * @type String
24310      */
24311     firstText : "First Page",
24312     /**
24313      * Customizable piece of the default paging text (defaults to "Previous Page")
24314      * @type String
24315      */
24316     prevText : "Previous Page",
24317     /**
24318      * Customizable piece of the default paging text (defaults to "Next Page")
24319      * @type String
24320      */
24321     nextText : "Next Page",
24322     /**
24323      * Customizable piece of the default paging text (defaults to "Last Page")
24324      * @type String
24325      */
24326     lastText : "Last Page",
24327     /**
24328      * Customizable piece of the default paging text (defaults to "Refresh")
24329      * @type String
24330      */
24331     refreshText : "Refresh",
24332
24333     buttons : false,
24334     // private
24335     onRender : function(ct, position) 
24336     {
24337         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24338         this.navgroup.parentId = this.id;
24339         this.navgroup.onRender(this.el, null);
24340         // add the buttons to the navgroup
24341         
24342         if(this.displayInfo){
24343             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24344             this.displayEl = this.el.select('.x-paging-info', true).first();
24345 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24346 //            this.displayEl = navel.el.select('span',true).first();
24347         }
24348         
24349         var _this = this;
24350         
24351         if(this.buttons){
24352             Roo.each(_this.buttons, function(e){ // this might need to use render????
24353                Roo.factory(e).onRender(_this.el, null);
24354             });
24355         }
24356             
24357         Roo.each(_this.toolbarItems, function(e) {
24358             _this.navgroup.addItem(e);
24359         });
24360         
24361         
24362         this.first = this.navgroup.addItem({
24363             tooltip: this.firstText,
24364             cls: "prev",
24365             icon : 'fa fa-backward',
24366             disabled: true,
24367             preventDefault: true,
24368             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24369         });
24370         
24371         this.prev =  this.navgroup.addItem({
24372             tooltip: this.prevText,
24373             cls: "prev",
24374             icon : 'fa fa-step-backward',
24375             disabled: true,
24376             preventDefault: true,
24377             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24378         });
24379     //this.addSeparator();
24380         
24381         
24382         var field = this.navgroup.addItem( {
24383             tagtype : 'span',
24384             cls : 'x-paging-position',
24385             
24386             html : this.beforePageText  +
24387                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24388                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24389          } ); //?? escaped?
24390         
24391         this.field = field.el.select('input', true).first();
24392         this.field.on("keydown", this.onPagingKeydown, this);
24393         this.field.on("focus", function(){this.dom.select();});
24394     
24395     
24396         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24397         //this.field.setHeight(18);
24398         //this.addSeparator();
24399         this.next = this.navgroup.addItem({
24400             tooltip: this.nextText,
24401             cls: "next",
24402             html : ' <i class="fa fa-step-forward">',
24403             disabled: true,
24404             preventDefault: true,
24405             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24406         });
24407         this.last = this.navgroup.addItem({
24408             tooltip: this.lastText,
24409             icon : 'fa fa-forward',
24410             cls: "next",
24411             disabled: true,
24412             preventDefault: true,
24413             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24414         });
24415     //this.addSeparator();
24416         this.loading = this.navgroup.addItem({
24417             tooltip: this.refreshText,
24418             icon: 'fa fa-refresh',
24419             preventDefault: true,
24420             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24421         });
24422         
24423     },
24424
24425     // private
24426     updateInfo : function(){
24427         if(this.displayEl){
24428             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24429             var msg = count == 0 ?
24430                 this.emptyMsg :
24431                 String.format(
24432                     this.displayMsg,
24433                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24434                 );
24435             this.displayEl.update(msg);
24436         }
24437     },
24438
24439     // private
24440     onLoad : function(ds, r, o)
24441     {
24442         this.cursor = o.params ? o.params.start : 0;
24443         var d = this.getPageData(),
24444             ap = d.activePage,
24445             ps = d.pages;
24446         
24447         
24448         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24449         this.field.dom.value = ap;
24450         this.first.setDisabled(ap == 1);
24451         this.prev.setDisabled(ap == 1);
24452         this.next.setDisabled(ap == ps);
24453         this.last.setDisabled(ap == ps);
24454         this.loading.enable();
24455         this.updateInfo();
24456     },
24457
24458     // private
24459     getPageData : function(){
24460         var total = this.ds.getTotalCount();
24461         return {
24462             total : total,
24463             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24464             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24465         };
24466     },
24467
24468     // private
24469     onLoadError : function(){
24470         this.loading.enable();
24471     },
24472
24473     // private
24474     onPagingKeydown : function(e){
24475         var k = e.getKey();
24476         var d = this.getPageData();
24477         if(k == e.RETURN){
24478             var v = this.field.dom.value, pageNum;
24479             if(!v || isNaN(pageNum = parseInt(v, 10))){
24480                 this.field.dom.value = d.activePage;
24481                 return;
24482             }
24483             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24484             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24485             e.stopEvent();
24486         }
24487         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))
24488         {
24489           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24490           this.field.dom.value = pageNum;
24491           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24492           e.stopEvent();
24493         }
24494         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24495         {
24496           var v = this.field.dom.value, pageNum; 
24497           var increment = (e.shiftKey) ? 10 : 1;
24498           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24499                 increment *= -1;
24500           }
24501           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24502             this.field.dom.value = d.activePage;
24503             return;
24504           }
24505           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24506           {
24507             this.field.dom.value = parseInt(v, 10) + increment;
24508             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24509             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24510           }
24511           e.stopEvent();
24512         }
24513     },
24514
24515     // private
24516     beforeLoad : function(){
24517         if(this.loading){
24518             this.loading.disable();
24519         }
24520     },
24521
24522     // private
24523     onClick : function(which){
24524         
24525         var ds = this.ds;
24526         if (!ds) {
24527             return;
24528         }
24529         
24530         switch(which){
24531             case "first":
24532                 ds.load({params:{start: 0, limit: this.pageSize}});
24533             break;
24534             case "prev":
24535                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24536             break;
24537             case "next":
24538                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24539             break;
24540             case "last":
24541                 var total = ds.getTotalCount();
24542                 var extra = total % this.pageSize;
24543                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24544                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24545             break;
24546             case "refresh":
24547                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24548             break;
24549         }
24550     },
24551
24552     /**
24553      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24554      * @param {Roo.data.Store} store The data store to unbind
24555      */
24556     unbind : function(ds){
24557         ds.un("beforeload", this.beforeLoad, this);
24558         ds.un("load", this.onLoad, this);
24559         ds.un("loadexception", this.onLoadError, this);
24560         ds.un("remove", this.updateInfo, this);
24561         ds.un("add", this.updateInfo, this);
24562         this.ds = undefined;
24563     },
24564
24565     /**
24566      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24567      * @param {Roo.data.Store} store The data store to bind
24568      */
24569     bind : function(ds){
24570         ds.on("beforeload", this.beforeLoad, this);
24571         ds.on("load", this.onLoad, this);
24572         ds.on("loadexception", this.onLoadError, this);
24573         ds.on("remove", this.updateInfo, this);
24574         ds.on("add", this.updateInfo, this);
24575         this.ds = ds;
24576     }
24577 });/*
24578  * - LGPL
24579  *
24580  * element
24581  * 
24582  */
24583
24584 /**
24585  * @class Roo.bootstrap.MessageBar
24586  * @extends Roo.bootstrap.Component
24587  * Bootstrap MessageBar class
24588  * @cfg {String} html contents of the MessageBar
24589  * @cfg {String} weight (info | success | warning | danger) default info
24590  * @cfg {String} beforeClass insert the bar before the given class
24591  * @cfg {Boolean} closable (true | false) default false
24592  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24593  * 
24594  * @constructor
24595  * Create a new Element
24596  * @param {Object} config The config object
24597  */
24598
24599 Roo.bootstrap.MessageBar = function(config){
24600     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24601 };
24602
24603 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24604     
24605     html: '',
24606     weight: 'info',
24607     closable: false,
24608     fixed: false,
24609     beforeClass: 'bootstrap-sticky-wrap',
24610     
24611     getAutoCreate : function(){
24612         
24613         var cfg = {
24614             tag: 'div',
24615             cls: 'alert alert-dismissable alert-' + this.weight,
24616             cn: [
24617                 {
24618                     tag: 'span',
24619                     cls: 'message',
24620                     html: this.html || ''
24621                 }
24622             ]
24623         };
24624         
24625         if(this.fixed){
24626             cfg.cls += ' alert-messages-fixed';
24627         }
24628         
24629         if(this.closable){
24630             cfg.cn.push({
24631                 tag: 'button',
24632                 cls: 'close',
24633                 html: 'x'
24634             });
24635         }
24636         
24637         return cfg;
24638     },
24639     
24640     onRender : function(ct, position)
24641     {
24642         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24643         
24644         if(!this.el){
24645             var cfg = Roo.apply({},  this.getAutoCreate());
24646             cfg.id = Roo.id();
24647             
24648             if (this.cls) {
24649                 cfg.cls += ' ' + this.cls;
24650             }
24651             if (this.style) {
24652                 cfg.style = this.style;
24653             }
24654             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24655             
24656             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24657         }
24658         
24659         this.el.select('>button.close').on('click', this.hide, this);
24660         
24661     },
24662     
24663     show : function()
24664     {
24665         if (!this.rendered) {
24666             this.render();
24667         }
24668         
24669         this.el.show();
24670         
24671         this.fireEvent('show', this);
24672         
24673     },
24674     
24675     hide : function()
24676     {
24677         if (!this.rendered) {
24678             this.render();
24679         }
24680         
24681         this.el.hide();
24682         
24683         this.fireEvent('hide', this);
24684     },
24685     
24686     update : function()
24687     {
24688 //        var e = this.el.dom.firstChild;
24689 //        
24690 //        if(this.closable){
24691 //            e = e.nextSibling;
24692 //        }
24693 //        
24694 //        e.data = this.html || '';
24695
24696         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24697     }
24698    
24699 });
24700
24701  
24702
24703      /*
24704  * - LGPL
24705  *
24706  * Graph
24707  * 
24708  */
24709
24710
24711 /**
24712  * @class Roo.bootstrap.Graph
24713  * @extends Roo.bootstrap.Component
24714  * Bootstrap Graph class
24715 > Prameters
24716  -sm {number} sm 4
24717  -md {number} md 5
24718  @cfg {String} graphtype  bar | vbar | pie
24719  @cfg {number} g_x coodinator | centre x (pie)
24720  @cfg {number} g_y coodinator | centre y (pie)
24721  @cfg {number} g_r radius (pie)
24722  @cfg {number} g_height height of the chart (respected by all elements in the set)
24723  @cfg {number} g_width width of the chart (respected by all elements in the set)
24724  @cfg {Object} title The title of the chart
24725     
24726  -{Array}  values
24727  -opts (object) options for the chart 
24728      o {
24729      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24730      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24731      o vgutter (number)
24732      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.
24733      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24734      o to
24735      o stretch (boolean)
24736      o }
24737  -opts (object) options for the pie
24738      o{
24739      o cut
24740      o startAngle (number)
24741      o endAngle (number)
24742      } 
24743  *
24744  * @constructor
24745  * Create a new Input
24746  * @param {Object} config The config object
24747  */
24748
24749 Roo.bootstrap.Graph = function(config){
24750     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24751     
24752     this.addEvents({
24753         // img events
24754         /**
24755          * @event click
24756          * The img click event for the img.
24757          * @param {Roo.EventObject} e
24758          */
24759         "click" : true
24760     });
24761 };
24762
24763 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
24764     
24765     sm: 4,
24766     md: 5,
24767     graphtype: 'bar',
24768     g_height: 250,
24769     g_width: 400,
24770     g_x: 50,
24771     g_y: 50,
24772     g_r: 30,
24773     opts:{
24774         //g_colors: this.colors,
24775         g_type: 'soft',
24776         g_gutter: '20%'
24777
24778     },
24779     title : false,
24780
24781     getAutoCreate : function(){
24782         
24783         var cfg = {
24784             tag: 'div',
24785             html : null
24786         };
24787         
24788         
24789         return  cfg;
24790     },
24791
24792     onRender : function(ct,position){
24793         
24794         
24795         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24796         
24797         if (typeof(Raphael) == 'undefined') {
24798             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24799             return;
24800         }
24801         
24802         this.raphael = Raphael(this.el.dom);
24803         
24804                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24805                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24806                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24807                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24808                 /*
24809                 r.text(160, 10, "Single Series Chart").attr(txtattr);
24810                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24811                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24812                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
24813                 
24814                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
24815                 r.barchart(330, 10, 300, 220, data1);
24816                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
24817                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
24818                 */
24819                 
24820                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24821                 // r.barchart(30, 30, 560, 250,  xdata, {
24822                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
24823                 //     axis : "0 0 1 1",
24824                 //     axisxlabels :  xdata
24825                 //     //yvalues : cols,
24826                    
24827                 // });
24828 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24829 //        
24830 //        this.load(null,xdata,{
24831 //                axis : "0 0 1 1",
24832 //                axisxlabels :  xdata
24833 //                });
24834
24835     },
24836
24837     load : function(graphtype,xdata,opts)
24838     {
24839         this.raphael.clear();
24840         if(!graphtype) {
24841             graphtype = this.graphtype;
24842         }
24843         if(!opts){
24844             opts = this.opts;
24845         }
24846         var r = this.raphael,
24847             fin = function () {
24848                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
24849             },
24850             fout = function () {
24851                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
24852             },
24853             pfin = function() {
24854                 this.sector.stop();
24855                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
24856
24857                 if (this.label) {
24858                     this.label[0].stop();
24859                     this.label[0].attr({ r: 7.5 });
24860                     this.label[1].attr({ "font-weight": 800 });
24861                 }
24862             },
24863             pfout = function() {
24864                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
24865
24866                 if (this.label) {
24867                     this.label[0].animate({ r: 5 }, 500, "bounce");
24868                     this.label[1].attr({ "font-weight": 400 });
24869                 }
24870             };
24871
24872         switch(graphtype){
24873             case 'bar':
24874                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24875                 break;
24876             case 'hbar':
24877                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24878                 break;
24879             case 'pie':
24880 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
24881 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
24882 //            
24883                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
24884                 
24885                 break;
24886
24887         }
24888         
24889         if(this.title){
24890             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
24891         }
24892         
24893     },
24894     
24895     setTitle: function(o)
24896     {
24897         this.title = o;
24898     },
24899     
24900     initEvents: function() {
24901         
24902         if(!this.href){
24903             this.el.on('click', this.onClick, this);
24904         }
24905     },
24906     
24907     onClick : function(e)
24908     {
24909         Roo.log('img onclick');
24910         this.fireEvent('click', this, e);
24911     }
24912    
24913 });
24914
24915  
24916 /*
24917  * - LGPL
24918  *
24919  * numberBox
24920  * 
24921  */
24922 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24923
24924 /**
24925  * @class Roo.bootstrap.dash.NumberBox
24926  * @extends Roo.bootstrap.Component
24927  * Bootstrap NumberBox class
24928  * @cfg {String} headline Box headline
24929  * @cfg {String} content Box content
24930  * @cfg {String} icon Box icon
24931  * @cfg {String} footer Footer text
24932  * @cfg {String} fhref Footer href
24933  * 
24934  * @constructor
24935  * Create a new NumberBox
24936  * @param {Object} config The config object
24937  */
24938
24939
24940 Roo.bootstrap.dash.NumberBox = function(config){
24941     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
24942     
24943 };
24944
24945 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
24946     
24947     headline : '',
24948     content : '',
24949     icon : '',
24950     footer : '',
24951     fhref : '',
24952     ficon : '',
24953     
24954     getAutoCreate : function(){
24955         
24956         var cfg = {
24957             tag : 'div',
24958             cls : 'small-box ',
24959             cn : [
24960                 {
24961                     tag : 'div',
24962                     cls : 'inner',
24963                     cn :[
24964                         {
24965                             tag : 'h3',
24966                             cls : 'roo-headline',
24967                             html : this.headline
24968                         },
24969                         {
24970                             tag : 'p',
24971                             cls : 'roo-content',
24972                             html : this.content
24973                         }
24974                     ]
24975                 }
24976             ]
24977         };
24978         
24979         if(this.icon){
24980             cfg.cn.push({
24981                 tag : 'div',
24982                 cls : 'icon',
24983                 cn :[
24984                     {
24985                         tag : 'i',
24986                         cls : 'ion ' + this.icon
24987                     }
24988                 ]
24989             });
24990         }
24991         
24992         if(this.footer){
24993             var footer = {
24994                 tag : 'a',
24995                 cls : 'small-box-footer',
24996                 href : this.fhref || '#',
24997                 html : this.footer
24998             };
24999             
25000             cfg.cn.push(footer);
25001             
25002         }
25003         
25004         return  cfg;
25005     },
25006
25007     onRender : function(ct,position){
25008         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25009
25010
25011        
25012                 
25013     },
25014
25015     setHeadline: function (value)
25016     {
25017         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25018     },
25019     
25020     setFooter: function (value, href)
25021     {
25022         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25023         
25024         if(href){
25025             this.el.select('a.small-box-footer',true).first().attr('href', href);
25026         }
25027         
25028     },
25029
25030     setContent: function (value)
25031     {
25032         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25033     },
25034
25035     initEvents: function() 
25036     {   
25037         
25038     }
25039     
25040 });
25041
25042  
25043 /*
25044  * - LGPL
25045  *
25046  * TabBox
25047  * 
25048  */
25049 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25050
25051 /**
25052  * @class Roo.bootstrap.dash.TabBox
25053  * @extends Roo.bootstrap.Component
25054  * Bootstrap TabBox class
25055  * @cfg {String} title Title of the TabBox
25056  * @cfg {String} icon Icon of the TabBox
25057  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25058  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25059  * 
25060  * @constructor
25061  * Create a new TabBox
25062  * @param {Object} config The config object
25063  */
25064
25065
25066 Roo.bootstrap.dash.TabBox = function(config){
25067     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25068     this.addEvents({
25069         // raw events
25070         /**
25071          * @event addpane
25072          * When a pane is added
25073          * @param {Roo.bootstrap.dash.TabPane} pane
25074          */
25075         "addpane" : true,
25076         /**
25077          * @event activatepane
25078          * When a pane is activated
25079          * @param {Roo.bootstrap.dash.TabPane} pane
25080          */
25081         "activatepane" : true
25082         
25083          
25084     });
25085     
25086     this.panes = [];
25087 };
25088
25089 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25090
25091     title : '',
25092     icon : false,
25093     showtabs : true,
25094     tabScrollable : false,
25095     
25096     getChildContainer : function()
25097     {
25098         return this.el.select('.tab-content', true).first();
25099     },
25100     
25101     getAutoCreate : function(){
25102         
25103         var header = {
25104             tag: 'li',
25105             cls: 'pull-left header',
25106             html: this.title,
25107             cn : []
25108         };
25109         
25110         if(this.icon){
25111             header.cn.push({
25112                 tag: 'i',
25113                 cls: 'fa ' + this.icon
25114             });
25115         }
25116         
25117         var h = {
25118             tag: 'ul',
25119             cls: 'nav nav-tabs pull-right',
25120             cn: [
25121                 header
25122             ]
25123         };
25124         
25125         if(this.tabScrollable){
25126             h = {
25127                 tag: 'div',
25128                 cls: 'tab-header',
25129                 cn: [
25130                     {
25131                         tag: 'ul',
25132                         cls: 'nav nav-tabs pull-right',
25133                         cn: [
25134                             header
25135                         ]
25136                     }
25137                 ]
25138             };
25139         }
25140         
25141         var cfg = {
25142             tag: 'div',
25143             cls: 'nav-tabs-custom',
25144             cn: [
25145                 h,
25146                 {
25147                     tag: 'div',
25148                     cls: 'tab-content no-padding',
25149                     cn: []
25150                 }
25151             ]
25152         };
25153
25154         return  cfg;
25155     },
25156     initEvents : function()
25157     {
25158         //Roo.log('add add pane handler');
25159         this.on('addpane', this.onAddPane, this);
25160     },
25161      /**
25162      * Updates the box title
25163      * @param {String} html to set the title to.
25164      */
25165     setTitle : function(value)
25166     {
25167         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25168     },
25169     onAddPane : function(pane)
25170     {
25171         this.panes.push(pane);
25172         //Roo.log('addpane');
25173         //Roo.log(pane);
25174         // tabs are rendere left to right..
25175         if(!this.showtabs){
25176             return;
25177         }
25178         
25179         var ctr = this.el.select('.nav-tabs', true).first();
25180          
25181          
25182         var existing = ctr.select('.nav-tab',true);
25183         var qty = existing.getCount();;
25184         
25185         
25186         var tab = ctr.createChild({
25187             tag : 'li',
25188             cls : 'nav-tab' + (qty ? '' : ' active'),
25189             cn : [
25190                 {
25191                     tag : 'a',
25192                     href:'#',
25193                     html : pane.title
25194                 }
25195             ]
25196         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25197         pane.tab = tab;
25198         
25199         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25200         if (!qty) {
25201             pane.el.addClass('active');
25202         }
25203         
25204                 
25205     },
25206     onTabClick : function(ev,un,ob,pane)
25207     {
25208         //Roo.log('tab - prev default');
25209         ev.preventDefault();
25210         
25211         
25212         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25213         pane.tab.addClass('active');
25214         //Roo.log(pane.title);
25215         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25216         // technically we should have a deactivate event.. but maybe add later.
25217         // and it should not de-activate the selected tab...
25218         this.fireEvent('activatepane', pane);
25219         pane.el.addClass('active');
25220         pane.fireEvent('activate');
25221         
25222         
25223     },
25224     
25225     getActivePane : function()
25226     {
25227         var r = false;
25228         Roo.each(this.panes, function(p) {
25229             if(p.el.hasClass('active')){
25230                 r = p;
25231                 return false;
25232             }
25233             
25234             return;
25235         });
25236         
25237         return r;
25238     }
25239     
25240     
25241 });
25242
25243  
25244 /*
25245  * - LGPL
25246  *
25247  * Tab pane
25248  * 
25249  */
25250 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25251 /**
25252  * @class Roo.bootstrap.TabPane
25253  * @extends Roo.bootstrap.Component
25254  * Bootstrap TabPane class
25255  * @cfg {Boolean} active (false | true) Default false
25256  * @cfg {String} title title of panel
25257
25258  * 
25259  * @constructor
25260  * Create a new TabPane
25261  * @param {Object} config The config object
25262  */
25263
25264 Roo.bootstrap.dash.TabPane = function(config){
25265     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25266     
25267     this.addEvents({
25268         // raw events
25269         /**
25270          * @event activate
25271          * When a pane is activated
25272          * @param {Roo.bootstrap.dash.TabPane} pane
25273          */
25274         "activate" : true
25275          
25276     });
25277 };
25278
25279 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25280     
25281     active : false,
25282     title : '',
25283     
25284     // the tabBox that this is attached to.
25285     tab : false,
25286      
25287     getAutoCreate : function() 
25288     {
25289         var cfg = {
25290             tag: 'div',
25291             cls: 'tab-pane'
25292         };
25293         
25294         if(this.active){
25295             cfg.cls += ' active';
25296         }
25297         
25298         return cfg;
25299     },
25300     initEvents  : function()
25301     {
25302         //Roo.log('trigger add pane handler');
25303         this.parent().fireEvent('addpane', this)
25304     },
25305     
25306      /**
25307      * Updates the tab title 
25308      * @param {String} html to set the title to.
25309      */
25310     setTitle: function(str)
25311     {
25312         if (!this.tab) {
25313             return;
25314         }
25315         this.title = str;
25316         this.tab.select('a', true).first().dom.innerHTML = str;
25317         
25318     }
25319     
25320     
25321     
25322 });
25323
25324  
25325
25326
25327  /*
25328  * - LGPL
25329  *
25330  * menu
25331  * 
25332  */
25333 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25334
25335 /**
25336  * @class Roo.bootstrap.menu.Menu
25337  * @extends Roo.bootstrap.Component
25338  * Bootstrap Menu class - container for Menu
25339  * @cfg {String} html Text of the menu
25340  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25341  * @cfg {String} icon Font awesome icon
25342  * @cfg {String} pos Menu align to (top | bottom) default bottom
25343  * 
25344  * 
25345  * @constructor
25346  * Create a new Menu
25347  * @param {Object} config The config object
25348  */
25349
25350
25351 Roo.bootstrap.menu.Menu = function(config){
25352     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25353     
25354     this.addEvents({
25355         /**
25356          * @event beforeshow
25357          * Fires before this menu is displayed
25358          * @param {Roo.bootstrap.menu.Menu} this
25359          */
25360         beforeshow : true,
25361         /**
25362          * @event beforehide
25363          * Fires before this menu is hidden
25364          * @param {Roo.bootstrap.menu.Menu} this
25365          */
25366         beforehide : true,
25367         /**
25368          * @event show
25369          * Fires after this menu is displayed
25370          * @param {Roo.bootstrap.menu.Menu} this
25371          */
25372         show : true,
25373         /**
25374          * @event hide
25375          * Fires after this menu is hidden
25376          * @param {Roo.bootstrap.menu.Menu} this
25377          */
25378         hide : true,
25379         /**
25380          * @event click
25381          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25382          * @param {Roo.bootstrap.menu.Menu} this
25383          * @param {Roo.EventObject} e
25384          */
25385         click : true
25386     });
25387     
25388 };
25389
25390 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25391     
25392     submenu : false,
25393     html : '',
25394     weight : 'default',
25395     icon : false,
25396     pos : 'bottom',
25397     
25398     
25399     getChildContainer : function() {
25400         if(this.isSubMenu){
25401             return this.el;
25402         }
25403         
25404         return this.el.select('ul.dropdown-menu', true).first();  
25405     },
25406     
25407     getAutoCreate : function()
25408     {
25409         var text = [
25410             {
25411                 tag : 'span',
25412                 cls : 'roo-menu-text',
25413                 html : this.html
25414             }
25415         ];
25416         
25417         if(this.icon){
25418             text.unshift({
25419                 tag : 'i',
25420                 cls : 'fa ' + this.icon
25421             })
25422         }
25423         
25424         
25425         var cfg = {
25426             tag : 'div',
25427             cls : 'btn-group',
25428             cn : [
25429                 {
25430                     tag : 'button',
25431                     cls : 'dropdown-button btn btn-' + this.weight,
25432                     cn : text
25433                 },
25434                 {
25435                     tag : 'button',
25436                     cls : 'dropdown-toggle btn btn-' + this.weight,
25437                     cn : [
25438                         {
25439                             tag : 'span',
25440                             cls : 'caret'
25441                         }
25442                     ]
25443                 },
25444                 {
25445                     tag : 'ul',
25446                     cls : 'dropdown-menu'
25447                 }
25448             ]
25449             
25450         };
25451         
25452         if(this.pos == 'top'){
25453             cfg.cls += ' dropup';
25454         }
25455         
25456         if(this.isSubMenu){
25457             cfg = {
25458                 tag : 'ul',
25459                 cls : 'dropdown-menu'
25460             }
25461         }
25462         
25463         return cfg;
25464     },
25465     
25466     onRender : function(ct, position)
25467     {
25468         this.isSubMenu = ct.hasClass('dropdown-submenu');
25469         
25470         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25471     },
25472     
25473     initEvents : function() 
25474     {
25475         if(this.isSubMenu){
25476             return;
25477         }
25478         
25479         this.hidden = true;
25480         
25481         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25482         this.triggerEl.on('click', this.onTriggerPress, this);
25483         
25484         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25485         this.buttonEl.on('click', this.onClick, this);
25486         
25487     },
25488     
25489     list : function()
25490     {
25491         if(this.isSubMenu){
25492             return this.el;
25493         }
25494         
25495         return this.el.select('ul.dropdown-menu', true).first();
25496     },
25497     
25498     onClick : function(e)
25499     {
25500         this.fireEvent("click", this, e);
25501     },
25502     
25503     onTriggerPress  : function(e)
25504     {   
25505         if (this.isVisible()) {
25506             this.hide();
25507         } else {
25508             this.show();
25509         }
25510     },
25511     
25512     isVisible : function(){
25513         return !this.hidden;
25514     },
25515     
25516     show : function()
25517     {
25518         this.fireEvent("beforeshow", this);
25519         
25520         this.hidden = false;
25521         this.el.addClass('open');
25522         
25523         Roo.get(document).on("mouseup", this.onMouseUp, this);
25524         
25525         this.fireEvent("show", this);
25526         
25527         
25528     },
25529     
25530     hide : function()
25531     {
25532         this.fireEvent("beforehide", this);
25533         
25534         this.hidden = true;
25535         this.el.removeClass('open');
25536         
25537         Roo.get(document).un("mouseup", this.onMouseUp);
25538         
25539         this.fireEvent("hide", this);
25540     },
25541     
25542     onMouseUp : function()
25543     {
25544         this.hide();
25545     }
25546     
25547 });
25548
25549  
25550  /*
25551  * - LGPL
25552  *
25553  * menu item
25554  * 
25555  */
25556 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25557
25558 /**
25559  * @class Roo.bootstrap.menu.Item
25560  * @extends Roo.bootstrap.Component
25561  * Bootstrap MenuItem class
25562  * @cfg {Boolean} submenu (true | false) default false
25563  * @cfg {String} html text of the item
25564  * @cfg {String} href the link
25565  * @cfg {Boolean} disable (true | false) default false
25566  * @cfg {Boolean} preventDefault (true | false) default true
25567  * @cfg {String} icon Font awesome icon
25568  * @cfg {String} pos Submenu align to (left | right) default right 
25569  * 
25570  * 
25571  * @constructor
25572  * Create a new Item
25573  * @param {Object} config The config object
25574  */
25575
25576
25577 Roo.bootstrap.menu.Item = function(config){
25578     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25579     this.addEvents({
25580         /**
25581          * @event mouseover
25582          * Fires when the mouse is hovering over this menu
25583          * @param {Roo.bootstrap.menu.Item} this
25584          * @param {Roo.EventObject} e
25585          */
25586         mouseover : true,
25587         /**
25588          * @event mouseout
25589          * Fires when the mouse exits this menu
25590          * @param {Roo.bootstrap.menu.Item} this
25591          * @param {Roo.EventObject} e
25592          */
25593         mouseout : true,
25594         // raw events
25595         /**
25596          * @event click
25597          * The raw click event for the entire grid.
25598          * @param {Roo.EventObject} e
25599          */
25600         click : true
25601     });
25602 };
25603
25604 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25605     
25606     submenu : false,
25607     href : '',
25608     html : '',
25609     preventDefault: true,
25610     disable : false,
25611     icon : false,
25612     pos : 'right',
25613     
25614     getAutoCreate : function()
25615     {
25616         var text = [
25617             {
25618                 tag : 'span',
25619                 cls : 'roo-menu-item-text',
25620                 html : this.html
25621             }
25622         ];
25623         
25624         if(this.icon){
25625             text.unshift({
25626                 tag : 'i',
25627                 cls : 'fa ' + this.icon
25628             })
25629         }
25630         
25631         var cfg = {
25632             tag : 'li',
25633             cn : [
25634                 {
25635                     tag : 'a',
25636                     href : this.href || '#',
25637                     cn : text
25638                 }
25639             ]
25640         };
25641         
25642         if(this.disable){
25643             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25644         }
25645         
25646         if(this.submenu){
25647             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25648             
25649             if(this.pos == 'left'){
25650                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25651             }
25652         }
25653         
25654         return cfg;
25655     },
25656     
25657     initEvents : function() 
25658     {
25659         this.el.on('mouseover', this.onMouseOver, this);
25660         this.el.on('mouseout', this.onMouseOut, this);
25661         
25662         this.el.select('a', true).first().on('click', this.onClick, this);
25663         
25664     },
25665     
25666     onClick : function(e)
25667     {
25668         if(this.preventDefault){
25669             e.preventDefault();
25670         }
25671         
25672         this.fireEvent("click", this, e);
25673     },
25674     
25675     onMouseOver : function(e)
25676     {
25677         if(this.submenu && this.pos == 'left'){
25678             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25679         }
25680         
25681         this.fireEvent("mouseover", this, e);
25682     },
25683     
25684     onMouseOut : function(e)
25685     {
25686         this.fireEvent("mouseout", this, e);
25687     }
25688 });
25689
25690  
25691
25692  /*
25693  * - LGPL
25694  *
25695  * menu separator
25696  * 
25697  */
25698 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25699
25700 /**
25701  * @class Roo.bootstrap.menu.Separator
25702  * @extends Roo.bootstrap.Component
25703  * Bootstrap Separator class
25704  * 
25705  * @constructor
25706  * Create a new Separator
25707  * @param {Object} config The config object
25708  */
25709
25710
25711 Roo.bootstrap.menu.Separator = function(config){
25712     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25713 };
25714
25715 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
25716     
25717     getAutoCreate : function(){
25718         var cfg = {
25719             tag : 'li',
25720             cls: 'divider'
25721         };
25722         
25723         return cfg;
25724     }
25725    
25726 });
25727
25728  
25729
25730  /*
25731  * - LGPL
25732  *
25733  * Tooltip
25734  * 
25735  */
25736
25737 /**
25738  * @class Roo.bootstrap.Tooltip
25739  * Bootstrap Tooltip class
25740  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25741  * to determine which dom element triggers the tooltip.
25742  * 
25743  * It needs to add support for additional attributes like tooltip-position
25744  * 
25745  * @constructor
25746  * Create a new Toolti
25747  * @param {Object} config The config object
25748  */
25749
25750 Roo.bootstrap.Tooltip = function(config){
25751     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25752     
25753     this.alignment = Roo.bootstrap.Tooltip.alignment;
25754     
25755     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25756         this.alignment = config.alignment;
25757     }
25758     
25759 };
25760
25761 Roo.apply(Roo.bootstrap.Tooltip, {
25762     /**
25763      * @function init initialize tooltip monitoring.
25764      * @static
25765      */
25766     currentEl : false,
25767     currentTip : false,
25768     currentRegion : false,
25769     
25770     //  init : delay?
25771     
25772     init : function()
25773     {
25774         Roo.get(document).on('mouseover', this.enter ,this);
25775         Roo.get(document).on('mouseout', this.leave, this);
25776          
25777         
25778         this.currentTip = new Roo.bootstrap.Tooltip();
25779     },
25780     
25781     enter : function(ev)
25782     {
25783         var dom = ev.getTarget();
25784         
25785         //Roo.log(['enter',dom]);
25786         var el = Roo.fly(dom);
25787         if (this.currentEl) {
25788             //Roo.log(dom);
25789             //Roo.log(this.currentEl);
25790             //Roo.log(this.currentEl.contains(dom));
25791             if (this.currentEl == el) {
25792                 return;
25793             }
25794             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25795                 return;
25796             }
25797
25798         }
25799         
25800         if (this.currentTip.el) {
25801             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25802         }    
25803         //Roo.log(ev);
25804         
25805         if(!el || el.dom == document){
25806             return;
25807         }
25808         
25809         var bindEl = el;
25810         
25811         // you can not look for children, as if el is the body.. then everythign is the child..
25812         if (!el.attr('tooltip')) { //
25813             if (!el.select("[tooltip]").elements.length) {
25814                 return;
25815             }
25816             // is the mouse over this child...?
25817             bindEl = el.select("[tooltip]").first();
25818             var xy = ev.getXY();
25819             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
25820                 //Roo.log("not in region.");
25821                 return;
25822             }
25823             //Roo.log("child element over..");
25824             
25825         }
25826         this.currentEl = bindEl;
25827         this.currentTip.bind(bindEl);
25828         this.currentRegion = Roo.lib.Region.getRegion(dom);
25829         this.currentTip.enter();
25830         
25831     },
25832     leave : function(ev)
25833     {
25834         var dom = ev.getTarget();
25835         //Roo.log(['leave',dom]);
25836         if (!this.currentEl) {
25837             return;
25838         }
25839         
25840         
25841         if (dom != this.currentEl.dom) {
25842             return;
25843         }
25844         var xy = ev.getXY();
25845         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
25846             return;
25847         }
25848         // only activate leave if mouse cursor is outside... bounding box..
25849         
25850         
25851         
25852         
25853         if (this.currentTip) {
25854             this.currentTip.leave();
25855         }
25856         //Roo.log('clear currentEl');
25857         this.currentEl = false;
25858         
25859         
25860     },
25861     alignment : {
25862         'left' : ['r-l', [-2,0], 'right'],
25863         'right' : ['l-r', [2,0], 'left'],
25864         'bottom' : ['t-b', [0,2], 'top'],
25865         'top' : [ 'b-t', [0,-2], 'bottom']
25866     }
25867     
25868 });
25869
25870
25871 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
25872     
25873     
25874     bindEl : false,
25875     
25876     delay : null, // can be { show : 300 , hide: 500}
25877     
25878     timeout : null,
25879     
25880     hoverState : null, //???
25881     
25882     placement : 'bottom', 
25883     
25884     alignment : false,
25885     
25886     getAutoCreate : function(){
25887     
25888         var cfg = {
25889            cls : 'tooltip',
25890            role : 'tooltip',
25891            cn : [
25892                 {
25893                     cls : 'tooltip-arrow'
25894                 },
25895                 {
25896                     cls : 'tooltip-inner'
25897                 }
25898            ]
25899         };
25900         
25901         return cfg;
25902     },
25903     bind : function(el)
25904     {
25905         this.bindEl = el;
25906     },
25907       
25908     
25909     enter : function () {
25910        
25911         if (this.timeout != null) {
25912             clearTimeout(this.timeout);
25913         }
25914         
25915         this.hoverState = 'in';
25916          //Roo.log("enter - show");
25917         if (!this.delay || !this.delay.show) {
25918             this.show();
25919             return;
25920         }
25921         var _t = this;
25922         this.timeout = setTimeout(function () {
25923             if (_t.hoverState == 'in') {
25924                 _t.show();
25925             }
25926         }, this.delay.show);
25927     },
25928     leave : function()
25929     {
25930         clearTimeout(this.timeout);
25931     
25932         this.hoverState = 'out';
25933          if (!this.delay || !this.delay.hide) {
25934             this.hide();
25935             return;
25936         }
25937        
25938         var _t = this;
25939         this.timeout = setTimeout(function () {
25940             //Roo.log("leave - timeout");
25941             
25942             if (_t.hoverState == 'out') {
25943                 _t.hide();
25944                 Roo.bootstrap.Tooltip.currentEl = false;
25945             }
25946         }, delay);
25947     },
25948     
25949     show : function (msg)
25950     {
25951         if (!this.el) {
25952             this.render(document.body);
25953         }
25954         // set content.
25955         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
25956         
25957         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
25958         
25959         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
25960         
25961         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
25962         
25963         var placement = typeof this.placement == 'function' ?
25964             this.placement.call(this, this.el, on_el) :
25965             this.placement;
25966             
25967         var autoToken = /\s?auto?\s?/i;
25968         var autoPlace = autoToken.test(placement);
25969         if (autoPlace) {
25970             placement = placement.replace(autoToken, '') || 'top';
25971         }
25972         
25973         //this.el.detach()
25974         //this.el.setXY([0,0]);
25975         this.el.show();
25976         //this.el.dom.style.display='block';
25977         
25978         //this.el.appendTo(on_el);
25979         
25980         var p = this.getPosition();
25981         var box = this.el.getBox();
25982         
25983         if (autoPlace) {
25984             // fixme..
25985         }
25986         
25987         var align = this.alignment[placement];
25988         
25989         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
25990         
25991         if(placement == 'top' || placement == 'bottom'){
25992             if(xy[0] < 0){
25993                 placement = 'right';
25994             }
25995             
25996             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
25997                 placement = 'left';
25998             }
25999             
26000             var scroll = Roo.select('body', true).first().getScroll();
26001             
26002             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26003                 placement = 'top';
26004             }
26005             
26006         }
26007         
26008         this.el.alignTo(this.bindEl, align[0],align[1]);
26009         //var arrow = this.el.select('.arrow',true).first();
26010         //arrow.set(align[2], 
26011         
26012         this.el.addClass(placement);
26013         
26014         this.el.addClass('in fade');
26015         
26016         this.hoverState = null;
26017         
26018         if (this.el.hasClass('fade')) {
26019             // fade it?
26020         }
26021         
26022     },
26023     hide : function()
26024     {
26025          
26026         if (!this.el) {
26027             return;
26028         }
26029         //this.el.setXY([0,0]);
26030         this.el.removeClass('in');
26031         //this.el.hide();
26032         
26033     }
26034     
26035 });
26036  
26037
26038  /*
26039  * - LGPL
26040  *
26041  * Location Picker
26042  * 
26043  */
26044
26045 /**
26046  * @class Roo.bootstrap.LocationPicker
26047  * @extends Roo.bootstrap.Component
26048  * Bootstrap LocationPicker class
26049  * @cfg {Number} latitude Position when init default 0
26050  * @cfg {Number} longitude Position when init default 0
26051  * @cfg {Number} zoom default 15
26052  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26053  * @cfg {Boolean} mapTypeControl default false
26054  * @cfg {Boolean} disableDoubleClickZoom default false
26055  * @cfg {Boolean} scrollwheel default true
26056  * @cfg {Boolean} streetViewControl default false
26057  * @cfg {Number} radius default 0
26058  * @cfg {String} locationName
26059  * @cfg {Boolean} draggable default true
26060  * @cfg {Boolean} enableAutocomplete default false
26061  * @cfg {Boolean} enableReverseGeocode default true
26062  * @cfg {String} markerTitle
26063  * 
26064  * @constructor
26065  * Create a new LocationPicker
26066  * @param {Object} config The config object
26067  */
26068
26069
26070 Roo.bootstrap.LocationPicker = function(config){
26071     
26072     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26073     
26074     this.addEvents({
26075         /**
26076          * @event initial
26077          * Fires when the picker initialized.
26078          * @param {Roo.bootstrap.LocationPicker} this
26079          * @param {Google Location} location
26080          */
26081         initial : true,
26082         /**
26083          * @event positionchanged
26084          * Fires when the picker position changed.
26085          * @param {Roo.bootstrap.LocationPicker} this
26086          * @param {Google Location} location
26087          */
26088         positionchanged : true,
26089         /**
26090          * @event resize
26091          * Fires when the map resize.
26092          * @param {Roo.bootstrap.LocationPicker} this
26093          */
26094         resize : true,
26095         /**
26096          * @event show
26097          * Fires when the map show.
26098          * @param {Roo.bootstrap.LocationPicker} this
26099          */
26100         show : true,
26101         /**
26102          * @event hide
26103          * Fires when the map hide.
26104          * @param {Roo.bootstrap.LocationPicker} this
26105          */
26106         hide : true,
26107         /**
26108          * @event mapClick
26109          * Fires when click the map.
26110          * @param {Roo.bootstrap.LocationPicker} this
26111          * @param {Map event} e
26112          */
26113         mapClick : true,
26114         /**
26115          * @event mapRightClick
26116          * Fires when right click the map.
26117          * @param {Roo.bootstrap.LocationPicker} this
26118          * @param {Map event} e
26119          */
26120         mapRightClick : true,
26121         /**
26122          * @event markerClick
26123          * Fires when click the marker.
26124          * @param {Roo.bootstrap.LocationPicker} this
26125          * @param {Map event} e
26126          */
26127         markerClick : true,
26128         /**
26129          * @event markerRightClick
26130          * Fires when right click the marker.
26131          * @param {Roo.bootstrap.LocationPicker} this
26132          * @param {Map event} e
26133          */
26134         markerRightClick : true,
26135         /**
26136          * @event OverlayViewDraw
26137          * Fires when OverlayView Draw
26138          * @param {Roo.bootstrap.LocationPicker} this
26139          */
26140         OverlayViewDraw : true,
26141         /**
26142          * @event OverlayViewOnAdd
26143          * Fires when OverlayView Draw
26144          * @param {Roo.bootstrap.LocationPicker} this
26145          */
26146         OverlayViewOnAdd : true,
26147         /**
26148          * @event OverlayViewOnRemove
26149          * Fires when OverlayView Draw
26150          * @param {Roo.bootstrap.LocationPicker} this
26151          */
26152         OverlayViewOnRemove : true,
26153         /**
26154          * @event OverlayViewShow
26155          * Fires when OverlayView Draw
26156          * @param {Roo.bootstrap.LocationPicker} this
26157          * @param {Pixel} cpx
26158          */
26159         OverlayViewShow : true,
26160         /**
26161          * @event OverlayViewHide
26162          * Fires when OverlayView Draw
26163          * @param {Roo.bootstrap.LocationPicker} this
26164          */
26165         OverlayViewHide : true,
26166         /**
26167          * @event loadexception
26168          * Fires when load google lib failed.
26169          * @param {Roo.bootstrap.LocationPicker} this
26170          */
26171         loadexception : true
26172     });
26173         
26174 };
26175
26176 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26177     
26178     gMapContext: false,
26179     
26180     latitude: 0,
26181     longitude: 0,
26182     zoom: 15,
26183     mapTypeId: false,
26184     mapTypeControl: false,
26185     disableDoubleClickZoom: false,
26186     scrollwheel: true,
26187     streetViewControl: false,
26188     radius: 0,
26189     locationName: '',
26190     draggable: true,
26191     enableAutocomplete: false,
26192     enableReverseGeocode: true,
26193     markerTitle: '',
26194     
26195     getAutoCreate: function()
26196     {
26197
26198         var cfg = {
26199             tag: 'div',
26200             cls: 'roo-location-picker'
26201         };
26202         
26203         return cfg
26204     },
26205     
26206     initEvents: function(ct, position)
26207     {       
26208         if(!this.el.getWidth() || this.isApplied()){
26209             return;
26210         }
26211         
26212         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26213         
26214         this.initial();
26215     },
26216     
26217     initial: function()
26218     {
26219         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26220             this.fireEvent('loadexception', this);
26221             return;
26222         }
26223         
26224         if(!this.mapTypeId){
26225             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26226         }
26227         
26228         this.gMapContext = this.GMapContext();
26229         
26230         this.initOverlayView();
26231         
26232         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26233         
26234         var _this = this;
26235                 
26236         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26237             _this.setPosition(_this.gMapContext.marker.position);
26238         });
26239         
26240         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26241             _this.fireEvent('mapClick', this, event);
26242             
26243         });
26244
26245         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26246             _this.fireEvent('mapRightClick', this, event);
26247             
26248         });
26249         
26250         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26251             _this.fireEvent('markerClick', this, event);
26252             
26253         });
26254
26255         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26256             _this.fireEvent('markerRightClick', this, event);
26257             
26258         });
26259         
26260         this.setPosition(this.gMapContext.location);
26261         
26262         this.fireEvent('initial', this, this.gMapContext.location);
26263     },
26264     
26265     initOverlayView: function()
26266     {
26267         var _this = this;
26268         
26269         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26270             
26271             draw: function()
26272             {
26273                 _this.fireEvent('OverlayViewDraw', _this);
26274             },
26275             
26276             onAdd: function()
26277             {
26278                 _this.fireEvent('OverlayViewOnAdd', _this);
26279             },
26280             
26281             onRemove: function()
26282             {
26283                 _this.fireEvent('OverlayViewOnRemove', _this);
26284             },
26285             
26286             show: function(cpx)
26287             {
26288                 _this.fireEvent('OverlayViewShow', _this, cpx);
26289             },
26290             
26291             hide: function()
26292             {
26293                 _this.fireEvent('OverlayViewHide', _this);
26294             }
26295             
26296         });
26297     },
26298     
26299     fromLatLngToContainerPixel: function(event)
26300     {
26301         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26302     },
26303     
26304     isApplied: function() 
26305     {
26306         return this.getGmapContext() == false ? false : true;
26307     },
26308     
26309     getGmapContext: function() 
26310     {
26311         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26312     },
26313     
26314     GMapContext: function() 
26315     {
26316         var position = new google.maps.LatLng(this.latitude, this.longitude);
26317         
26318         var _map = new google.maps.Map(this.el.dom, {
26319             center: position,
26320             zoom: this.zoom,
26321             mapTypeId: this.mapTypeId,
26322             mapTypeControl: this.mapTypeControl,
26323             disableDoubleClickZoom: this.disableDoubleClickZoom,
26324             scrollwheel: this.scrollwheel,
26325             streetViewControl: this.streetViewControl,
26326             locationName: this.locationName,
26327             draggable: this.draggable,
26328             enableAutocomplete: this.enableAutocomplete,
26329             enableReverseGeocode: this.enableReverseGeocode
26330         });
26331         
26332         var _marker = new google.maps.Marker({
26333             position: position,
26334             map: _map,
26335             title: this.markerTitle,
26336             draggable: this.draggable
26337         });
26338         
26339         return {
26340             map: _map,
26341             marker: _marker,
26342             circle: null,
26343             location: position,
26344             radius: this.radius,
26345             locationName: this.locationName,
26346             addressComponents: {
26347                 formatted_address: null,
26348                 addressLine1: null,
26349                 addressLine2: null,
26350                 streetName: null,
26351                 streetNumber: null,
26352                 city: null,
26353                 district: null,
26354                 state: null,
26355                 stateOrProvince: null
26356             },
26357             settings: this,
26358             domContainer: this.el.dom,
26359             geodecoder: new google.maps.Geocoder()
26360         };
26361     },
26362     
26363     drawCircle: function(center, radius, options) 
26364     {
26365         if (this.gMapContext.circle != null) {
26366             this.gMapContext.circle.setMap(null);
26367         }
26368         if (radius > 0) {
26369             radius *= 1;
26370             options = Roo.apply({}, options, {
26371                 strokeColor: "#0000FF",
26372                 strokeOpacity: .35,
26373                 strokeWeight: 2,
26374                 fillColor: "#0000FF",
26375                 fillOpacity: .2
26376             });
26377             
26378             options.map = this.gMapContext.map;
26379             options.radius = radius;
26380             options.center = center;
26381             this.gMapContext.circle = new google.maps.Circle(options);
26382             return this.gMapContext.circle;
26383         }
26384         
26385         return null;
26386     },
26387     
26388     setPosition: function(location) 
26389     {
26390         this.gMapContext.location = location;
26391         this.gMapContext.marker.setPosition(location);
26392         this.gMapContext.map.panTo(location);
26393         this.drawCircle(location, this.gMapContext.radius, {});
26394         
26395         var _this = this;
26396         
26397         if (this.gMapContext.settings.enableReverseGeocode) {
26398             this.gMapContext.geodecoder.geocode({
26399                 latLng: this.gMapContext.location
26400             }, function(results, status) {
26401                 
26402                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26403                     _this.gMapContext.locationName = results[0].formatted_address;
26404                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26405                     
26406                     _this.fireEvent('positionchanged', this, location);
26407                 }
26408             });
26409             
26410             return;
26411         }
26412         
26413         this.fireEvent('positionchanged', this, location);
26414     },
26415     
26416     resize: function()
26417     {
26418         google.maps.event.trigger(this.gMapContext.map, "resize");
26419         
26420         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26421         
26422         this.fireEvent('resize', this);
26423     },
26424     
26425     setPositionByLatLng: function(latitude, longitude)
26426     {
26427         this.setPosition(new google.maps.LatLng(latitude, longitude));
26428     },
26429     
26430     getCurrentPosition: function() 
26431     {
26432         return {
26433             latitude: this.gMapContext.location.lat(),
26434             longitude: this.gMapContext.location.lng()
26435         };
26436     },
26437     
26438     getAddressName: function() 
26439     {
26440         return this.gMapContext.locationName;
26441     },
26442     
26443     getAddressComponents: function() 
26444     {
26445         return this.gMapContext.addressComponents;
26446     },
26447     
26448     address_component_from_google_geocode: function(address_components) 
26449     {
26450         var result = {};
26451         
26452         for (var i = 0; i < address_components.length; i++) {
26453             var component = address_components[i];
26454             if (component.types.indexOf("postal_code") >= 0) {
26455                 result.postalCode = component.short_name;
26456             } else if (component.types.indexOf("street_number") >= 0) {
26457                 result.streetNumber = component.short_name;
26458             } else if (component.types.indexOf("route") >= 0) {
26459                 result.streetName = component.short_name;
26460             } else if (component.types.indexOf("neighborhood") >= 0) {
26461                 result.city = component.short_name;
26462             } else if (component.types.indexOf("locality") >= 0) {
26463                 result.city = component.short_name;
26464             } else if (component.types.indexOf("sublocality") >= 0) {
26465                 result.district = component.short_name;
26466             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26467                 result.stateOrProvince = component.short_name;
26468             } else if (component.types.indexOf("country") >= 0) {
26469                 result.country = component.short_name;
26470             }
26471         }
26472         
26473         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26474         result.addressLine2 = "";
26475         return result;
26476     },
26477     
26478     setZoomLevel: function(zoom)
26479     {
26480         this.gMapContext.map.setZoom(zoom);
26481     },
26482     
26483     show: function()
26484     {
26485         if(!this.el){
26486             return;
26487         }
26488         
26489         this.el.show();
26490         
26491         this.resize();
26492         
26493         this.fireEvent('show', this);
26494     },
26495     
26496     hide: function()
26497     {
26498         if(!this.el){
26499             return;
26500         }
26501         
26502         this.el.hide();
26503         
26504         this.fireEvent('hide', this);
26505     }
26506     
26507 });
26508
26509 Roo.apply(Roo.bootstrap.LocationPicker, {
26510     
26511     OverlayView : function(map, options)
26512     {
26513         options = options || {};
26514         
26515         this.setMap(map);
26516     }
26517     
26518     
26519 });/*
26520  * - LGPL
26521  *
26522  * Alert
26523  * 
26524  */
26525
26526 /**
26527  * @class Roo.bootstrap.Alert
26528  * @extends Roo.bootstrap.Component
26529  * Bootstrap Alert class
26530  * @cfg {String} title The title of alert
26531  * @cfg {String} html The content of alert
26532  * @cfg {String} weight (  success | info | warning | danger )
26533  * @cfg {String} faicon font-awesomeicon
26534  * 
26535  * @constructor
26536  * Create a new alert
26537  * @param {Object} config The config object
26538  */
26539
26540
26541 Roo.bootstrap.Alert = function(config){
26542     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26543     
26544 };
26545
26546 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26547     
26548     title: '',
26549     html: '',
26550     weight: false,
26551     faicon: false,
26552     
26553     getAutoCreate : function()
26554     {
26555         
26556         var cfg = {
26557             tag : 'div',
26558             cls : 'alert',
26559             cn : [
26560                 {
26561                     tag : 'i',
26562                     cls : 'roo-alert-icon'
26563                     
26564                 },
26565                 {
26566                     tag : 'b',
26567                     cls : 'roo-alert-title',
26568                     html : this.title
26569                 },
26570                 {
26571                     tag : 'span',
26572                     cls : 'roo-alert-text',
26573                     html : this.html
26574                 }
26575             ]
26576         };
26577         
26578         if(this.faicon){
26579             cfg.cn[0].cls += ' fa ' + this.faicon;
26580         }
26581         
26582         if(this.weight){
26583             cfg.cls += ' alert-' + this.weight;
26584         }
26585         
26586         return cfg;
26587     },
26588     
26589     initEvents: function() 
26590     {
26591         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26592     },
26593     
26594     setTitle : function(str)
26595     {
26596         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26597     },
26598     
26599     setText : function(str)
26600     {
26601         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26602     },
26603     
26604     setWeight : function(weight)
26605     {
26606         if(this.weight){
26607             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26608         }
26609         
26610         this.weight = weight;
26611         
26612         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26613     },
26614     
26615     setIcon : function(icon)
26616     {
26617         if(this.faicon){
26618             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26619         }
26620         
26621         this.faicon = icon;
26622         
26623         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26624     },
26625     
26626     hide: function() 
26627     {
26628         this.el.hide();   
26629     },
26630     
26631     show: function() 
26632     {  
26633         this.el.show();   
26634     }
26635     
26636 });
26637
26638  
26639 /*
26640 * Licence: LGPL
26641 */
26642
26643 /**
26644  * @class Roo.bootstrap.UploadCropbox
26645  * @extends Roo.bootstrap.Component
26646  * Bootstrap UploadCropbox class
26647  * @cfg {String} emptyText show when image has been loaded
26648  * @cfg {String} rotateNotify show when image too small to rotate
26649  * @cfg {Number} errorTimeout default 3000
26650  * @cfg {Number} minWidth default 300
26651  * @cfg {Number} minHeight default 300
26652  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26653  * @cfg {Boolean} isDocument (true|false) default false
26654  * @cfg {String} url action url
26655  * @cfg {String} paramName default 'imageUpload'
26656  * @cfg {String} method default POST
26657  * @cfg {Boolean} loadMask (true|false) default true
26658  * @cfg {Boolean} loadingText default 'Loading...'
26659  * 
26660  * @constructor
26661  * Create a new UploadCropbox
26662  * @param {Object} config The config object
26663  */
26664
26665 Roo.bootstrap.UploadCropbox = function(config){
26666     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26667     
26668     this.addEvents({
26669         /**
26670          * @event beforeselectfile
26671          * Fire before select file
26672          * @param {Roo.bootstrap.UploadCropbox} this
26673          */
26674         "beforeselectfile" : true,
26675         /**
26676          * @event initial
26677          * Fire after initEvent
26678          * @param {Roo.bootstrap.UploadCropbox} this
26679          */
26680         "initial" : true,
26681         /**
26682          * @event crop
26683          * Fire after initEvent
26684          * @param {Roo.bootstrap.UploadCropbox} this
26685          * @param {String} data
26686          */
26687         "crop" : true,
26688         /**
26689          * @event prepare
26690          * Fire when preparing the file data
26691          * @param {Roo.bootstrap.UploadCropbox} this
26692          * @param {Object} file
26693          */
26694         "prepare" : true,
26695         /**
26696          * @event exception
26697          * Fire when get exception
26698          * @param {Roo.bootstrap.UploadCropbox} this
26699          * @param {XMLHttpRequest} xhr
26700          */
26701         "exception" : true,
26702         /**
26703          * @event beforeloadcanvas
26704          * Fire before load the canvas
26705          * @param {Roo.bootstrap.UploadCropbox} this
26706          * @param {String} src
26707          */
26708         "beforeloadcanvas" : true,
26709         /**
26710          * @event trash
26711          * Fire when trash image
26712          * @param {Roo.bootstrap.UploadCropbox} this
26713          */
26714         "trash" : true,
26715         /**
26716          * @event download
26717          * Fire when download the image
26718          * @param {Roo.bootstrap.UploadCropbox} this
26719          */
26720         "download" : true,
26721         /**
26722          * @event footerbuttonclick
26723          * Fire when footerbuttonclick
26724          * @param {Roo.bootstrap.UploadCropbox} this
26725          * @param {String} type
26726          */
26727         "footerbuttonclick" : true,
26728         /**
26729          * @event resize
26730          * Fire when resize
26731          * @param {Roo.bootstrap.UploadCropbox} this
26732          */
26733         "resize" : true,
26734         /**
26735          * @event rotate
26736          * Fire when rotate the image
26737          * @param {Roo.bootstrap.UploadCropbox} this
26738          * @param {String} pos
26739          */
26740         "rotate" : true,
26741         /**
26742          * @event inspect
26743          * Fire when inspect the file
26744          * @param {Roo.bootstrap.UploadCropbox} this
26745          * @param {Object} file
26746          */
26747         "inspect" : true,
26748         /**
26749          * @event upload
26750          * Fire when xhr upload the file
26751          * @param {Roo.bootstrap.UploadCropbox} this
26752          * @param {Object} data
26753          */
26754         "upload" : true,
26755         /**
26756          * @event arrange
26757          * Fire when arrange the file data
26758          * @param {Roo.bootstrap.UploadCropbox} this
26759          * @param {Object} formData
26760          */
26761         "arrange" : true
26762     });
26763     
26764     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26765 };
26766
26767 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
26768     
26769     emptyText : 'Click to upload image',
26770     rotateNotify : 'Image is too small to rotate',
26771     errorTimeout : 3000,
26772     scale : 0,
26773     baseScale : 1,
26774     rotate : 0,
26775     dragable : false,
26776     pinching : false,
26777     mouseX : 0,
26778     mouseY : 0,
26779     cropData : false,
26780     minWidth : 300,
26781     minHeight : 300,
26782     file : false,
26783     exif : {},
26784     baseRotate : 1,
26785     cropType : 'image/jpeg',
26786     buttons : false,
26787     canvasLoaded : false,
26788     isDocument : false,
26789     method : 'POST',
26790     paramName : 'imageUpload',
26791     loadMask : true,
26792     loadingText : 'Loading...',
26793     maskEl : false,
26794     
26795     getAutoCreate : function()
26796     {
26797         var cfg = {
26798             tag : 'div',
26799             cls : 'roo-upload-cropbox',
26800             cn : [
26801                 {
26802                     tag : 'input',
26803                     cls : 'roo-upload-cropbox-selector',
26804                     type : 'file'
26805                 },
26806                 {
26807                     tag : 'div',
26808                     cls : 'roo-upload-cropbox-body',
26809                     style : 'cursor:pointer',
26810                     cn : [
26811                         {
26812                             tag : 'div',
26813                             cls : 'roo-upload-cropbox-preview'
26814                         },
26815                         {
26816                             tag : 'div',
26817                             cls : 'roo-upload-cropbox-thumb'
26818                         },
26819                         {
26820                             tag : 'div',
26821                             cls : 'roo-upload-cropbox-empty-notify',
26822                             html : this.emptyText
26823                         },
26824                         {
26825                             tag : 'div',
26826                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
26827                             html : this.rotateNotify
26828                         }
26829                     ]
26830                 },
26831                 {
26832                     tag : 'div',
26833                     cls : 'roo-upload-cropbox-footer',
26834                     cn : {
26835                         tag : 'div',
26836                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
26837                         cn : []
26838                     }
26839                 }
26840             ]
26841         };
26842         
26843         return cfg;
26844     },
26845     
26846     onRender : function(ct, position)
26847     {
26848         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
26849         
26850         if (this.buttons.length) {
26851             
26852             Roo.each(this.buttons, function(bb) {
26853                 
26854                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
26855                 
26856                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
26857                 
26858             }, this);
26859         }
26860         
26861         if(this.loadMask){
26862             this.maskEl = this.el;
26863         }
26864     },
26865     
26866     initEvents : function()
26867     {
26868         this.urlAPI = (window.createObjectURL && window) || 
26869                                 (window.URL && URL.revokeObjectURL && URL) || 
26870                                 (window.webkitURL && webkitURL);
26871                         
26872         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
26873         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26874         
26875         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
26876         this.selectorEl.hide();
26877         
26878         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
26879         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26880         
26881         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
26882         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26883         this.thumbEl.hide();
26884         
26885         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
26886         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26887         
26888         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
26889         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26890         this.errorEl.hide();
26891         
26892         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
26893         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26894         this.footerEl.hide();
26895         
26896         this.setThumbBoxSize();
26897         
26898         this.bind();
26899         
26900         this.resize();
26901         
26902         this.fireEvent('initial', this);
26903     },
26904
26905     bind : function()
26906     {
26907         var _this = this;
26908         
26909         window.addEventListener("resize", function() { _this.resize(); } );
26910         
26911         this.bodyEl.on('click', this.beforeSelectFile, this);
26912         
26913         if(Roo.isTouch){
26914             this.bodyEl.on('touchstart', this.onTouchStart, this);
26915             this.bodyEl.on('touchmove', this.onTouchMove, this);
26916             this.bodyEl.on('touchend', this.onTouchEnd, this);
26917         }
26918         
26919         if(!Roo.isTouch){
26920             this.bodyEl.on('mousedown', this.onMouseDown, this);
26921             this.bodyEl.on('mousemove', this.onMouseMove, this);
26922             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
26923             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
26924             Roo.get(document).on('mouseup', this.onMouseUp, this);
26925         }
26926         
26927         this.selectorEl.on('change', this.onFileSelected, this);
26928     },
26929     
26930     reset : function()
26931     {    
26932         this.scale = 0;
26933         this.baseScale = 1;
26934         this.rotate = 0;
26935         this.baseRotate = 1;
26936         this.dragable = false;
26937         this.pinching = false;
26938         this.mouseX = 0;
26939         this.mouseY = 0;
26940         this.cropData = false;
26941         this.notifyEl.dom.innerHTML = this.emptyText;
26942         
26943         this.selectorEl.dom.value = '';
26944         
26945     },
26946     
26947     resize : function()
26948     {
26949         if(this.fireEvent('resize', this) != false){
26950             this.setThumbBoxPosition();
26951             this.setCanvasPosition();
26952         }
26953     },
26954     
26955     onFooterButtonClick : function(e, el, o, type)
26956     {
26957         switch (type) {
26958             case 'rotate-left' :
26959                 this.onRotateLeft(e);
26960                 break;
26961             case 'rotate-right' :
26962                 this.onRotateRight(e);
26963                 break;
26964             case 'picture' :
26965                 this.beforeSelectFile(e);
26966                 break;
26967             case 'trash' :
26968                 this.trash(e);
26969                 break;
26970             case 'crop' :
26971                 this.crop(e);
26972                 break;
26973             case 'download' :
26974                 this.download(e);
26975                 break;
26976             default :
26977                 break;
26978         }
26979         
26980         this.fireEvent('footerbuttonclick', this, type);
26981     },
26982     
26983     beforeSelectFile : function(e)
26984     {
26985         e.preventDefault();
26986         
26987         if(this.fireEvent('beforeselectfile', this) != false){
26988             this.selectorEl.dom.click();
26989         }
26990     },
26991     
26992     onFileSelected : function(e)
26993     {
26994         e.preventDefault();
26995         
26996         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26997             return;
26998         }
26999         
27000         var file = this.selectorEl.dom.files[0];
27001         
27002         if(this.fireEvent('inspect', this, file) != false){
27003             this.prepare(file);
27004         }
27005         
27006     },
27007     
27008     trash : function(e)
27009     {
27010         this.fireEvent('trash', this);
27011     },
27012     
27013     download : function(e)
27014     {
27015         this.fireEvent('download', this);
27016     },
27017     
27018     loadCanvas : function(src)
27019     {   
27020         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27021             
27022             this.reset();
27023             
27024             this.imageEl = document.createElement('img');
27025             
27026             var _this = this;
27027             
27028             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27029             
27030             this.imageEl.src = src;
27031         }
27032     },
27033     
27034     onLoadCanvas : function()
27035     {   
27036         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27037         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27038         
27039         this.bodyEl.un('click', this.beforeSelectFile, this);
27040         
27041         this.notifyEl.hide();
27042         this.thumbEl.show();
27043         this.footerEl.show();
27044         
27045         this.baseRotateLevel();
27046         
27047         if(this.isDocument){
27048             this.setThumbBoxSize();
27049         }
27050         
27051         this.setThumbBoxPosition();
27052         
27053         this.baseScaleLevel();
27054         
27055         this.draw();
27056         
27057         this.resize();
27058         
27059         this.canvasLoaded = true;
27060         
27061         if(this.loadMask){
27062             this.maskEl.unmask();
27063         }
27064         
27065     },
27066     
27067     setCanvasPosition : function()
27068     {   
27069         if(!this.canvasEl){
27070             return;
27071         }
27072         
27073         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27074         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27075         
27076         this.previewEl.setLeft(pw);
27077         this.previewEl.setTop(ph);
27078         
27079     },
27080     
27081     onMouseDown : function(e)
27082     {   
27083         e.stopEvent();
27084         
27085         this.dragable = true;
27086         this.pinching = false;
27087         
27088         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27089             this.dragable = false;
27090             return;
27091         }
27092         
27093         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27094         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27095         
27096     },
27097     
27098     onMouseMove : function(e)
27099     {   
27100         e.stopEvent();
27101         
27102         if(!this.canvasLoaded){
27103             return;
27104         }
27105         
27106         if (!this.dragable){
27107             return;
27108         }
27109         
27110         var minX = Math.ceil(this.thumbEl.getLeft(true));
27111         var minY = Math.ceil(this.thumbEl.getTop(true));
27112         
27113         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27114         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27115         
27116         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27117         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27118         
27119         x = x - this.mouseX;
27120         y = y - this.mouseY;
27121         
27122         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27123         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27124         
27125         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27126         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27127         
27128         this.previewEl.setLeft(bgX);
27129         this.previewEl.setTop(bgY);
27130         
27131         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27132         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27133     },
27134     
27135     onMouseUp : function(e)
27136     {   
27137         e.stopEvent();
27138         
27139         this.dragable = false;
27140     },
27141     
27142     onMouseWheel : function(e)
27143     {   
27144         e.stopEvent();
27145         
27146         this.startScale = this.scale;
27147         
27148         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27149         
27150         if(!this.zoomable()){
27151             this.scale = this.startScale;
27152             return;
27153         }
27154         
27155         this.draw();
27156         
27157         return;
27158     },
27159     
27160     zoomable : function()
27161     {
27162         var minScale = this.thumbEl.getWidth() / this.minWidth;
27163         
27164         if(this.minWidth < this.minHeight){
27165             minScale = this.thumbEl.getHeight() / this.minHeight;
27166         }
27167         
27168         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27169         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27170         
27171         if(
27172                 this.isDocument &&
27173                 (this.rotate == 0 || this.rotate == 180) && 
27174                 (
27175                     width > this.imageEl.OriginWidth || 
27176                     height > this.imageEl.OriginHeight ||
27177                     (width < this.minWidth && height < this.minHeight)
27178                 )
27179         ){
27180             return false;
27181         }
27182         
27183         if(
27184                 this.isDocument &&
27185                 (this.rotate == 90 || this.rotate == 270) && 
27186                 (
27187                     width > this.imageEl.OriginWidth || 
27188                     height > this.imageEl.OriginHeight ||
27189                     (width < this.minHeight && height < this.minWidth)
27190                 )
27191         ){
27192             return false;
27193         }
27194         
27195         if(
27196                 !this.isDocument &&
27197                 (this.rotate == 0 || this.rotate == 180) && 
27198                 (
27199                     width < this.minWidth || 
27200                     width > this.imageEl.OriginWidth || 
27201                     height < this.minHeight || 
27202                     height > this.imageEl.OriginHeight
27203                 )
27204         ){
27205             return false;
27206         }
27207         
27208         if(
27209                 !this.isDocument &&
27210                 (this.rotate == 90 || this.rotate == 270) && 
27211                 (
27212                     width < this.minHeight || 
27213                     width > this.imageEl.OriginWidth || 
27214                     height < this.minWidth || 
27215                     height > this.imageEl.OriginHeight
27216                 )
27217         ){
27218             return false;
27219         }
27220         
27221         return true;
27222         
27223     },
27224     
27225     onRotateLeft : function(e)
27226     {   
27227         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27228             
27229             var minScale = this.thumbEl.getWidth() / this.minWidth;
27230             
27231             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27232             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27233             
27234             this.startScale = this.scale;
27235             
27236             while (this.getScaleLevel() < minScale){
27237             
27238                 this.scale = this.scale + 1;
27239                 
27240                 if(!this.zoomable()){
27241                     break;
27242                 }
27243                 
27244                 if(
27245                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27246                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27247                 ){
27248                     continue;
27249                 }
27250                 
27251                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27252
27253                 this.draw();
27254                 
27255                 return;
27256             }
27257             
27258             this.scale = this.startScale;
27259             
27260             this.onRotateFail();
27261             
27262             return false;
27263         }
27264         
27265         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27266
27267         if(this.isDocument){
27268             this.setThumbBoxSize();
27269             this.setThumbBoxPosition();
27270             this.setCanvasPosition();
27271         }
27272         
27273         this.draw();
27274         
27275         this.fireEvent('rotate', this, 'left');
27276         
27277     },
27278     
27279     onRotateRight : function(e)
27280     {
27281         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27282             
27283             var minScale = this.thumbEl.getWidth() / this.minWidth;
27284         
27285             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27286             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27287             
27288             this.startScale = this.scale;
27289             
27290             while (this.getScaleLevel() < minScale){
27291             
27292                 this.scale = this.scale + 1;
27293                 
27294                 if(!this.zoomable()){
27295                     break;
27296                 }
27297                 
27298                 if(
27299                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27300                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27301                 ){
27302                     continue;
27303                 }
27304                 
27305                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27306
27307                 this.draw();
27308                 
27309                 return;
27310             }
27311             
27312             this.scale = this.startScale;
27313             
27314             this.onRotateFail();
27315             
27316             return false;
27317         }
27318         
27319         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27320
27321         if(this.isDocument){
27322             this.setThumbBoxSize();
27323             this.setThumbBoxPosition();
27324             this.setCanvasPosition();
27325         }
27326         
27327         this.draw();
27328         
27329         this.fireEvent('rotate', this, 'right');
27330     },
27331     
27332     onRotateFail : function()
27333     {
27334         this.errorEl.show(true);
27335         
27336         var _this = this;
27337         
27338         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27339     },
27340     
27341     draw : function()
27342     {
27343         this.previewEl.dom.innerHTML = '';
27344         
27345         var canvasEl = document.createElement("canvas");
27346         
27347         var contextEl = canvasEl.getContext("2d");
27348         
27349         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27350         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27351         var center = this.imageEl.OriginWidth / 2;
27352         
27353         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27354             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27355             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27356             center = this.imageEl.OriginHeight / 2;
27357         }
27358         
27359         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27360         
27361         contextEl.translate(center, center);
27362         contextEl.rotate(this.rotate * Math.PI / 180);
27363
27364         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27365         
27366         this.canvasEl = document.createElement("canvas");
27367         
27368         this.contextEl = this.canvasEl.getContext("2d");
27369         
27370         switch (this.rotate) {
27371             case 0 :
27372                 
27373                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27374                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27375                 
27376                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27377                 
27378                 break;
27379             case 90 : 
27380                 
27381                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27382                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27383                 
27384                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27385                     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);
27386                     break;
27387                 }
27388                 
27389                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27390                 
27391                 break;
27392             case 180 :
27393                 
27394                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27395                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27396                 
27397                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27398                     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);
27399                     break;
27400                 }
27401                 
27402                 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);
27403                 
27404                 break;
27405             case 270 :
27406                 
27407                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27408                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27409         
27410                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27411                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27412                     break;
27413                 }
27414                 
27415                 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);
27416                 
27417                 break;
27418             default : 
27419                 break;
27420         }
27421         
27422         this.previewEl.appendChild(this.canvasEl);
27423         
27424         this.setCanvasPosition();
27425     },
27426     
27427     crop : function()
27428     {
27429         if(!this.canvasLoaded){
27430             return;
27431         }
27432         
27433         var imageCanvas = document.createElement("canvas");
27434         
27435         var imageContext = imageCanvas.getContext("2d");
27436         
27437         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27438         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27439         
27440         var center = imageCanvas.width / 2;
27441         
27442         imageContext.translate(center, center);
27443         
27444         imageContext.rotate(this.rotate * Math.PI / 180);
27445         
27446         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27447         
27448         var canvas = document.createElement("canvas");
27449         
27450         var context = canvas.getContext("2d");
27451                 
27452         canvas.width = this.minWidth;
27453         canvas.height = this.minHeight;
27454
27455         switch (this.rotate) {
27456             case 0 :
27457                 
27458                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27459                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27460                 
27461                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27462                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27463                 
27464                 var targetWidth = this.minWidth - 2 * x;
27465                 var targetHeight = this.minHeight - 2 * y;
27466                 
27467                 var scale = 1;
27468                 
27469                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27470                     scale = targetWidth / width;
27471                 }
27472                 
27473                 if(x > 0 && y == 0){
27474                     scale = targetHeight / height;
27475                 }
27476                 
27477                 if(x > 0 && y > 0){
27478                     scale = targetWidth / width;
27479                     
27480                     if(width < height){
27481                         scale = targetHeight / height;
27482                     }
27483                 }
27484                 
27485                 context.scale(scale, scale);
27486                 
27487                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27488                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27489
27490                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27491                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27492
27493                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27494                 
27495                 break;
27496             case 90 : 
27497                 
27498                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27499                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27500                 
27501                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27502                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27503                 
27504                 var targetWidth = this.minWidth - 2 * x;
27505                 var targetHeight = this.minHeight - 2 * y;
27506                 
27507                 var scale = 1;
27508                 
27509                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27510                     scale = targetWidth / width;
27511                 }
27512                 
27513                 if(x > 0 && y == 0){
27514                     scale = targetHeight / height;
27515                 }
27516                 
27517                 if(x > 0 && y > 0){
27518                     scale = targetWidth / width;
27519                     
27520                     if(width < height){
27521                         scale = targetHeight / height;
27522                     }
27523                 }
27524                 
27525                 context.scale(scale, scale);
27526                 
27527                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27528                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27529
27530                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27531                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27532                 
27533                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27534                 
27535                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27536                 
27537                 break;
27538             case 180 :
27539                 
27540                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27541                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27542                 
27543                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27544                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27545                 
27546                 var targetWidth = this.minWidth - 2 * x;
27547                 var targetHeight = this.minHeight - 2 * y;
27548                 
27549                 var scale = 1;
27550                 
27551                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27552                     scale = targetWidth / width;
27553                 }
27554                 
27555                 if(x > 0 && y == 0){
27556                     scale = targetHeight / height;
27557                 }
27558                 
27559                 if(x > 0 && y > 0){
27560                     scale = targetWidth / width;
27561                     
27562                     if(width < height){
27563                         scale = targetHeight / height;
27564                     }
27565                 }
27566                 
27567                 context.scale(scale, scale);
27568                 
27569                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27570                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27571
27572                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27573                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27574
27575                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27576                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27577                 
27578                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27579                 
27580                 break;
27581             case 270 :
27582                 
27583                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27584                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27585                 
27586                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27587                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27588                 
27589                 var targetWidth = this.minWidth - 2 * x;
27590                 var targetHeight = this.minHeight - 2 * y;
27591                 
27592                 var scale = 1;
27593                 
27594                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27595                     scale = targetWidth / width;
27596                 }
27597                 
27598                 if(x > 0 && y == 0){
27599                     scale = targetHeight / height;
27600                 }
27601                 
27602                 if(x > 0 && y > 0){
27603                     scale = targetWidth / width;
27604                     
27605                     if(width < height){
27606                         scale = targetHeight / height;
27607                     }
27608                 }
27609                 
27610                 context.scale(scale, scale);
27611                 
27612                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27613                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27614
27615                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27616                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27617                 
27618                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27619                 
27620                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27621                 
27622                 break;
27623             default : 
27624                 break;
27625         }
27626         
27627         this.cropData = canvas.toDataURL(this.cropType);
27628         
27629         if(this.fireEvent('crop', this, this.cropData) !== false){
27630             this.process(this.file, this.cropData);
27631         }
27632         
27633         return;
27634         
27635     },
27636     
27637     setThumbBoxSize : function()
27638     {
27639         var width, height;
27640         
27641         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27642             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27643             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27644             
27645             this.minWidth = width;
27646             this.minHeight = height;
27647             
27648             if(this.rotate == 90 || this.rotate == 270){
27649                 this.minWidth = height;
27650                 this.minHeight = width;
27651             }
27652         }
27653         
27654         height = 300;
27655         width = Math.ceil(this.minWidth * height / this.minHeight);
27656         
27657         if(this.minWidth > this.minHeight){
27658             width = 300;
27659             height = Math.ceil(this.minHeight * width / this.minWidth);
27660         }
27661         
27662         this.thumbEl.setStyle({
27663             width : width + 'px',
27664             height : height + 'px'
27665         });
27666
27667         return;
27668             
27669     },
27670     
27671     setThumbBoxPosition : function()
27672     {
27673         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27674         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27675         
27676         this.thumbEl.setLeft(x);
27677         this.thumbEl.setTop(y);
27678         
27679     },
27680     
27681     baseRotateLevel : function()
27682     {
27683         this.baseRotate = 1;
27684         
27685         if(
27686                 typeof(this.exif) != 'undefined' &&
27687                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27688                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27689         ){
27690             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27691         }
27692         
27693         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27694         
27695     },
27696     
27697     baseScaleLevel : function()
27698     {
27699         var width, height;
27700         
27701         if(this.isDocument){
27702             
27703             if(this.baseRotate == 6 || this.baseRotate == 8){
27704             
27705                 height = this.thumbEl.getHeight();
27706                 this.baseScale = height / this.imageEl.OriginWidth;
27707
27708                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27709                     width = this.thumbEl.getWidth();
27710                     this.baseScale = width / this.imageEl.OriginHeight;
27711                 }
27712
27713                 return;
27714             }
27715
27716             height = this.thumbEl.getHeight();
27717             this.baseScale = height / this.imageEl.OriginHeight;
27718
27719             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27720                 width = this.thumbEl.getWidth();
27721                 this.baseScale = width / this.imageEl.OriginWidth;
27722             }
27723
27724             return;
27725         }
27726         
27727         if(this.baseRotate == 6 || this.baseRotate == 8){
27728             
27729             width = this.thumbEl.getHeight();
27730             this.baseScale = width / this.imageEl.OriginHeight;
27731             
27732             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27733                 height = this.thumbEl.getWidth();
27734                 this.baseScale = height / this.imageEl.OriginHeight;
27735             }
27736             
27737             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27738                 height = this.thumbEl.getWidth();
27739                 this.baseScale = height / this.imageEl.OriginHeight;
27740                 
27741                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27742                     width = this.thumbEl.getHeight();
27743                     this.baseScale = width / this.imageEl.OriginWidth;
27744                 }
27745             }
27746             
27747             return;
27748         }
27749         
27750         width = this.thumbEl.getWidth();
27751         this.baseScale = width / this.imageEl.OriginWidth;
27752         
27753         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27754             height = this.thumbEl.getHeight();
27755             this.baseScale = height / this.imageEl.OriginHeight;
27756         }
27757         
27758         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27759             
27760             height = this.thumbEl.getHeight();
27761             this.baseScale = height / this.imageEl.OriginHeight;
27762             
27763             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27764                 width = this.thumbEl.getWidth();
27765                 this.baseScale = width / this.imageEl.OriginWidth;
27766             }
27767             
27768         }
27769         
27770         return;
27771     },
27772     
27773     getScaleLevel : function()
27774     {
27775         return this.baseScale * Math.pow(1.1, this.scale);
27776     },
27777     
27778     onTouchStart : function(e)
27779     {
27780         if(!this.canvasLoaded){
27781             this.beforeSelectFile(e);
27782             return;
27783         }
27784         
27785         var touches = e.browserEvent.touches;
27786         
27787         if(!touches){
27788             return;
27789         }
27790         
27791         if(touches.length == 1){
27792             this.onMouseDown(e);
27793             return;
27794         }
27795         
27796         if(touches.length != 2){
27797             return;
27798         }
27799         
27800         var coords = [];
27801         
27802         for(var i = 0, finger; finger = touches[i]; i++){
27803             coords.push(finger.pageX, finger.pageY);
27804         }
27805         
27806         var x = Math.pow(coords[0] - coords[2], 2);
27807         var y = Math.pow(coords[1] - coords[3], 2);
27808         
27809         this.startDistance = Math.sqrt(x + y);
27810         
27811         this.startScale = this.scale;
27812         
27813         this.pinching = true;
27814         this.dragable = false;
27815         
27816     },
27817     
27818     onTouchMove : function(e)
27819     {
27820         if(!this.pinching && !this.dragable){
27821             return;
27822         }
27823         
27824         var touches = e.browserEvent.touches;
27825         
27826         if(!touches){
27827             return;
27828         }
27829         
27830         if(this.dragable){
27831             this.onMouseMove(e);
27832             return;
27833         }
27834         
27835         var coords = [];
27836         
27837         for(var i = 0, finger; finger = touches[i]; i++){
27838             coords.push(finger.pageX, finger.pageY);
27839         }
27840         
27841         var x = Math.pow(coords[0] - coords[2], 2);
27842         var y = Math.pow(coords[1] - coords[3], 2);
27843         
27844         this.endDistance = Math.sqrt(x + y);
27845         
27846         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
27847         
27848         if(!this.zoomable()){
27849             this.scale = this.startScale;
27850             return;
27851         }
27852         
27853         this.draw();
27854         
27855     },
27856     
27857     onTouchEnd : function(e)
27858     {
27859         this.pinching = false;
27860         this.dragable = false;
27861         
27862     },
27863     
27864     process : function(file, crop)
27865     {
27866         if(this.loadMask){
27867             this.maskEl.mask(this.loadingText);
27868         }
27869         
27870         this.xhr = new XMLHttpRequest();
27871         
27872         file.xhr = this.xhr;
27873
27874         this.xhr.open(this.method, this.url, true);
27875         
27876         var headers = {
27877             "Accept": "application/json",
27878             "Cache-Control": "no-cache",
27879             "X-Requested-With": "XMLHttpRequest"
27880         };
27881         
27882         for (var headerName in headers) {
27883             var headerValue = headers[headerName];
27884             if (headerValue) {
27885                 this.xhr.setRequestHeader(headerName, headerValue);
27886             }
27887         }
27888         
27889         var _this = this;
27890         
27891         this.xhr.onload = function()
27892         {
27893             _this.xhrOnLoad(_this.xhr);
27894         }
27895         
27896         this.xhr.onerror = function()
27897         {
27898             _this.xhrOnError(_this.xhr);
27899         }
27900         
27901         var formData = new FormData();
27902
27903         formData.append('returnHTML', 'NO');
27904         
27905         if(crop){
27906             formData.append('crop', crop);
27907         }
27908         
27909         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
27910             formData.append(this.paramName, file, file.name);
27911         }
27912         
27913         if(typeof(file.filename) != 'undefined'){
27914             formData.append('filename', file.filename);
27915         }
27916         
27917         if(typeof(file.mimetype) != 'undefined'){
27918             formData.append('mimetype', file.mimetype);
27919         }
27920         
27921         if(this.fireEvent('arrange', this, formData) != false){
27922             this.xhr.send(formData);
27923         };
27924     },
27925     
27926     xhrOnLoad : function(xhr)
27927     {
27928         if(this.loadMask){
27929             this.maskEl.unmask();
27930         }
27931         
27932         if (xhr.readyState !== 4) {
27933             this.fireEvent('exception', this, xhr);
27934             return;
27935         }
27936
27937         var response = Roo.decode(xhr.responseText);
27938         
27939         if(!response.success){
27940             this.fireEvent('exception', this, xhr);
27941             return;
27942         }
27943         
27944         var response = Roo.decode(xhr.responseText);
27945         
27946         this.fireEvent('upload', this, response);
27947         
27948     },
27949     
27950     xhrOnError : function()
27951     {
27952         if(this.loadMask){
27953             this.maskEl.unmask();
27954         }
27955         
27956         Roo.log('xhr on error');
27957         
27958         var response = Roo.decode(xhr.responseText);
27959           
27960         Roo.log(response);
27961         
27962     },
27963     
27964     prepare : function(file)
27965     {   
27966         if(this.loadMask){
27967             this.maskEl.mask(this.loadingText);
27968         }
27969         
27970         this.file = false;
27971         this.exif = {};
27972         
27973         if(typeof(file) === 'string'){
27974             this.loadCanvas(file);
27975             return;
27976         }
27977         
27978         if(!file || !this.urlAPI){
27979             return;
27980         }
27981         
27982         this.file = file;
27983         this.cropType = file.type;
27984         
27985         var _this = this;
27986         
27987         if(this.fireEvent('prepare', this, this.file) != false){
27988             
27989             var reader = new FileReader();
27990             
27991             reader.onload = function (e) {
27992                 if (e.target.error) {
27993                     Roo.log(e.target.error);
27994                     return;
27995                 }
27996                 
27997                 var buffer = e.target.result,
27998                     dataView = new DataView(buffer),
27999                     offset = 2,
28000                     maxOffset = dataView.byteLength - 4,
28001                     markerBytes,
28002                     markerLength;
28003                 
28004                 if (dataView.getUint16(0) === 0xffd8) {
28005                     while (offset < maxOffset) {
28006                         markerBytes = dataView.getUint16(offset);
28007                         
28008                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28009                             markerLength = dataView.getUint16(offset + 2) + 2;
28010                             if (offset + markerLength > dataView.byteLength) {
28011                                 Roo.log('Invalid meta data: Invalid segment size.');
28012                                 break;
28013                             }
28014                             
28015                             if(markerBytes == 0xffe1){
28016                                 _this.parseExifData(
28017                                     dataView,
28018                                     offset,
28019                                     markerLength
28020                                 );
28021                             }
28022                             
28023                             offset += markerLength;
28024                             
28025                             continue;
28026                         }
28027                         
28028                         break;
28029                     }
28030                     
28031                 }
28032                 
28033                 var url = _this.urlAPI.createObjectURL(_this.file);
28034                 
28035                 _this.loadCanvas(url);
28036                 
28037                 return;
28038             }
28039             
28040             reader.readAsArrayBuffer(this.file);
28041             
28042         }
28043         
28044     },
28045     
28046     parseExifData : function(dataView, offset, length)
28047     {
28048         var tiffOffset = offset + 10,
28049             littleEndian,
28050             dirOffset;
28051     
28052         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28053             // No Exif data, might be XMP data instead
28054             return;
28055         }
28056         
28057         // Check for the ASCII code for "Exif" (0x45786966):
28058         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28059             // No Exif data, might be XMP data instead
28060             return;
28061         }
28062         if (tiffOffset + 8 > dataView.byteLength) {
28063             Roo.log('Invalid Exif data: Invalid segment size.');
28064             return;
28065         }
28066         // Check for the two null bytes:
28067         if (dataView.getUint16(offset + 8) !== 0x0000) {
28068             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28069             return;
28070         }
28071         // Check the byte alignment:
28072         switch (dataView.getUint16(tiffOffset)) {
28073         case 0x4949:
28074             littleEndian = true;
28075             break;
28076         case 0x4D4D:
28077             littleEndian = false;
28078             break;
28079         default:
28080             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28081             return;
28082         }
28083         // Check for the TIFF tag marker (0x002A):
28084         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28085             Roo.log('Invalid Exif data: Missing TIFF marker.');
28086             return;
28087         }
28088         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28089         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28090         
28091         this.parseExifTags(
28092             dataView,
28093             tiffOffset,
28094             tiffOffset + dirOffset,
28095             littleEndian
28096         );
28097     },
28098     
28099     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28100     {
28101         var tagsNumber,
28102             dirEndOffset,
28103             i;
28104         if (dirOffset + 6 > dataView.byteLength) {
28105             Roo.log('Invalid Exif data: Invalid directory offset.');
28106             return;
28107         }
28108         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28109         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28110         if (dirEndOffset + 4 > dataView.byteLength) {
28111             Roo.log('Invalid Exif data: Invalid directory size.');
28112             return;
28113         }
28114         for (i = 0; i < tagsNumber; i += 1) {
28115             this.parseExifTag(
28116                 dataView,
28117                 tiffOffset,
28118                 dirOffset + 2 + 12 * i, // tag offset
28119                 littleEndian
28120             );
28121         }
28122         // Return the offset to the next directory:
28123         return dataView.getUint32(dirEndOffset, littleEndian);
28124     },
28125     
28126     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28127     {
28128         var tag = dataView.getUint16(offset, littleEndian);
28129         
28130         this.exif[tag] = this.getExifValue(
28131             dataView,
28132             tiffOffset,
28133             offset,
28134             dataView.getUint16(offset + 2, littleEndian), // tag type
28135             dataView.getUint32(offset + 4, littleEndian), // tag length
28136             littleEndian
28137         );
28138     },
28139     
28140     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28141     {
28142         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28143             tagSize,
28144             dataOffset,
28145             values,
28146             i,
28147             str,
28148             c;
28149     
28150         if (!tagType) {
28151             Roo.log('Invalid Exif data: Invalid tag type.');
28152             return;
28153         }
28154         
28155         tagSize = tagType.size * length;
28156         // Determine if the value is contained in the dataOffset bytes,
28157         // or if the value at the dataOffset is a pointer to the actual data:
28158         dataOffset = tagSize > 4 ?
28159                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28160         if (dataOffset + tagSize > dataView.byteLength) {
28161             Roo.log('Invalid Exif data: Invalid data offset.');
28162             return;
28163         }
28164         if (length === 1) {
28165             return tagType.getValue(dataView, dataOffset, littleEndian);
28166         }
28167         values = [];
28168         for (i = 0; i < length; i += 1) {
28169             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28170         }
28171         
28172         if (tagType.ascii) {
28173             str = '';
28174             // Concatenate the chars:
28175             for (i = 0; i < values.length; i += 1) {
28176                 c = values[i];
28177                 // Ignore the terminating NULL byte(s):
28178                 if (c === '\u0000') {
28179                     break;
28180                 }
28181                 str += c;
28182             }
28183             return str;
28184         }
28185         return values;
28186     }
28187     
28188 });
28189
28190 Roo.apply(Roo.bootstrap.UploadCropbox, {
28191     tags : {
28192         'Orientation': 0x0112
28193     },
28194     
28195     Orientation: {
28196             1: 0, //'top-left',
28197 //            2: 'top-right',
28198             3: 180, //'bottom-right',
28199 //            4: 'bottom-left',
28200 //            5: 'left-top',
28201             6: 90, //'right-top',
28202 //            7: 'right-bottom',
28203             8: 270 //'left-bottom'
28204     },
28205     
28206     exifTagTypes : {
28207         // byte, 8-bit unsigned int:
28208         1: {
28209             getValue: function (dataView, dataOffset) {
28210                 return dataView.getUint8(dataOffset);
28211             },
28212             size: 1
28213         },
28214         // ascii, 8-bit byte:
28215         2: {
28216             getValue: function (dataView, dataOffset) {
28217                 return String.fromCharCode(dataView.getUint8(dataOffset));
28218             },
28219             size: 1,
28220             ascii: true
28221         },
28222         // short, 16 bit int:
28223         3: {
28224             getValue: function (dataView, dataOffset, littleEndian) {
28225                 return dataView.getUint16(dataOffset, littleEndian);
28226             },
28227             size: 2
28228         },
28229         // long, 32 bit int:
28230         4: {
28231             getValue: function (dataView, dataOffset, littleEndian) {
28232                 return dataView.getUint32(dataOffset, littleEndian);
28233             },
28234             size: 4
28235         },
28236         // rational = two long values, first is numerator, second is denominator:
28237         5: {
28238             getValue: function (dataView, dataOffset, littleEndian) {
28239                 return dataView.getUint32(dataOffset, littleEndian) /
28240                     dataView.getUint32(dataOffset + 4, littleEndian);
28241             },
28242             size: 8
28243         },
28244         // slong, 32 bit signed int:
28245         9: {
28246             getValue: function (dataView, dataOffset, littleEndian) {
28247                 return dataView.getInt32(dataOffset, littleEndian);
28248             },
28249             size: 4
28250         },
28251         // srational, two slongs, first is numerator, second is denominator:
28252         10: {
28253             getValue: function (dataView, dataOffset, littleEndian) {
28254                 return dataView.getInt32(dataOffset, littleEndian) /
28255                     dataView.getInt32(dataOffset + 4, littleEndian);
28256             },
28257             size: 8
28258         }
28259     },
28260     
28261     footer : {
28262         STANDARD : [
28263             {
28264                 tag : 'div',
28265                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28266                 action : 'rotate-left',
28267                 cn : [
28268                     {
28269                         tag : 'button',
28270                         cls : 'btn btn-default',
28271                         html : '<i class="fa fa-undo"></i>'
28272                     }
28273                 ]
28274             },
28275             {
28276                 tag : 'div',
28277                 cls : 'btn-group roo-upload-cropbox-picture',
28278                 action : 'picture',
28279                 cn : [
28280                     {
28281                         tag : 'button',
28282                         cls : 'btn btn-default',
28283                         html : '<i class="fa fa-picture-o"></i>'
28284                     }
28285                 ]
28286             },
28287             {
28288                 tag : 'div',
28289                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28290                 action : 'rotate-right',
28291                 cn : [
28292                     {
28293                         tag : 'button',
28294                         cls : 'btn btn-default',
28295                         html : '<i class="fa fa-repeat"></i>'
28296                     }
28297                 ]
28298             }
28299         ],
28300         DOCUMENT : [
28301             {
28302                 tag : 'div',
28303                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28304                 action : 'rotate-left',
28305                 cn : [
28306                     {
28307                         tag : 'button',
28308                         cls : 'btn btn-default',
28309                         html : '<i class="fa fa-undo"></i>'
28310                     }
28311                 ]
28312             },
28313             {
28314                 tag : 'div',
28315                 cls : 'btn-group roo-upload-cropbox-download',
28316                 action : 'download',
28317                 cn : [
28318                     {
28319                         tag : 'button',
28320                         cls : 'btn btn-default',
28321                         html : '<i class="fa fa-download"></i>'
28322                     }
28323                 ]
28324             },
28325             {
28326                 tag : 'div',
28327                 cls : 'btn-group roo-upload-cropbox-crop',
28328                 action : 'crop',
28329                 cn : [
28330                     {
28331                         tag : 'button',
28332                         cls : 'btn btn-default',
28333                         html : '<i class="fa fa-crop"></i>'
28334                     }
28335                 ]
28336             },
28337             {
28338                 tag : 'div',
28339                 cls : 'btn-group roo-upload-cropbox-trash',
28340                 action : 'trash',
28341                 cn : [
28342                     {
28343                         tag : 'button',
28344                         cls : 'btn btn-default',
28345                         html : '<i class="fa fa-trash"></i>'
28346                     }
28347                 ]
28348             },
28349             {
28350                 tag : 'div',
28351                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28352                 action : 'rotate-right',
28353                 cn : [
28354                     {
28355                         tag : 'button',
28356                         cls : 'btn btn-default',
28357                         html : '<i class="fa fa-repeat"></i>'
28358                     }
28359                 ]
28360             }
28361         ],
28362         ROTATOR : [
28363             {
28364                 tag : 'div',
28365                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28366                 action : 'rotate-left',
28367                 cn : [
28368                     {
28369                         tag : 'button',
28370                         cls : 'btn btn-default',
28371                         html : '<i class="fa fa-undo"></i>'
28372                     }
28373                 ]
28374             },
28375             {
28376                 tag : 'div',
28377                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28378                 action : 'rotate-right',
28379                 cn : [
28380                     {
28381                         tag : 'button',
28382                         cls : 'btn btn-default',
28383                         html : '<i class="fa fa-repeat"></i>'
28384                     }
28385                 ]
28386             }
28387         ]
28388     }
28389 });
28390
28391 /*
28392 * Licence: LGPL
28393 */
28394
28395 /**
28396  * @class Roo.bootstrap.DocumentManager
28397  * @extends Roo.bootstrap.Component
28398  * Bootstrap DocumentManager class
28399  * @cfg {String} paramName default 'imageUpload'
28400  * @cfg {String} toolTipName default 'filename'
28401  * @cfg {String} method default POST
28402  * @cfg {String} url action url
28403  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28404  * @cfg {Boolean} multiple multiple upload default true
28405  * @cfg {Number} thumbSize default 300
28406  * @cfg {String} fieldLabel
28407  * @cfg {Number} labelWidth default 4
28408  * @cfg {String} labelAlign (left|top) default left
28409  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28410 * @cfg {Number} labellg set the width of label (1-12)
28411  * @cfg {Number} labelmd set the width of label (1-12)
28412  * @cfg {Number} labelsm set the width of label (1-12)
28413  * @cfg {Number} labelxs set the width of label (1-12)
28414  * 
28415  * @constructor
28416  * Create a new DocumentManager
28417  * @param {Object} config The config object
28418  */
28419
28420 Roo.bootstrap.DocumentManager = function(config){
28421     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28422     
28423     this.files = [];
28424     this.delegates = [];
28425     
28426     this.addEvents({
28427         /**
28428          * @event initial
28429          * Fire when initial the DocumentManager
28430          * @param {Roo.bootstrap.DocumentManager} this
28431          */
28432         "initial" : true,
28433         /**
28434          * @event inspect
28435          * inspect selected file
28436          * @param {Roo.bootstrap.DocumentManager} this
28437          * @param {File} file
28438          */
28439         "inspect" : true,
28440         /**
28441          * @event exception
28442          * Fire when xhr load exception
28443          * @param {Roo.bootstrap.DocumentManager} this
28444          * @param {XMLHttpRequest} xhr
28445          */
28446         "exception" : true,
28447         /**
28448          * @event afterupload
28449          * Fire when xhr load exception
28450          * @param {Roo.bootstrap.DocumentManager} this
28451          * @param {XMLHttpRequest} xhr
28452          */
28453         "afterupload" : true,
28454         /**
28455          * @event prepare
28456          * prepare the form data
28457          * @param {Roo.bootstrap.DocumentManager} this
28458          * @param {Object} formData
28459          */
28460         "prepare" : true,
28461         /**
28462          * @event remove
28463          * Fire when remove the file
28464          * @param {Roo.bootstrap.DocumentManager} this
28465          * @param {Object} file
28466          */
28467         "remove" : true,
28468         /**
28469          * @event refresh
28470          * Fire after refresh the file
28471          * @param {Roo.bootstrap.DocumentManager} this
28472          */
28473         "refresh" : true,
28474         /**
28475          * @event click
28476          * Fire after click the image
28477          * @param {Roo.bootstrap.DocumentManager} this
28478          * @param {Object} file
28479          */
28480         "click" : true,
28481         /**
28482          * @event edit
28483          * Fire when upload a image and editable set to true
28484          * @param {Roo.bootstrap.DocumentManager} this
28485          * @param {Object} file
28486          */
28487         "edit" : true,
28488         /**
28489          * @event beforeselectfile
28490          * Fire before select file
28491          * @param {Roo.bootstrap.DocumentManager} this
28492          */
28493         "beforeselectfile" : true,
28494         /**
28495          * @event process
28496          * Fire before process file
28497          * @param {Roo.bootstrap.DocumentManager} this
28498          * @param {Object} file
28499          */
28500         "process" : true,
28501         /**
28502          * @event previewrendered
28503          * Fire when preview rendered
28504          * @param {Roo.bootstrap.DocumentManager} this
28505          * @param {Object} file
28506          */
28507         "previewrendered" : true
28508         
28509     });
28510 };
28511
28512 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28513     
28514     boxes : 0,
28515     inputName : '',
28516     thumbSize : 300,
28517     multiple : true,
28518     files : false,
28519     method : 'POST',
28520     url : '',
28521     paramName : 'imageUpload',
28522     toolTipName : 'filename',
28523     fieldLabel : '',
28524     labelWidth : 4,
28525     labelAlign : 'left',
28526     editable : true,
28527     delegates : false,
28528     xhr : false, 
28529     
28530     labellg : 0,
28531     labelmd : 0,
28532     labelsm : 0,
28533     labelxs : 0,
28534     
28535     getAutoCreate : function()
28536     {   
28537         var managerWidget = {
28538             tag : 'div',
28539             cls : 'roo-document-manager',
28540             cn : [
28541                 {
28542                     tag : 'input',
28543                     cls : 'roo-document-manager-selector',
28544                     type : 'file'
28545                 },
28546                 {
28547                     tag : 'div',
28548                     cls : 'roo-document-manager-uploader',
28549                     cn : [
28550                         {
28551                             tag : 'div',
28552                             cls : 'roo-document-manager-upload-btn',
28553                             html : '<i class="fa fa-plus"></i>'
28554                         }
28555                     ]
28556                     
28557                 }
28558             ]
28559         };
28560         
28561         var content = [
28562             {
28563                 tag : 'div',
28564                 cls : 'column col-md-12',
28565                 cn : managerWidget
28566             }
28567         ];
28568         
28569         if(this.fieldLabel.length){
28570             
28571             content = [
28572                 {
28573                     tag : 'div',
28574                     cls : 'column col-md-12',
28575                     html : this.fieldLabel
28576                 },
28577                 {
28578                     tag : 'div',
28579                     cls : 'column col-md-12',
28580                     cn : managerWidget
28581                 }
28582             ];
28583
28584             if(this.labelAlign == 'left'){
28585                 content = [
28586                     {
28587                         tag : 'div',
28588                         cls : 'column',
28589                         html : this.fieldLabel
28590                     },
28591                     {
28592                         tag : 'div',
28593                         cls : 'column',
28594                         cn : managerWidget
28595                     }
28596                 ];
28597                 
28598                 if(this.labelWidth > 12){
28599                     content[0].style = "width: " + this.labelWidth + 'px';
28600                 }
28601
28602                 if(this.labelWidth < 13 && this.labelmd == 0){
28603                     this.labelmd = this.labelWidth;
28604                 }
28605
28606                 if(this.labellg > 0){
28607                     content[0].cls += ' col-lg-' + this.labellg;
28608                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28609                 }
28610
28611                 if(this.labelmd > 0){
28612                     content[0].cls += ' col-md-' + this.labelmd;
28613                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28614                 }
28615
28616                 if(this.labelsm > 0){
28617                     content[0].cls += ' col-sm-' + this.labelsm;
28618                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28619                 }
28620
28621                 if(this.labelxs > 0){
28622                     content[0].cls += ' col-xs-' + this.labelxs;
28623                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28624                 }
28625                 
28626             }
28627         }
28628         
28629         var cfg = {
28630             tag : 'div',
28631             cls : 'row clearfix',
28632             cn : content
28633         };
28634         
28635         return cfg;
28636         
28637     },
28638     
28639     initEvents : function()
28640     {
28641         this.managerEl = this.el.select('.roo-document-manager', true).first();
28642         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28643         
28644         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28645         this.selectorEl.hide();
28646         
28647         if(this.multiple){
28648             this.selectorEl.attr('multiple', 'multiple');
28649         }
28650         
28651         this.selectorEl.on('change', this.onFileSelected, this);
28652         
28653         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28654         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28655         
28656         this.uploader.on('click', this.onUploaderClick, this);
28657         
28658         this.renderProgressDialog();
28659         
28660         var _this = this;
28661         
28662         window.addEventListener("resize", function() { _this.refresh(); } );
28663         
28664         this.fireEvent('initial', this);
28665     },
28666     
28667     renderProgressDialog : function()
28668     {
28669         var _this = this;
28670         
28671         this.progressDialog = new Roo.bootstrap.Modal({
28672             cls : 'roo-document-manager-progress-dialog',
28673             allow_close : false,
28674             title : '',
28675             buttons : [
28676                 {
28677                     name  :'cancel',
28678                     weight : 'danger',
28679                     html : 'Cancel'
28680                 }
28681             ], 
28682             listeners : { 
28683                 btnclick : function() {
28684                     _this.uploadCancel();
28685                     this.hide();
28686                 }
28687             }
28688         });
28689          
28690         this.progressDialog.render(Roo.get(document.body));
28691          
28692         this.progress = new Roo.bootstrap.Progress({
28693             cls : 'roo-document-manager-progress',
28694             active : true,
28695             striped : true
28696         });
28697         
28698         this.progress.render(this.progressDialog.getChildContainer());
28699         
28700         this.progressBar = new Roo.bootstrap.ProgressBar({
28701             cls : 'roo-document-manager-progress-bar',
28702             aria_valuenow : 0,
28703             aria_valuemin : 0,
28704             aria_valuemax : 12,
28705             panel : 'success'
28706         });
28707         
28708         this.progressBar.render(this.progress.getChildContainer());
28709     },
28710     
28711     onUploaderClick : function(e)
28712     {
28713         e.preventDefault();
28714      
28715         if(this.fireEvent('beforeselectfile', this) != false){
28716             this.selectorEl.dom.click();
28717         }
28718         
28719     },
28720     
28721     onFileSelected : function(e)
28722     {
28723         e.preventDefault();
28724         
28725         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28726             return;
28727         }
28728         
28729         Roo.each(this.selectorEl.dom.files, function(file){
28730             if(this.fireEvent('inspect', this, file) != false){
28731                 this.files.push(file);
28732             }
28733         }, this);
28734         
28735         this.queue();
28736         
28737     },
28738     
28739     queue : function()
28740     {
28741         this.selectorEl.dom.value = '';
28742         
28743         if(!this.files || !this.files.length){
28744             return;
28745         }
28746         
28747         if(this.boxes > 0 && this.files.length > this.boxes){
28748             this.files = this.files.slice(0, this.boxes);
28749         }
28750         
28751         this.uploader.show();
28752         
28753         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28754             this.uploader.hide();
28755         }
28756         
28757         var _this = this;
28758         
28759         var files = [];
28760         
28761         var docs = [];
28762         
28763         Roo.each(this.files, function(file){
28764             
28765             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28766                 var f = this.renderPreview(file);
28767                 files.push(f);
28768                 return;
28769             }
28770             
28771             if(file.type.indexOf('image') != -1){
28772                 this.delegates.push(
28773                     (function(){
28774                         _this.process(file);
28775                     }).createDelegate(this)
28776                 );
28777         
28778                 return;
28779             }
28780             
28781             docs.push(
28782                 (function(){
28783                     _this.process(file);
28784                 }).createDelegate(this)
28785             );
28786             
28787         }, this);
28788         
28789         this.files = files;
28790         
28791         this.delegates = this.delegates.concat(docs);
28792         
28793         if(!this.delegates.length){
28794             this.refresh();
28795             return;
28796         }
28797         
28798         this.progressBar.aria_valuemax = this.delegates.length;
28799         
28800         this.arrange();
28801         
28802         return;
28803     },
28804     
28805     arrange : function()
28806     {
28807         if(!this.delegates.length){
28808             this.progressDialog.hide();
28809             this.refresh();
28810             return;
28811         }
28812         
28813         var delegate = this.delegates.shift();
28814         
28815         this.progressDialog.show();
28816         
28817         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
28818         
28819         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
28820         
28821         delegate();
28822     },
28823     
28824     refresh : function()
28825     {
28826         this.uploader.show();
28827         
28828         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28829             this.uploader.hide();
28830         }
28831         
28832         Roo.isTouch ? this.closable(false) : this.closable(true);
28833         
28834         this.fireEvent('refresh', this);
28835     },
28836     
28837     onRemove : function(e, el, o)
28838     {
28839         e.preventDefault();
28840         
28841         this.fireEvent('remove', this, o);
28842         
28843     },
28844     
28845     remove : function(o)
28846     {
28847         var files = [];
28848         
28849         Roo.each(this.files, function(file){
28850             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
28851                 files.push(file);
28852                 return;
28853             }
28854
28855             o.target.remove();
28856
28857         }, this);
28858         
28859         this.files = files;
28860         
28861         this.refresh();
28862     },
28863     
28864     clear : function()
28865     {
28866         Roo.each(this.files, function(file){
28867             if(!file.target){
28868                 return;
28869             }
28870             
28871             file.target.remove();
28872
28873         }, this);
28874         
28875         this.files = [];
28876         
28877         this.refresh();
28878     },
28879     
28880     onClick : function(e, el, o)
28881     {
28882         e.preventDefault();
28883         
28884         this.fireEvent('click', this, o);
28885         
28886     },
28887     
28888     closable : function(closable)
28889     {
28890         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
28891             
28892             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28893             
28894             if(closable){
28895                 el.show();
28896                 return;
28897             }
28898             
28899             el.hide();
28900             
28901         }, this);
28902     },
28903     
28904     xhrOnLoad : function(xhr)
28905     {
28906         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28907             el.remove();
28908         }, this);
28909         
28910         if (xhr.readyState !== 4) {
28911             this.arrange();
28912             this.fireEvent('exception', this, xhr);
28913             return;
28914         }
28915
28916         var response = Roo.decode(xhr.responseText);
28917         
28918         if(!response.success){
28919             this.arrange();
28920             this.fireEvent('exception', this, xhr);
28921             return;
28922         }
28923         
28924         var file = this.renderPreview(response.data);
28925         
28926         this.files.push(file);
28927         
28928         this.arrange();
28929         
28930         this.fireEvent('afterupload', this, xhr);
28931         
28932     },
28933     
28934     xhrOnError : function(xhr)
28935     {
28936         Roo.log('xhr on error');
28937         
28938         var response = Roo.decode(xhr.responseText);
28939           
28940         Roo.log(response);
28941         
28942         this.arrange();
28943     },
28944     
28945     process : function(file)
28946     {
28947         if(this.fireEvent('process', this, file) !== false){
28948             if(this.editable && file.type.indexOf('image') != -1){
28949                 this.fireEvent('edit', this, file);
28950                 return;
28951             }
28952
28953             this.uploadStart(file, false);
28954
28955             return;
28956         }
28957         
28958     },
28959     
28960     uploadStart : function(file, crop)
28961     {
28962         this.xhr = new XMLHttpRequest();
28963         
28964         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28965             this.arrange();
28966             return;
28967         }
28968         
28969         file.xhr = this.xhr;
28970             
28971         this.managerEl.createChild({
28972             tag : 'div',
28973             cls : 'roo-document-manager-loading',
28974             cn : [
28975                 {
28976                     tag : 'div',
28977                     tooltip : file.name,
28978                     cls : 'roo-document-manager-thumb',
28979                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28980                 }
28981             ]
28982
28983         });
28984
28985         this.xhr.open(this.method, this.url, true);
28986         
28987         var headers = {
28988             "Accept": "application/json",
28989             "Cache-Control": "no-cache",
28990             "X-Requested-With": "XMLHttpRequest"
28991         };
28992         
28993         for (var headerName in headers) {
28994             var headerValue = headers[headerName];
28995             if (headerValue) {
28996                 this.xhr.setRequestHeader(headerName, headerValue);
28997             }
28998         }
28999         
29000         var _this = this;
29001         
29002         this.xhr.onload = function()
29003         {
29004             _this.xhrOnLoad(_this.xhr);
29005         }
29006         
29007         this.xhr.onerror = function()
29008         {
29009             _this.xhrOnError(_this.xhr);
29010         }
29011         
29012         var formData = new FormData();
29013
29014         formData.append('returnHTML', 'NO');
29015         
29016         if(crop){
29017             formData.append('crop', crop);
29018         }
29019         
29020         formData.append(this.paramName, file, file.name);
29021         
29022         var options = {
29023             file : file, 
29024             manually : false
29025         };
29026         
29027         if(this.fireEvent('prepare', this, formData, options) != false){
29028             
29029             if(options.manually){
29030                 return;
29031             }
29032             
29033             this.xhr.send(formData);
29034             return;
29035         };
29036         
29037         this.uploadCancel();
29038     },
29039     
29040     uploadCancel : function()
29041     {
29042         if (this.xhr) {
29043             this.xhr.abort();
29044         }
29045         
29046         this.delegates = [];
29047         
29048         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29049             el.remove();
29050         }, this);
29051         
29052         this.arrange();
29053     },
29054     
29055     renderPreview : function(file)
29056     {
29057         if(typeof(file.target) != 'undefined' && file.target){
29058             return file;
29059         }
29060         
29061         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29062         
29063         var previewEl = this.managerEl.createChild({
29064             tag : 'div',
29065             cls : 'roo-document-manager-preview',
29066             cn : [
29067                 {
29068                     tag : 'div',
29069                     tooltip : file[this.toolTipName],
29070                     cls : 'roo-document-manager-thumb',
29071                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29072                 },
29073                 {
29074                     tag : 'button',
29075                     cls : 'close',
29076                     html : '<i class="fa fa-times-circle"></i>'
29077                 }
29078             ]
29079         });
29080
29081         var close = previewEl.select('button.close', true).first();
29082
29083         close.on('click', this.onRemove, this, file);
29084
29085         file.target = previewEl;
29086
29087         var image = previewEl.select('img', true).first();
29088         
29089         var _this = this;
29090         
29091         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29092         
29093         image.on('click', this.onClick, this, file);
29094         
29095         this.fireEvent('previewrendered', this, file);
29096         
29097         return file;
29098         
29099     },
29100     
29101     onPreviewLoad : function(file, image)
29102     {
29103         if(typeof(file.target) == 'undefined' || !file.target){
29104             return;
29105         }
29106         
29107         var width = image.dom.naturalWidth || image.dom.width;
29108         var height = image.dom.naturalHeight || image.dom.height;
29109         
29110         if(width > height){
29111             file.target.addClass('wide');
29112             return;
29113         }
29114         
29115         file.target.addClass('tall');
29116         return;
29117         
29118     },
29119     
29120     uploadFromSource : function(file, crop)
29121     {
29122         this.xhr = new XMLHttpRequest();
29123         
29124         this.managerEl.createChild({
29125             tag : 'div',
29126             cls : 'roo-document-manager-loading',
29127             cn : [
29128                 {
29129                     tag : 'div',
29130                     tooltip : file.name,
29131                     cls : 'roo-document-manager-thumb',
29132                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29133                 }
29134             ]
29135
29136         });
29137
29138         this.xhr.open(this.method, this.url, true);
29139         
29140         var headers = {
29141             "Accept": "application/json",
29142             "Cache-Control": "no-cache",
29143             "X-Requested-With": "XMLHttpRequest"
29144         };
29145         
29146         for (var headerName in headers) {
29147             var headerValue = headers[headerName];
29148             if (headerValue) {
29149                 this.xhr.setRequestHeader(headerName, headerValue);
29150             }
29151         }
29152         
29153         var _this = this;
29154         
29155         this.xhr.onload = function()
29156         {
29157             _this.xhrOnLoad(_this.xhr);
29158         }
29159         
29160         this.xhr.onerror = function()
29161         {
29162             _this.xhrOnError(_this.xhr);
29163         }
29164         
29165         var formData = new FormData();
29166
29167         formData.append('returnHTML', 'NO');
29168         
29169         formData.append('crop', crop);
29170         
29171         if(typeof(file.filename) != 'undefined'){
29172             formData.append('filename', file.filename);
29173         }
29174         
29175         if(typeof(file.mimetype) != 'undefined'){
29176             formData.append('mimetype', file.mimetype);
29177         }
29178         
29179         Roo.log(formData);
29180         
29181         if(this.fireEvent('prepare', this, formData) != false){
29182             this.xhr.send(formData);
29183         };
29184     }
29185 });
29186
29187 /*
29188 * Licence: LGPL
29189 */
29190
29191 /**
29192  * @class Roo.bootstrap.DocumentViewer
29193  * @extends Roo.bootstrap.Component
29194  * Bootstrap DocumentViewer class
29195  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29196  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29197  * 
29198  * @constructor
29199  * Create a new DocumentViewer
29200  * @param {Object} config The config object
29201  */
29202
29203 Roo.bootstrap.DocumentViewer = function(config){
29204     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29205     
29206     this.addEvents({
29207         /**
29208          * @event initial
29209          * Fire after initEvent
29210          * @param {Roo.bootstrap.DocumentViewer} this
29211          */
29212         "initial" : true,
29213         /**
29214          * @event click
29215          * Fire after click
29216          * @param {Roo.bootstrap.DocumentViewer} this
29217          */
29218         "click" : true,
29219         /**
29220          * @event download
29221          * Fire after download button
29222          * @param {Roo.bootstrap.DocumentViewer} this
29223          */
29224         "download" : true,
29225         /**
29226          * @event trash
29227          * Fire after trash button
29228          * @param {Roo.bootstrap.DocumentViewer} this
29229          */
29230         "trash" : true
29231         
29232     });
29233 };
29234
29235 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29236     
29237     showDownload : true,
29238     
29239     showTrash : true,
29240     
29241     getAutoCreate : function()
29242     {
29243         var cfg = {
29244             tag : 'div',
29245             cls : 'roo-document-viewer',
29246             cn : [
29247                 {
29248                     tag : 'div',
29249                     cls : 'roo-document-viewer-body',
29250                     cn : [
29251                         {
29252                             tag : 'div',
29253                             cls : 'roo-document-viewer-thumb',
29254                             cn : [
29255                                 {
29256                                     tag : 'img',
29257                                     cls : 'roo-document-viewer-image'
29258                                 }
29259                             ]
29260                         }
29261                     ]
29262                 },
29263                 {
29264                     tag : 'div',
29265                     cls : 'roo-document-viewer-footer',
29266                     cn : {
29267                         tag : 'div',
29268                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29269                         cn : [
29270                             {
29271                                 tag : 'div',
29272                                 cls : 'btn-group roo-document-viewer-download',
29273                                 cn : [
29274                                     {
29275                                         tag : 'button',
29276                                         cls : 'btn btn-default',
29277                                         html : '<i class="fa fa-download"></i>'
29278                                     }
29279                                 ]
29280                             },
29281                             {
29282                                 tag : 'div',
29283                                 cls : 'btn-group roo-document-viewer-trash',
29284                                 cn : [
29285                                     {
29286                                         tag : 'button',
29287                                         cls : 'btn btn-default',
29288                                         html : '<i class="fa fa-trash"></i>'
29289                                     }
29290                                 ]
29291                             }
29292                         ]
29293                     }
29294                 }
29295             ]
29296         };
29297         
29298         return cfg;
29299     },
29300     
29301     initEvents : function()
29302     {
29303         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29304         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29305         
29306         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29307         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29308         
29309         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29310         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29311         
29312         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29313         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29314         
29315         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29316         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29317         
29318         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29319         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29320         
29321         this.bodyEl.on('click', this.onClick, this);
29322         this.downloadBtn.on('click', this.onDownload, this);
29323         this.trashBtn.on('click', this.onTrash, this);
29324         
29325         this.downloadBtn.hide();
29326         this.trashBtn.hide();
29327         
29328         if(this.showDownload){
29329             this.downloadBtn.show();
29330         }
29331         
29332         if(this.showTrash){
29333             this.trashBtn.show();
29334         }
29335         
29336         if(!this.showDownload && !this.showTrash) {
29337             this.footerEl.hide();
29338         }
29339         
29340     },
29341     
29342     initial : function()
29343     {
29344         this.fireEvent('initial', this);
29345         
29346     },
29347     
29348     onClick : function(e)
29349     {
29350         e.preventDefault();
29351         
29352         this.fireEvent('click', this);
29353     },
29354     
29355     onDownload : function(e)
29356     {
29357         e.preventDefault();
29358         
29359         this.fireEvent('download', this);
29360     },
29361     
29362     onTrash : function(e)
29363     {
29364         e.preventDefault();
29365         
29366         this.fireEvent('trash', this);
29367     }
29368     
29369 });
29370 /*
29371  * - LGPL
29372  *
29373  * nav progress bar
29374  * 
29375  */
29376
29377 /**
29378  * @class Roo.bootstrap.NavProgressBar
29379  * @extends Roo.bootstrap.Component
29380  * Bootstrap NavProgressBar class
29381  * 
29382  * @constructor
29383  * Create a new nav progress bar
29384  * @param {Object} config The config object
29385  */
29386
29387 Roo.bootstrap.NavProgressBar = function(config){
29388     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29389
29390     this.bullets = this.bullets || [];
29391    
29392 //    Roo.bootstrap.NavProgressBar.register(this);
29393      this.addEvents({
29394         /**
29395              * @event changed
29396              * Fires when the active item changes
29397              * @param {Roo.bootstrap.NavProgressBar} this
29398              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29399              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29400          */
29401         'changed': true
29402      });
29403     
29404 };
29405
29406 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29407     
29408     bullets : [],
29409     barItems : [],
29410     
29411     getAutoCreate : function()
29412     {
29413         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29414         
29415         cfg = {
29416             tag : 'div',
29417             cls : 'roo-navigation-bar-group',
29418             cn : [
29419                 {
29420                     tag : 'div',
29421                     cls : 'roo-navigation-top-bar'
29422                 },
29423                 {
29424                     tag : 'div',
29425                     cls : 'roo-navigation-bullets-bar',
29426                     cn : [
29427                         {
29428                             tag : 'ul',
29429                             cls : 'roo-navigation-bar'
29430                         }
29431                     ]
29432                 },
29433                 
29434                 {
29435                     tag : 'div',
29436                     cls : 'roo-navigation-bottom-bar'
29437                 }
29438             ]
29439             
29440         };
29441         
29442         return cfg;
29443         
29444     },
29445     
29446     initEvents: function() 
29447     {
29448         
29449     },
29450     
29451     onRender : function(ct, position) 
29452     {
29453         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29454         
29455         if(this.bullets.length){
29456             Roo.each(this.bullets, function(b){
29457                this.addItem(b);
29458             }, this);
29459         }
29460         
29461         this.format();
29462         
29463     },
29464     
29465     addItem : function(cfg)
29466     {
29467         var item = new Roo.bootstrap.NavProgressItem(cfg);
29468         
29469         item.parentId = this.id;
29470         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29471         
29472         if(cfg.html){
29473             var top = new Roo.bootstrap.Element({
29474                 tag : 'div',
29475                 cls : 'roo-navigation-bar-text'
29476             });
29477             
29478             var bottom = new Roo.bootstrap.Element({
29479                 tag : 'div',
29480                 cls : 'roo-navigation-bar-text'
29481             });
29482             
29483             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29484             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29485             
29486             var topText = new Roo.bootstrap.Element({
29487                 tag : 'span',
29488                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29489             });
29490             
29491             var bottomText = new Roo.bootstrap.Element({
29492                 tag : 'span',
29493                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29494             });
29495             
29496             topText.onRender(top.el, null);
29497             bottomText.onRender(bottom.el, null);
29498             
29499             item.topEl = top;
29500             item.bottomEl = bottom;
29501         }
29502         
29503         this.barItems.push(item);
29504         
29505         return item;
29506     },
29507     
29508     getActive : function()
29509     {
29510         var active = false;
29511         
29512         Roo.each(this.barItems, function(v){
29513             
29514             if (!v.isActive()) {
29515                 return;
29516             }
29517             
29518             active = v;
29519             return false;
29520             
29521         });
29522         
29523         return active;
29524     },
29525     
29526     setActiveItem : function(item)
29527     {
29528         var prev = false;
29529         
29530         Roo.each(this.barItems, function(v){
29531             if (v.rid == item.rid) {
29532                 return ;
29533             }
29534             
29535             if (v.isActive()) {
29536                 v.setActive(false);
29537                 prev = v;
29538             }
29539         });
29540
29541         item.setActive(true);
29542         
29543         this.fireEvent('changed', this, item, prev);
29544     },
29545     
29546     getBarItem: function(rid)
29547     {
29548         var ret = false;
29549         
29550         Roo.each(this.barItems, function(e) {
29551             if (e.rid != rid) {
29552                 return;
29553             }
29554             
29555             ret =  e;
29556             return false;
29557         });
29558         
29559         return ret;
29560     },
29561     
29562     indexOfItem : function(item)
29563     {
29564         var index = false;
29565         
29566         Roo.each(this.barItems, function(v, i){
29567             
29568             if (v.rid != item.rid) {
29569                 return;
29570             }
29571             
29572             index = i;
29573             return false
29574         });
29575         
29576         return index;
29577     },
29578     
29579     setActiveNext : function()
29580     {
29581         var i = this.indexOfItem(this.getActive());
29582         
29583         if (i > this.barItems.length) {
29584             return;
29585         }
29586         
29587         this.setActiveItem(this.barItems[i+1]);
29588     },
29589     
29590     setActivePrev : function()
29591     {
29592         var i = this.indexOfItem(this.getActive());
29593         
29594         if (i  < 1) {
29595             return;
29596         }
29597         
29598         this.setActiveItem(this.barItems[i-1]);
29599     },
29600     
29601     format : function()
29602     {
29603         if(!this.barItems.length){
29604             return;
29605         }
29606      
29607         var width = 100 / this.barItems.length;
29608         
29609         Roo.each(this.barItems, function(i){
29610             i.el.setStyle('width', width + '%');
29611             i.topEl.el.setStyle('width', width + '%');
29612             i.bottomEl.el.setStyle('width', width + '%');
29613         }, this);
29614         
29615     }
29616     
29617 });
29618 /*
29619  * - LGPL
29620  *
29621  * Nav Progress Item
29622  * 
29623  */
29624
29625 /**
29626  * @class Roo.bootstrap.NavProgressItem
29627  * @extends Roo.bootstrap.Component
29628  * Bootstrap NavProgressItem class
29629  * @cfg {String} rid the reference id
29630  * @cfg {Boolean} active (true|false) Is item active default false
29631  * @cfg {Boolean} disabled (true|false) Is item active default false
29632  * @cfg {String} html
29633  * @cfg {String} position (top|bottom) text position default bottom
29634  * @cfg {String} icon show icon instead of number
29635  * 
29636  * @constructor
29637  * Create a new NavProgressItem
29638  * @param {Object} config The config object
29639  */
29640 Roo.bootstrap.NavProgressItem = function(config){
29641     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29642     this.addEvents({
29643         // raw events
29644         /**
29645          * @event click
29646          * The raw click event for the entire grid.
29647          * @param {Roo.bootstrap.NavProgressItem} this
29648          * @param {Roo.EventObject} e
29649          */
29650         "click" : true
29651     });
29652    
29653 };
29654
29655 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29656     
29657     rid : '',
29658     active : false,
29659     disabled : false,
29660     html : '',
29661     position : 'bottom',
29662     icon : false,
29663     
29664     getAutoCreate : function()
29665     {
29666         var iconCls = 'roo-navigation-bar-item-icon';
29667         
29668         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29669         
29670         var cfg = {
29671             tag: 'li',
29672             cls: 'roo-navigation-bar-item',
29673             cn : [
29674                 {
29675                     tag : 'i',
29676                     cls : iconCls
29677                 }
29678             ]
29679         };
29680         
29681         if(this.active){
29682             cfg.cls += ' active';
29683         }
29684         if(this.disabled){
29685             cfg.cls += ' disabled';
29686         }
29687         
29688         return cfg;
29689     },
29690     
29691     disable : function()
29692     {
29693         this.setDisabled(true);
29694     },
29695     
29696     enable : function()
29697     {
29698         this.setDisabled(false);
29699     },
29700     
29701     initEvents: function() 
29702     {
29703         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29704         
29705         this.iconEl.on('click', this.onClick, this);
29706     },
29707     
29708     onClick : function(e)
29709     {
29710         e.preventDefault();
29711         
29712         if(this.disabled){
29713             return;
29714         }
29715         
29716         if(this.fireEvent('click', this, e) === false){
29717             return;
29718         };
29719         
29720         this.parent().setActiveItem(this);
29721     },
29722     
29723     isActive: function () 
29724     {
29725         return this.active;
29726     },
29727     
29728     setActive : function(state)
29729     {
29730         if(this.active == state){
29731             return;
29732         }
29733         
29734         this.active = state;
29735         
29736         if (state) {
29737             this.el.addClass('active');
29738             return;
29739         }
29740         
29741         this.el.removeClass('active');
29742         
29743         return;
29744     },
29745     
29746     setDisabled : function(state)
29747     {
29748         if(this.disabled == state){
29749             return;
29750         }
29751         
29752         this.disabled = state;
29753         
29754         if (state) {
29755             this.el.addClass('disabled');
29756             return;
29757         }
29758         
29759         this.el.removeClass('disabled');
29760     },
29761     
29762     tooltipEl : function()
29763     {
29764         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29765     }
29766 });
29767  
29768
29769  /*
29770  * - LGPL
29771  *
29772  * FieldLabel
29773  * 
29774  */
29775
29776 /**
29777  * @class Roo.bootstrap.FieldLabel
29778  * @extends Roo.bootstrap.Component
29779  * Bootstrap FieldLabel class
29780  * @cfg {String} html contents of the element
29781  * @cfg {String} tag tag of the element default label
29782  * @cfg {String} cls class of the element
29783  * @cfg {String} target label target 
29784  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29785  * @cfg {String} invalidClass default "text-warning"
29786  * @cfg {String} validClass default "text-success"
29787  * @cfg {String} iconTooltip default "This field is required"
29788  * @cfg {String} indicatorpos (left|right) default left
29789  * 
29790  * @constructor
29791  * Create a new FieldLabel
29792  * @param {Object} config The config object
29793  */
29794
29795 Roo.bootstrap.FieldLabel = function(config){
29796     Roo.bootstrap.Element.superclass.constructor.call(this, config);
29797     
29798     this.addEvents({
29799             /**
29800              * @event invalid
29801              * Fires after the field has been marked as invalid.
29802              * @param {Roo.form.FieldLabel} this
29803              * @param {String} msg The validation message
29804              */
29805             invalid : true,
29806             /**
29807              * @event valid
29808              * Fires after the field has been validated with no errors.
29809              * @param {Roo.form.FieldLabel} this
29810              */
29811             valid : true
29812         });
29813 };
29814
29815 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
29816     
29817     tag: 'label',
29818     cls: '',
29819     html: '',
29820     target: '',
29821     allowBlank : true,
29822     invalidClass : 'has-warning',
29823     validClass : 'has-success',
29824     iconTooltip : 'This field is required',
29825     indicatorpos : 'left',
29826     
29827     getAutoCreate : function(){
29828         
29829         var cfg = {
29830             tag : this.tag,
29831             cls : 'roo-bootstrap-field-label ' + this.cls,
29832             for : this.target,
29833             cn : [
29834                 {
29835                     tag : 'i',
29836                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
29837                     tooltip : this.iconTooltip
29838                 },
29839                 {
29840                     tag : 'span',
29841                     html : this.html
29842                 }
29843             ] 
29844         };
29845         
29846         if(this.indicatorpos == 'right'){
29847             var cfg = {
29848                 tag : this.tag,
29849                 cls : 'roo-bootstrap-field-label ' + this.cls,
29850                 for : this.target,
29851                 cn : [
29852                     {
29853                         tag : 'span',
29854                         html : this.html
29855                     },
29856                     {
29857                         tag : 'i',
29858                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
29859                         tooltip : this.iconTooltip
29860                     }
29861                 ] 
29862             };
29863         }
29864         
29865         return cfg;
29866     },
29867     
29868     initEvents: function() 
29869     {
29870         Roo.bootstrap.Element.superclass.initEvents.call(this);
29871         
29872         this.indicator = this.indicatorEl();
29873         
29874         if(this.indicator){
29875             this.indicator.removeClass('visible');
29876             this.indicator.addClass('invisible');
29877         }
29878         
29879         Roo.bootstrap.FieldLabel.register(this);
29880     },
29881     
29882     indicatorEl : function()
29883     {
29884         var indicator = this.el.select('i.roo-required-indicator',true).first();
29885         
29886         if(!indicator){
29887             return false;
29888         }
29889         
29890         return indicator;
29891         
29892     },
29893     
29894     /**
29895      * Mark this field as valid
29896      */
29897     markValid : function()
29898     {
29899         if(this.indicator){
29900             this.indicator.removeClass('visible');
29901             this.indicator.addClass('invisible');
29902         }
29903         
29904         this.el.removeClass(this.invalidClass);
29905         
29906         this.el.addClass(this.validClass);
29907         
29908         this.fireEvent('valid', this);
29909     },
29910     
29911     /**
29912      * Mark this field as invalid
29913      * @param {String} msg The validation message
29914      */
29915     markInvalid : function(msg)
29916     {
29917         if(this.indicator){
29918             this.indicator.removeClass('invisible');
29919             this.indicator.addClass('visible');
29920         }
29921         
29922         this.el.removeClass(this.validClass);
29923         
29924         this.el.addClass(this.invalidClass);
29925         
29926         this.fireEvent('invalid', this, msg);
29927     }
29928     
29929    
29930 });
29931
29932 Roo.apply(Roo.bootstrap.FieldLabel, {
29933     
29934     groups: {},
29935     
29936      /**
29937     * register a FieldLabel Group
29938     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
29939     */
29940     register : function(label)
29941     {
29942         if(this.groups.hasOwnProperty(label.target)){
29943             return;
29944         }
29945      
29946         this.groups[label.target] = label;
29947         
29948     },
29949     /**
29950     * fetch a FieldLabel Group based on the target
29951     * @param {string} target
29952     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
29953     */
29954     get: function(target) {
29955         if (typeof(this.groups[target]) == 'undefined') {
29956             return false;
29957         }
29958         
29959         return this.groups[target] ;
29960     }
29961 });
29962
29963  
29964
29965  /*
29966  * - LGPL
29967  *
29968  * page DateSplitField.
29969  * 
29970  */
29971
29972
29973 /**
29974  * @class Roo.bootstrap.DateSplitField
29975  * @extends Roo.bootstrap.Component
29976  * Bootstrap DateSplitField class
29977  * @cfg {string} fieldLabel - the label associated
29978  * @cfg {Number} labelWidth set the width of label (0-12)
29979  * @cfg {String} labelAlign (top|left)
29980  * @cfg {Boolean} dayAllowBlank (true|false) default false
29981  * @cfg {Boolean} monthAllowBlank (true|false) default false
29982  * @cfg {Boolean} yearAllowBlank (true|false) default false
29983  * @cfg {string} dayPlaceholder 
29984  * @cfg {string} monthPlaceholder
29985  * @cfg {string} yearPlaceholder
29986  * @cfg {string} dayFormat default 'd'
29987  * @cfg {string} monthFormat default 'm'
29988  * @cfg {string} yearFormat default 'Y'
29989  * @cfg {Number} labellg set the width of label (1-12)
29990  * @cfg {Number} labelmd set the width of label (1-12)
29991  * @cfg {Number} labelsm set the width of label (1-12)
29992  * @cfg {Number} labelxs set the width of label (1-12)
29993
29994  *     
29995  * @constructor
29996  * Create a new DateSplitField
29997  * @param {Object} config The config object
29998  */
29999
30000 Roo.bootstrap.DateSplitField = function(config){
30001     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30002     
30003     this.addEvents({
30004         // raw events
30005          /**
30006          * @event years
30007          * getting the data of years
30008          * @param {Roo.bootstrap.DateSplitField} this
30009          * @param {Object} years
30010          */
30011         "years" : true,
30012         /**
30013          * @event days
30014          * getting the data of days
30015          * @param {Roo.bootstrap.DateSplitField} this
30016          * @param {Object} days
30017          */
30018         "days" : true,
30019         /**
30020          * @event invalid
30021          * Fires after the field has been marked as invalid.
30022          * @param {Roo.form.Field} this
30023          * @param {String} msg The validation message
30024          */
30025         invalid : true,
30026        /**
30027          * @event valid
30028          * Fires after the field has been validated with no errors.
30029          * @param {Roo.form.Field} this
30030          */
30031         valid : true
30032     });
30033 };
30034
30035 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30036     
30037     fieldLabel : '',
30038     labelAlign : 'top',
30039     labelWidth : 3,
30040     dayAllowBlank : false,
30041     monthAllowBlank : false,
30042     yearAllowBlank : false,
30043     dayPlaceholder : '',
30044     monthPlaceholder : '',
30045     yearPlaceholder : '',
30046     dayFormat : 'd',
30047     monthFormat : 'm',
30048     yearFormat : 'Y',
30049     isFormField : true,
30050     labellg : 0,
30051     labelmd : 0,
30052     labelsm : 0,
30053     labelxs : 0,
30054     
30055     getAutoCreate : function()
30056     {
30057         var cfg = {
30058             tag : 'div',
30059             cls : 'row roo-date-split-field-group',
30060             cn : [
30061                 {
30062                     tag : 'input',
30063                     type : 'hidden',
30064                     cls : 'form-hidden-field roo-date-split-field-group-value',
30065                     name : this.name
30066                 }
30067             ]
30068         };
30069         
30070         var labelCls = 'col-md-12';
30071         var contentCls = 'col-md-4';
30072         
30073         if(this.fieldLabel){
30074             
30075             var label = {
30076                 tag : 'div',
30077                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30078                 cn : [
30079                     {
30080                         tag : 'label',
30081                         html : this.fieldLabel
30082                     }
30083                 ]
30084             };
30085             
30086             if(this.labelAlign == 'left'){
30087             
30088                 if(this.labelWidth > 12){
30089                     label.style = "width: " + this.labelWidth + 'px';
30090                 }
30091
30092                 if(this.labelWidth < 13 && this.labelmd == 0){
30093                     this.labelmd = this.labelWidth;
30094                 }
30095
30096                 if(this.labellg > 0){
30097                     labelCls = ' col-lg-' + this.labellg;
30098                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30099                 }
30100
30101                 if(this.labelmd > 0){
30102                     labelCls = ' col-md-' + this.labelmd;
30103                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30104                 }
30105
30106                 if(this.labelsm > 0){
30107                     labelCls = ' col-sm-' + this.labelsm;
30108                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30109                 }
30110
30111                 if(this.labelxs > 0){
30112                     labelCls = ' col-xs-' + this.labelxs;
30113                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30114                 }
30115             }
30116             
30117             label.cls += ' ' + labelCls;
30118             
30119             cfg.cn.push(label);
30120         }
30121         
30122         Roo.each(['day', 'month', 'year'], function(t){
30123             cfg.cn.push({
30124                 tag : 'div',
30125                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30126             });
30127         }, this);
30128         
30129         return cfg;
30130     },
30131     
30132     inputEl: function ()
30133     {
30134         return this.el.select('.roo-date-split-field-group-value', true).first();
30135     },
30136     
30137     onRender : function(ct, position) 
30138     {
30139         var _this = this;
30140         
30141         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30142         
30143         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30144         
30145         this.dayField = new Roo.bootstrap.ComboBox({
30146             allowBlank : this.dayAllowBlank,
30147             alwaysQuery : true,
30148             displayField : 'value',
30149             editable : false,
30150             fieldLabel : '',
30151             forceSelection : true,
30152             mode : 'local',
30153             placeholder : this.dayPlaceholder,
30154             selectOnFocus : true,
30155             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30156             triggerAction : 'all',
30157             typeAhead : true,
30158             valueField : 'value',
30159             store : new Roo.data.SimpleStore({
30160                 data : (function() {    
30161                     var days = [];
30162                     _this.fireEvent('days', _this, days);
30163                     return days;
30164                 })(),
30165                 fields : [ 'value' ]
30166             }),
30167             listeners : {
30168                 select : function (_self, record, index)
30169                 {
30170                     _this.setValue(_this.getValue());
30171                 }
30172             }
30173         });
30174
30175         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30176         
30177         this.monthField = new Roo.bootstrap.MonthField({
30178             after : '<i class=\"fa fa-calendar\"></i>',
30179             allowBlank : this.monthAllowBlank,
30180             placeholder : this.monthPlaceholder,
30181             readOnly : true,
30182             listeners : {
30183                 render : function (_self)
30184                 {
30185                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30186                         e.preventDefault();
30187                         _self.focus();
30188                     });
30189                 },
30190                 select : function (_self, oldvalue, newvalue)
30191                 {
30192                     _this.setValue(_this.getValue());
30193                 }
30194             }
30195         });
30196         
30197         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30198         
30199         this.yearField = new Roo.bootstrap.ComboBox({
30200             allowBlank : this.yearAllowBlank,
30201             alwaysQuery : true,
30202             displayField : 'value',
30203             editable : false,
30204             fieldLabel : '',
30205             forceSelection : true,
30206             mode : 'local',
30207             placeholder : this.yearPlaceholder,
30208             selectOnFocus : true,
30209             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30210             triggerAction : 'all',
30211             typeAhead : true,
30212             valueField : 'value',
30213             store : new Roo.data.SimpleStore({
30214                 data : (function() {
30215                     var years = [];
30216                     _this.fireEvent('years', _this, years);
30217                     return years;
30218                 })(),
30219                 fields : [ 'value' ]
30220             }),
30221             listeners : {
30222                 select : function (_self, record, index)
30223                 {
30224                     _this.setValue(_this.getValue());
30225                 }
30226             }
30227         });
30228
30229         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30230     },
30231     
30232     setValue : function(v, format)
30233     {
30234         this.inputEl.dom.value = v;
30235         
30236         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30237         
30238         var d = Date.parseDate(v, f);
30239         
30240         if(!d){
30241             this.validate();
30242             return;
30243         }
30244         
30245         this.setDay(d.format(this.dayFormat));
30246         this.setMonth(d.format(this.monthFormat));
30247         this.setYear(d.format(this.yearFormat));
30248         
30249         this.validate();
30250         
30251         return;
30252     },
30253     
30254     setDay : function(v)
30255     {
30256         this.dayField.setValue(v);
30257         this.inputEl.dom.value = this.getValue();
30258         this.validate();
30259         return;
30260     },
30261     
30262     setMonth : function(v)
30263     {
30264         this.monthField.setValue(v, true);
30265         this.inputEl.dom.value = this.getValue();
30266         this.validate();
30267         return;
30268     },
30269     
30270     setYear : function(v)
30271     {
30272         this.yearField.setValue(v);
30273         this.inputEl.dom.value = this.getValue();
30274         this.validate();
30275         return;
30276     },
30277     
30278     getDay : function()
30279     {
30280         return this.dayField.getValue();
30281     },
30282     
30283     getMonth : function()
30284     {
30285         return this.monthField.getValue();
30286     },
30287     
30288     getYear : function()
30289     {
30290         return this.yearField.getValue();
30291     },
30292     
30293     getValue : function()
30294     {
30295         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30296         
30297         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30298         
30299         return date;
30300     },
30301     
30302     reset : function()
30303     {
30304         this.setDay('');
30305         this.setMonth('');
30306         this.setYear('');
30307         this.inputEl.dom.value = '';
30308         this.validate();
30309         return;
30310     },
30311     
30312     validate : function()
30313     {
30314         var d = this.dayField.validate();
30315         var m = this.monthField.validate();
30316         var y = this.yearField.validate();
30317         
30318         var valid = true;
30319         
30320         if(
30321                 (!this.dayAllowBlank && !d) ||
30322                 (!this.monthAllowBlank && !m) ||
30323                 (!this.yearAllowBlank && !y)
30324         ){
30325             valid = false;
30326         }
30327         
30328         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30329             return valid;
30330         }
30331         
30332         if(valid){
30333             this.markValid();
30334             return valid;
30335         }
30336         
30337         this.markInvalid();
30338         
30339         return valid;
30340     },
30341     
30342     markValid : function()
30343     {
30344         
30345         var label = this.el.select('label', true).first();
30346         var icon = this.el.select('i.fa-star', true).first();
30347
30348         if(label && icon){
30349             icon.remove();
30350         }
30351         
30352         this.fireEvent('valid', this);
30353     },
30354     
30355      /**
30356      * Mark this field as invalid
30357      * @param {String} msg The validation message
30358      */
30359     markInvalid : function(msg)
30360     {
30361         
30362         var label = this.el.select('label', true).first();
30363         var icon = this.el.select('i.fa-star', true).first();
30364
30365         if(label && !icon){
30366             this.el.select('.roo-date-split-field-label', true).createChild({
30367                 tag : 'i',
30368                 cls : 'text-danger fa fa-lg fa-star',
30369                 tooltip : 'This field is required',
30370                 style : 'margin-right:5px;'
30371             }, label, true);
30372         }
30373         
30374         this.fireEvent('invalid', this, msg);
30375     },
30376     
30377     clearInvalid : function()
30378     {
30379         var label = this.el.select('label', true).first();
30380         var icon = this.el.select('i.fa-star', true).first();
30381
30382         if(label && icon){
30383             icon.remove();
30384         }
30385         
30386         this.fireEvent('valid', this);
30387     },
30388     
30389     getName: function()
30390     {
30391         return this.name;
30392     }
30393     
30394 });
30395
30396  /**
30397  *
30398  * This is based on 
30399  * http://masonry.desandro.com
30400  *
30401  * The idea is to render all the bricks based on vertical width...
30402  *
30403  * The original code extends 'outlayer' - we might need to use that....
30404  * 
30405  */
30406
30407
30408 /**
30409  * @class Roo.bootstrap.LayoutMasonry
30410  * @extends Roo.bootstrap.Component
30411  * Bootstrap Layout Masonry class
30412  * 
30413  * @constructor
30414  * Create a new Element
30415  * @param {Object} config The config object
30416  */
30417
30418 Roo.bootstrap.LayoutMasonry = function(config){
30419     
30420     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30421     
30422     this.bricks = [];
30423     
30424     Roo.bootstrap.LayoutMasonry.register(this);
30425     
30426     this.addEvents({
30427         // raw events
30428         /**
30429          * @event layout
30430          * Fire after layout the items
30431          * @param {Roo.bootstrap.LayoutMasonry} this
30432          * @param {Roo.EventObject} e
30433          */
30434         "layout" : true
30435     });
30436     
30437 };
30438
30439 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30440     
30441     /**
30442      * @cfg {Boolean} isLayoutInstant = no animation?
30443      */   
30444     isLayoutInstant : false, // needed?
30445    
30446     /**
30447      * @cfg {Number} boxWidth  width of the columns
30448      */   
30449     boxWidth : 450,
30450     
30451       /**
30452      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30453      */   
30454     boxHeight : 0,
30455     
30456     /**
30457      * @cfg {Number} padWidth padding below box..
30458      */   
30459     padWidth : 10, 
30460     
30461     /**
30462      * @cfg {Number} gutter gutter width..
30463      */   
30464     gutter : 10,
30465     
30466      /**
30467      * @cfg {Number} maxCols maximum number of columns
30468      */   
30469     
30470     maxCols: 0,
30471     
30472     /**
30473      * @cfg {Boolean} isAutoInitial defalut true
30474      */   
30475     isAutoInitial : true, 
30476     
30477     containerWidth: 0,
30478     
30479     /**
30480      * @cfg {Boolean} isHorizontal defalut false
30481      */   
30482     isHorizontal : false, 
30483
30484     currentSize : null,
30485     
30486     tag: 'div',
30487     
30488     cls: '',
30489     
30490     bricks: null, //CompositeElement
30491     
30492     cols : 1,
30493     
30494     _isLayoutInited : false,
30495     
30496 //    isAlternative : false, // only use for vertical layout...
30497     
30498     /**
30499      * @cfg {Number} alternativePadWidth padding below box..
30500      */   
30501     alternativePadWidth : 50,
30502     
30503     selectedBrick : [],
30504     
30505     getAutoCreate : function(){
30506         
30507         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30508         
30509         var cfg = {
30510             tag: this.tag,
30511             cls: 'blog-masonary-wrapper ' + this.cls,
30512             cn : {
30513                 cls : 'mas-boxes masonary'
30514             }
30515         };
30516         
30517         return cfg;
30518     },
30519     
30520     getChildContainer: function( )
30521     {
30522         if (this.boxesEl) {
30523             return this.boxesEl;
30524         }
30525         
30526         this.boxesEl = this.el.select('.mas-boxes').first();
30527         
30528         return this.boxesEl;
30529     },
30530     
30531     
30532     initEvents : function()
30533     {
30534         var _this = this;
30535         
30536         if(this.isAutoInitial){
30537             Roo.log('hook children rendered');
30538             this.on('childrenrendered', function() {
30539                 Roo.log('children rendered');
30540                 _this.initial();
30541             } ,this);
30542         }
30543     },
30544     
30545     initial : function()
30546     {
30547         this.selectedBrick = [];
30548         
30549         this.currentSize = this.el.getBox(true);
30550         
30551         Roo.EventManager.onWindowResize(this.resize, this); 
30552
30553         if(!this.isAutoInitial){
30554             this.layout();
30555             return;
30556         }
30557         
30558         this.layout();
30559         
30560         return;
30561         //this.layout.defer(500,this);
30562         
30563     },
30564     
30565     resize : function()
30566     {
30567         var cs = this.el.getBox(true);
30568         
30569         if (
30570                 this.currentSize.width == cs.width && 
30571                 this.currentSize.x == cs.x && 
30572                 this.currentSize.height == cs.height && 
30573                 this.currentSize.y == cs.y 
30574         ) {
30575             Roo.log("no change in with or X or Y");
30576             return;
30577         }
30578         
30579         this.currentSize = cs;
30580         
30581         this.layout();
30582         
30583     },
30584     
30585     layout : function()
30586     {   
30587         this._resetLayout();
30588         
30589         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30590         
30591         this.layoutItems( isInstant );
30592       
30593         this._isLayoutInited = true;
30594         
30595         this.fireEvent('layout', this);
30596         
30597     },
30598     
30599     _resetLayout : function()
30600     {
30601         if(this.isHorizontal){
30602             this.horizontalMeasureColumns();
30603             return;
30604         }
30605         
30606         this.verticalMeasureColumns();
30607         
30608     },
30609     
30610     verticalMeasureColumns : function()
30611     {
30612         this.getContainerWidth();
30613         
30614 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30615 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30616 //            return;
30617 //        }
30618         
30619         var boxWidth = this.boxWidth + this.padWidth;
30620         
30621         if(this.containerWidth < this.boxWidth){
30622             boxWidth = this.containerWidth
30623         }
30624         
30625         var containerWidth = this.containerWidth;
30626         
30627         var cols = Math.floor(containerWidth / boxWidth);
30628         
30629         this.cols = Math.max( cols, 1 );
30630         
30631         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30632         
30633         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30634         
30635         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30636         
30637         this.colWidth = boxWidth + avail - this.padWidth;
30638         
30639         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30640         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30641     },
30642     
30643     horizontalMeasureColumns : function()
30644     {
30645         this.getContainerWidth();
30646         
30647         var boxWidth = this.boxWidth;
30648         
30649         if(this.containerWidth < boxWidth){
30650             boxWidth = this.containerWidth;
30651         }
30652         
30653         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30654         
30655         this.el.setHeight(boxWidth);
30656         
30657     },
30658     
30659     getContainerWidth : function()
30660     {
30661         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30662     },
30663     
30664     layoutItems : function( isInstant )
30665     {
30666         Roo.log(this.bricks);
30667         
30668         var items = Roo.apply([], this.bricks);
30669         
30670         if(this.isHorizontal){
30671             this._horizontalLayoutItems( items , isInstant );
30672             return;
30673         }
30674         
30675 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30676 //            this._verticalAlternativeLayoutItems( items , isInstant );
30677 //            return;
30678 //        }
30679         
30680         this._verticalLayoutItems( items , isInstant );
30681         
30682     },
30683     
30684     _verticalLayoutItems : function ( items , isInstant)
30685     {
30686         if ( !items || !items.length ) {
30687             return;
30688         }
30689         
30690         var standard = [
30691             ['xs', 'xs', 'xs', 'tall'],
30692             ['xs', 'xs', 'tall'],
30693             ['xs', 'xs', 'sm'],
30694             ['xs', 'xs', 'xs'],
30695             ['xs', 'tall'],
30696             ['xs', 'sm'],
30697             ['xs', 'xs'],
30698             ['xs'],
30699             
30700             ['sm', 'xs', 'xs'],
30701             ['sm', 'xs'],
30702             ['sm'],
30703             
30704             ['tall', 'xs', 'xs', 'xs'],
30705             ['tall', 'xs', 'xs'],
30706             ['tall', 'xs'],
30707             ['tall']
30708             
30709         ];
30710         
30711         var queue = [];
30712         
30713         var boxes = [];
30714         
30715         var box = [];
30716         
30717         Roo.each(items, function(item, k){
30718             
30719             switch (item.size) {
30720                 // these layouts take up a full box,
30721                 case 'md' :
30722                 case 'md-left' :
30723                 case 'md-right' :
30724                 case 'wide' :
30725                     
30726                     if(box.length){
30727                         boxes.push(box);
30728                         box = [];
30729                     }
30730                     
30731                     boxes.push([item]);
30732                     
30733                     break;
30734                     
30735                 case 'xs' :
30736                 case 'sm' :
30737                 case 'tall' :
30738                     
30739                     box.push(item);
30740                     
30741                     break;
30742                 default :
30743                     break;
30744                     
30745             }
30746             
30747         }, this);
30748         
30749         if(box.length){
30750             boxes.push(box);
30751             box = [];
30752         }
30753         
30754         var filterPattern = function(box, length)
30755         {
30756             if(!box.length){
30757                 return;
30758             }
30759             
30760             var match = false;
30761             
30762             var pattern = box.slice(0, length);
30763             
30764             var format = [];
30765             
30766             Roo.each(pattern, function(i){
30767                 format.push(i.size);
30768             }, this);
30769             
30770             Roo.each(standard, function(s){
30771                 
30772                 if(String(s) != String(format)){
30773                     return;
30774                 }
30775                 
30776                 match = true;
30777                 return false;
30778                 
30779             }, this);
30780             
30781             if(!match && length == 1){
30782                 return;
30783             }
30784             
30785             if(!match){
30786                 filterPattern(box, length - 1);
30787                 return;
30788             }
30789                 
30790             queue.push(pattern);
30791
30792             box = box.slice(length, box.length);
30793
30794             filterPattern(box, 4);
30795
30796             return;
30797             
30798         }
30799         
30800         Roo.each(boxes, function(box, k){
30801             
30802             if(!box.length){
30803                 return;
30804             }
30805             
30806             if(box.length == 1){
30807                 queue.push(box);
30808                 return;
30809             }
30810             
30811             filterPattern(box, 4);
30812             
30813         }, this);
30814         
30815         this._processVerticalLayoutQueue( queue, isInstant );
30816         
30817     },
30818     
30819 //    _verticalAlternativeLayoutItems : function( items , isInstant )
30820 //    {
30821 //        if ( !items || !items.length ) {
30822 //            return;
30823 //        }
30824 //
30825 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
30826 //        
30827 //    },
30828     
30829     _horizontalLayoutItems : function ( items , isInstant)
30830     {
30831         if ( !items || !items.length || items.length < 3) {
30832             return;
30833         }
30834         
30835         items.reverse();
30836         
30837         var eItems = items.slice(0, 3);
30838         
30839         items = items.slice(3, items.length);
30840         
30841         var standard = [
30842             ['xs', 'xs', 'xs', 'wide'],
30843             ['xs', 'xs', 'wide'],
30844             ['xs', 'xs', 'sm'],
30845             ['xs', 'xs', 'xs'],
30846             ['xs', 'wide'],
30847             ['xs', 'sm'],
30848             ['xs', 'xs'],
30849             ['xs'],
30850             
30851             ['sm', 'xs', 'xs'],
30852             ['sm', 'xs'],
30853             ['sm'],
30854             
30855             ['wide', 'xs', 'xs', 'xs'],
30856             ['wide', 'xs', 'xs'],
30857             ['wide', 'xs'],
30858             ['wide'],
30859             
30860             ['wide-thin']
30861         ];
30862         
30863         var queue = [];
30864         
30865         var boxes = [];
30866         
30867         var box = [];
30868         
30869         Roo.each(items, function(item, k){
30870             
30871             switch (item.size) {
30872                 case 'md' :
30873                 case 'md-left' :
30874                 case 'md-right' :
30875                 case 'tall' :
30876                     
30877                     if(box.length){
30878                         boxes.push(box);
30879                         box = [];
30880                     }
30881                     
30882                     boxes.push([item]);
30883                     
30884                     break;
30885                     
30886                 case 'xs' :
30887                 case 'sm' :
30888                 case 'wide' :
30889                 case 'wide-thin' :
30890                     
30891                     box.push(item);
30892                     
30893                     break;
30894                 default :
30895                     break;
30896                     
30897             }
30898             
30899         }, this);
30900         
30901         if(box.length){
30902             boxes.push(box);
30903             box = [];
30904         }
30905         
30906         var filterPattern = function(box, length)
30907         {
30908             if(!box.length){
30909                 return;
30910             }
30911             
30912             var match = false;
30913             
30914             var pattern = box.slice(0, length);
30915             
30916             var format = [];
30917             
30918             Roo.each(pattern, function(i){
30919                 format.push(i.size);
30920             }, this);
30921             
30922             Roo.each(standard, function(s){
30923                 
30924                 if(String(s) != String(format)){
30925                     return;
30926                 }
30927                 
30928                 match = true;
30929                 return false;
30930                 
30931             }, this);
30932             
30933             if(!match && length == 1){
30934                 return;
30935             }
30936             
30937             if(!match){
30938                 filterPattern(box, length - 1);
30939                 return;
30940             }
30941                 
30942             queue.push(pattern);
30943
30944             box = box.slice(length, box.length);
30945
30946             filterPattern(box, 4);
30947
30948             return;
30949             
30950         }
30951         
30952         Roo.each(boxes, function(box, k){
30953             
30954             if(!box.length){
30955                 return;
30956             }
30957             
30958             if(box.length == 1){
30959                 queue.push(box);
30960                 return;
30961             }
30962             
30963             filterPattern(box, 4);
30964             
30965         }, this);
30966         
30967         
30968         var prune = [];
30969         
30970         var pos = this.el.getBox(true);
30971         
30972         var minX = pos.x;
30973         
30974         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30975         
30976         var hit_end = false;
30977         
30978         Roo.each(queue, function(box){
30979             
30980             if(hit_end){
30981                 
30982                 Roo.each(box, function(b){
30983                 
30984                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30985                     b.el.hide();
30986
30987                 }, this);
30988
30989                 return;
30990             }
30991             
30992             var mx = 0;
30993             
30994             Roo.each(box, function(b){
30995                 
30996                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
30997                 b.el.show();
30998
30999                 mx = Math.max(mx, b.x);
31000                 
31001             }, this);
31002             
31003             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31004             
31005             if(maxX < minX){
31006                 
31007                 Roo.each(box, function(b){
31008                 
31009                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31010                     b.el.hide();
31011                     
31012                 }, this);
31013                 
31014                 hit_end = true;
31015                 
31016                 return;
31017             }
31018             
31019             prune.push(box);
31020             
31021         }, this);
31022         
31023         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31024     },
31025     
31026     /** Sets position of item in DOM
31027     * @param {Element} item
31028     * @param {Number} x - horizontal position
31029     * @param {Number} y - vertical position
31030     * @param {Boolean} isInstant - disables transitions
31031     */
31032     _processVerticalLayoutQueue : function( queue, isInstant )
31033     {
31034         var pos = this.el.getBox(true);
31035         var x = pos.x;
31036         var y = pos.y;
31037         var maxY = [];
31038         
31039         for (var i = 0; i < this.cols; i++){
31040             maxY[i] = pos.y;
31041         }
31042         
31043         Roo.each(queue, function(box, k){
31044             
31045             var col = k % this.cols;
31046             
31047             Roo.each(box, function(b,kk){
31048                 
31049                 b.el.position('absolute');
31050                 
31051                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31052                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31053                 
31054                 if(b.size == 'md-left' || b.size == 'md-right'){
31055                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31056                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31057                 }
31058                 
31059                 b.el.setWidth(width);
31060                 b.el.setHeight(height);
31061                 // iframe?
31062                 b.el.select('iframe',true).setSize(width,height);
31063                 
31064             }, this);
31065             
31066             for (var i = 0; i < this.cols; i++){
31067                 
31068                 if(maxY[i] < maxY[col]){
31069                     col = i;
31070                     continue;
31071                 }
31072                 
31073                 col = Math.min(col, i);
31074                 
31075             }
31076             
31077             x = pos.x + col * (this.colWidth + this.padWidth);
31078             
31079             y = maxY[col];
31080             
31081             var positions = [];
31082             
31083             switch (box.length){
31084                 case 1 :
31085                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31086                     break;
31087                 case 2 :
31088                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31089                     break;
31090                 case 3 :
31091                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31092                     break;
31093                 case 4 :
31094                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31095                     break;
31096                 default :
31097                     break;
31098             }
31099             
31100             Roo.each(box, function(b,kk){
31101                 
31102                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31103                 
31104                 var sz = b.el.getSize();
31105                 
31106                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31107                 
31108             }, this);
31109             
31110         }, this);
31111         
31112         var mY = 0;
31113         
31114         for (var i = 0; i < this.cols; i++){
31115             mY = Math.max(mY, maxY[i]);
31116         }
31117         
31118         this.el.setHeight(mY - pos.y);
31119         
31120     },
31121     
31122 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31123 //    {
31124 //        var pos = this.el.getBox(true);
31125 //        var x = pos.x;
31126 //        var y = pos.y;
31127 //        var maxX = pos.right;
31128 //        
31129 //        var maxHeight = 0;
31130 //        
31131 //        Roo.each(items, function(item, k){
31132 //            
31133 //            var c = k % 2;
31134 //            
31135 //            item.el.position('absolute');
31136 //                
31137 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31138 //
31139 //            item.el.setWidth(width);
31140 //
31141 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31142 //
31143 //            item.el.setHeight(height);
31144 //            
31145 //            if(c == 0){
31146 //                item.el.setXY([x, y], isInstant ? false : true);
31147 //            } else {
31148 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31149 //            }
31150 //            
31151 //            y = y + height + this.alternativePadWidth;
31152 //            
31153 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31154 //            
31155 //        }, this);
31156 //        
31157 //        this.el.setHeight(maxHeight);
31158 //        
31159 //    },
31160     
31161     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31162     {
31163         var pos = this.el.getBox(true);
31164         
31165         var minX = pos.x;
31166         var minY = pos.y;
31167         
31168         var maxX = pos.right;
31169         
31170         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31171         
31172         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31173         
31174         Roo.each(queue, function(box, k){
31175             
31176             Roo.each(box, function(b, kk){
31177                 
31178                 b.el.position('absolute');
31179                 
31180                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31181                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31182                 
31183                 if(b.size == 'md-left' || b.size == 'md-right'){
31184                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31185                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31186                 }
31187                 
31188                 b.el.setWidth(width);
31189                 b.el.setHeight(height);
31190                 
31191             }, this);
31192             
31193             if(!box.length){
31194                 return;
31195             }
31196             
31197             var positions = [];
31198             
31199             switch (box.length){
31200                 case 1 :
31201                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31202                     break;
31203                 case 2 :
31204                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31205                     break;
31206                 case 3 :
31207                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31208                     break;
31209                 case 4 :
31210                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31211                     break;
31212                 default :
31213                     break;
31214             }
31215             
31216             Roo.each(box, function(b,kk){
31217                 
31218                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31219                 
31220                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31221                 
31222             }, this);
31223             
31224         }, this);
31225         
31226     },
31227     
31228     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31229     {
31230         Roo.each(eItems, function(b,k){
31231             
31232             b.size = (k == 0) ? 'sm' : 'xs';
31233             b.x = (k == 0) ? 2 : 1;
31234             b.y = (k == 0) ? 2 : 1;
31235             
31236             b.el.position('absolute');
31237             
31238             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31239                 
31240             b.el.setWidth(width);
31241             
31242             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31243             
31244             b.el.setHeight(height);
31245             
31246         }, this);
31247
31248         var positions = [];
31249         
31250         positions.push({
31251             x : maxX - this.unitWidth * 2 - this.gutter,
31252             y : minY
31253         });
31254         
31255         positions.push({
31256             x : maxX - this.unitWidth,
31257             y : minY + (this.unitWidth + this.gutter) * 2
31258         });
31259         
31260         positions.push({
31261             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31262             y : minY
31263         });
31264         
31265         Roo.each(eItems, function(b,k){
31266             
31267             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31268
31269         }, this);
31270         
31271     },
31272     
31273     getVerticalOneBoxColPositions : function(x, y, box)
31274     {
31275         var pos = [];
31276         
31277         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31278         
31279         if(box[0].size == 'md-left'){
31280             rand = 0;
31281         }
31282         
31283         if(box[0].size == 'md-right'){
31284             rand = 1;
31285         }
31286         
31287         pos.push({
31288             x : x + (this.unitWidth + this.gutter) * rand,
31289             y : y
31290         });
31291         
31292         return pos;
31293     },
31294     
31295     getVerticalTwoBoxColPositions : function(x, y, box)
31296     {
31297         var pos = [];
31298         
31299         if(box[0].size == 'xs'){
31300             
31301             pos.push({
31302                 x : x,
31303                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31304             });
31305
31306             pos.push({
31307                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31308                 y : y
31309             });
31310             
31311             return pos;
31312             
31313         }
31314         
31315         pos.push({
31316             x : x,
31317             y : y
31318         });
31319
31320         pos.push({
31321             x : x + (this.unitWidth + this.gutter) * 2,
31322             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31323         });
31324         
31325         return pos;
31326         
31327     },
31328     
31329     getVerticalThreeBoxColPositions : function(x, y, box)
31330     {
31331         var pos = [];
31332         
31333         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31334             
31335             pos.push({
31336                 x : x,
31337                 y : y
31338             });
31339
31340             pos.push({
31341                 x : x + (this.unitWidth + this.gutter) * 1,
31342                 y : y
31343             });
31344             
31345             pos.push({
31346                 x : x + (this.unitWidth + this.gutter) * 2,
31347                 y : y
31348             });
31349             
31350             return pos;
31351             
31352         }
31353         
31354         if(box[0].size == 'xs' && box[1].size == 'xs'){
31355             
31356             pos.push({
31357                 x : x,
31358                 y : y
31359             });
31360
31361             pos.push({
31362                 x : x,
31363                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31364             });
31365             
31366             pos.push({
31367                 x : x + (this.unitWidth + this.gutter) * 1,
31368                 y : y
31369             });
31370             
31371             return pos;
31372             
31373         }
31374         
31375         pos.push({
31376             x : x,
31377             y : y
31378         });
31379
31380         pos.push({
31381             x : x + (this.unitWidth + this.gutter) * 2,
31382             y : y
31383         });
31384
31385         pos.push({
31386             x : x + (this.unitWidth + this.gutter) * 2,
31387             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31388         });
31389             
31390         return pos;
31391         
31392     },
31393     
31394     getVerticalFourBoxColPositions : function(x, y, box)
31395     {
31396         var pos = [];
31397         
31398         if(box[0].size == 'xs'){
31399             
31400             pos.push({
31401                 x : x,
31402                 y : y
31403             });
31404
31405             pos.push({
31406                 x : x,
31407                 y : y + (this.unitHeight + this.gutter) * 1
31408             });
31409             
31410             pos.push({
31411                 x : x,
31412                 y : y + (this.unitHeight + this.gutter) * 2
31413             });
31414             
31415             pos.push({
31416                 x : x + (this.unitWidth + this.gutter) * 1,
31417                 y : y
31418             });
31419             
31420             return pos;
31421             
31422         }
31423         
31424         pos.push({
31425             x : x,
31426             y : y
31427         });
31428
31429         pos.push({
31430             x : x + (this.unitWidth + this.gutter) * 2,
31431             y : y
31432         });
31433
31434         pos.push({
31435             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31436             y : y + (this.unitHeight + this.gutter) * 1
31437         });
31438
31439         pos.push({
31440             x : x + (this.unitWidth + this.gutter) * 2,
31441             y : y + (this.unitWidth + this.gutter) * 2
31442         });
31443
31444         return pos;
31445         
31446     },
31447     
31448     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31449     {
31450         var pos = [];
31451         
31452         if(box[0].size == 'md-left'){
31453             pos.push({
31454                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31455                 y : minY
31456             });
31457             
31458             return pos;
31459         }
31460         
31461         if(box[0].size == 'md-right'){
31462             pos.push({
31463                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31464                 y : minY + (this.unitWidth + this.gutter) * 1
31465             });
31466             
31467             return pos;
31468         }
31469         
31470         var rand = Math.floor(Math.random() * (4 - box[0].y));
31471         
31472         pos.push({
31473             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31474             y : minY + (this.unitWidth + this.gutter) * rand
31475         });
31476         
31477         return pos;
31478         
31479     },
31480     
31481     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31482     {
31483         var pos = [];
31484         
31485         if(box[0].size == 'xs'){
31486             
31487             pos.push({
31488                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31489                 y : minY
31490             });
31491
31492             pos.push({
31493                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31494                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31495             });
31496             
31497             return pos;
31498             
31499         }
31500         
31501         pos.push({
31502             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31503             y : minY
31504         });
31505
31506         pos.push({
31507             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31508             y : minY + (this.unitWidth + this.gutter) * 2
31509         });
31510         
31511         return pos;
31512         
31513     },
31514     
31515     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31516     {
31517         var pos = [];
31518         
31519         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31520             
31521             pos.push({
31522                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31523                 y : minY
31524             });
31525
31526             pos.push({
31527                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31528                 y : minY + (this.unitWidth + this.gutter) * 1
31529             });
31530             
31531             pos.push({
31532                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31533                 y : minY + (this.unitWidth + this.gutter) * 2
31534             });
31535             
31536             return pos;
31537             
31538         }
31539         
31540         if(box[0].size == 'xs' && box[1].size == 'xs'){
31541             
31542             pos.push({
31543                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31544                 y : minY
31545             });
31546
31547             pos.push({
31548                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31549                 y : minY
31550             });
31551             
31552             pos.push({
31553                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31554                 y : minY + (this.unitWidth + this.gutter) * 1
31555             });
31556             
31557             return pos;
31558             
31559         }
31560         
31561         pos.push({
31562             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31563             y : minY
31564         });
31565
31566         pos.push({
31567             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31568             y : minY + (this.unitWidth + this.gutter) * 2
31569         });
31570
31571         pos.push({
31572             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31573             y : minY + (this.unitWidth + this.gutter) * 2
31574         });
31575             
31576         return pos;
31577         
31578     },
31579     
31580     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31581     {
31582         var pos = [];
31583         
31584         if(box[0].size == 'xs'){
31585             
31586             pos.push({
31587                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31588                 y : minY
31589             });
31590
31591             pos.push({
31592                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31593                 y : minY
31594             });
31595             
31596             pos.push({
31597                 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),
31598                 y : minY
31599             });
31600             
31601             pos.push({
31602                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31603                 y : minY + (this.unitWidth + this.gutter) * 1
31604             });
31605             
31606             return pos;
31607             
31608         }
31609         
31610         pos.push({
31611             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31612             y : minY
31613         });
31614         
31615         pos.push({
31616             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31617             y : minY + (this.unitWidth + this.gutter) * 2
31618         });
31619         
31620         pos.push({
31621             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31622             y : minY + (this.unitWidth + this.gutter) * 2
31623         });
31624         
31625         pos.push({
31626             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),
31627             y : minY + (this.unitWidth + this.gutter) * 2
31628         });
31629
31630         return pos;
31631         
31632     },
31633     
31634     /**
31635     * remove a Masonry Brick
31636     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31637     */
31638     removeBrick : function(brick_id)
31639     {
31640         if (!brick_id) {
31641             return;
31642         }
31643         
31644         for (var i = 0; i<this.bricks.length; i++) {
31645             if (this.bricks[i].id == brick_id) {
31646                 this.bricks.splice(i,1);
31647                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31648                 this.initial();
31649             }
31650         }
31651     },
31652     
31653     /**
31654     * adds a Masonry Brick
31655     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31656     */
31657     addBrick : function(cfg)
31658     {
31659         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31660         //this.register(cn);
31661         cn.parentId = this.id;
31662         cn.onRender(this.el, null);
31663         return cn;
31664     },
31665     
31666     /**
31667     * register a Masonry Brick
31668     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31669     */
31670     
31671     register : function(brick)
31672     {
31673         this.bricks.push(brick);
31674         brick.masonryId = this.id;
31675     },
31676     
31677     /**
31678     * clear all the Masonry Brick
31679     */
31680     clearAll : function()
31681     {
31682         this.bricks = [];
31683         //this.getChildContainer().dom.innerHTML = "";
31684         this.el.dom.innerHTML = '';
31685     },
31686     
31687     getSelected : function()
31688     {
31689         if (!this.selectedBrick) {
31690             return false;
31691         }
31692         
31693         return this.selectedBrick;
31694     }
31695 });
31696
31697 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31698     
31699     groups: {},
31700      /**
31701     * register a Masonry Layout
31702     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31703     */
31704     
31705     register : function(layout)
31706     {
31707         this.groups[layout.id] = layout;
31708     },
31709     /**
31710     * fetch a  Masonry Layout based on the masonry layout ID
31711     * @param {string} the masonry layout to add
31712     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31713     */
31714     
31715     get: function(layout_id) {
31716         if (typeof(this.groups[layout_id]) == 'undefined') {
31717             return false;
31718         }
31719         return this.groups[layout_id] ;
31720     }
31721     
31722     
31723     
31724 });
31725
31726  
31727
31728  /**
31729  *
31730  * This is based on 
31731  * http://masonry.desandro.com
31732  *
31733  * The idea is to render all the bricks based on vertical width...
31734  *
31735  * The original code extends 'outlayer' - we might need to use that....
31736  * 
31737  */
31738
31739
31740 /**
31741  * @class Roo.bootstrap.LayoutMasonryAuto
31742  * @extends Roo.bootstrap.Component
31743  * Bootstrap Layout Masonry class
31744  * 
31745  * @constructor
31746  * Create a new Element
31747  * @param {Object} config The config object
31748  */
31749
31750 Roo.bootstrap.LayoutMasonryAuto = function(config){
31751     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31752 };
31753
31754 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
31755     
31756       /**
31757      * @cfg {Boolean} isFitWidth  - resize the width..
31758      */   
31759     isFitWidth : false,  // options..
31760     /**
31761      * @cfg {Boolean} isOriginLeft = left align?
31762      */   
31763     isOriginLeft : true,
31764     /**
31765      * @cfg {Boolean} isOriginTop = top align?
31766      */   
31767     isOriginTop : false,
31768     /**
31769      * @cfg {Boolean} isLayoutInstant = no animation?
31770      */   
31771     isLayoutInstant : false, // needed?
31772     /**
31773      * @cfg {Boolean} isResizingContainer = not sure if this is used..
31774      */   
31775     isResizingContainer : true,
31776     /**
31777      * @cfg {Number} columnWidth  width of the columns 
31778      */   
31779     
31780     columnWidth : 0,
31781     
31782     /**
31783      * @cfg {Number} maxCols maximum number of columns
31784      */   
31785     
31786     maxCols: 0,
31787     /**
31788      * @cfg {Number} padHeight padding below box..
31789      */   
31790     
31791     padHeight : 10, 
31792     
31793     /**
31794      * @cfg {Boolean} isAutoInitial defalut true
31795      */   
31796     
31797     isAutoInitial : true, 
31798     
31799     // private?
31800     gutter : 0,
31801     
31802     containerWidth: 0,
31803     initialColumnWidth : 0,
31804     currentSize : null,
31805     
31806     colYs : null, // array.
31807     maxY : 0,
31808     padWidth: 10,
31809     
31810     
31811     tag: 'div',
31812     cls: '',
31813     bricks: null, //CompositeElement
31814     cols : 0, // array?
31815     // element : null, // wrapped now this.el
31816     _isLayoutInited : null, 
31817     
31818     
31819     getAutoCreate : function(){
31820         
31821         var cfg = {
31822             tag: this.tag,
31823             cls: 'blog-masonary-wrapper ' + this.cls,
31824             cn : {
31825                 cls : 'mas-boxes masonary'
31826             }
31827         };
31828         
31829         return cfg;
31830     },
31831     
31832     getChildContainer: function( )
31833     {
31834         if (this.boxesEl) {
31835             return this.boxesEl;
31836         }
31837         
31838         this.boxesEl = this.el.select('.mas-boxes').first();
31839         
31840         return this.boxesEl;
31841     },
31842     
31843     
31844     initEvents : function()
31845     {
31846         var _this = this;
31847         
31848         if(this.isAutoInitial){
31849             Roo.log('hook children rendered');
31850             this.on('childrenrendered', function() {
31851                 Roo.log('children rendered');
31852                 _this.initial();
31853             } ,this);
31854         }
31855         
31856     },
31857     
31858     initial : function()
31859     {
31860         this.reloadItems();
31861
31862         this.currentSize = this.el.getBox(true);
31863
31864         /// was window resize... - let's see if this works..
31865         Roo.EventManager.onWindowResize(this.resize, this); 
31866
31867         if(!this.isAutoInitial){
31868             this.layout();
31869             return;
31870         }
31871         
31872         this.layout.defer(500,this);
31873     },
31874     
31875     reloadItems: function()
31876     {
31877         this.bricks = this.el.select('.masonry-brick', true);
31878         
31879         this.bricks.each(function(b) {
31880             //Roo.log(b.getSize());
31881             if (!b.attr('originalwidth')) {
31882                 b.attr('originalwidth',  b.getSize().width);
31883             }
31884             
31885         });
31886         
31887         Roo.log(this.bricks.elements.length);
31888     },
31889     
31890     resize : function()
31891     {
31892         Roo.log('resize');
31893         var cs = this.el.getBox(true);
31894         
31895         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
31896             Roo.log("no change in with or X");
31897             return;
31898         }
31899         this.currentSize = cs;
31900         this.layout();
31901     },
31902     
31903     layout : function()
31904     {
31905          Roo.log('layout');
31906         this._resetLayout();
31907         //this._manageStamps();
31908       
31909         // don't animate first layout
31910         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31911         this.layoutItems( isInstant );
31912       
31913         // flag for initalized
31914         this._isLayoutInited = true;
31915     },
31916     
31917     layoutItems : function( isInstant )
31918     {
31919         //var items = this._getItemsForLayout( this.items );
31920         // original code supports filtering layout items.. we just ignore it..
31921         
31922         this._layoutItems( this.bricks , isInstant );
31923       
31924         this._postLayout();
31925     },
31926     _layoutItems : function ( items , isInstant)
31927     {
31928        //this.fireEvent( 'layout', this, items );
31929     
31930
31931         if ( !items || !items.elements.length ) {
31932           // no items, emit event with empty array
31933             return;
31934         }
31935
31936         var queue = [];
31937         items.each(function(item) {
31938             Roo.log("layout item");
31939             Roo.log(item);
31940             // get x/y object from method
31941             var position = this._getItemLayoutPosition( item );
31942             // enqueue
31943             position.item = item;
31944             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
31945             queue.push( position );
31946         }, this);
31947       
31948         this._processLayoutQueue( queue );
31949     },
31950     /** Sets position of item in DOM
31951     * @param {Element} item
31952     * @param {Number} x - horizontal position
31953     * @param {Number} y - vertical position
31954     * @param {Boolean} isInstant - disables transitions
31955     */
31956     _processLayoutQueue : function( queue )
31957     {
31958         for ( var i=0, len = queue.length; i < len; i++ ) {
31959             var obj = queue[i];
31960             obj.item.position('absolute');
31961             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
31962         }
31963     },
31964       
31965     
31966     /**
31967     * Any logic you want to do after each layout,
31968     * i.e. size the container
31969     */
31970     _postLayout : function()
31971     {
31972         this.resizeContainer();
31973     },
31974     
31975     resizeContainer : function()
31976     {
31977         if ( !this.isResizingContainer ) {
31978             return;
31979         }
31980         var size = this._getContainerSize();
31981         if ( size ) {
31982             this.el.setSize(size.width,size.height);
31983             this.boxesEl.setSize(size.width,size.height);
31984         }
31985     },
31986     
31987     
31988     
31989     _resetLayout : function()
31990     {
31991         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
31992         this.colWidth = this.el.getWidth();
31993         //this.gutter = this.el.getWidth(); 
31994         
31995         this.measureColumns();
31996
31997         // reset column Y
31998         var i = this.cols;
31999         this.colYs = [];
32000         while (i--) {
32001             this.colYs.push( 0 );
32002         }
32003     
32004         this.maxY = 0;
32005     },
32006
32007     measureColumns : function()
32008     {
32009         this.getContainerWidth();
32010       // if columnWidth is 0, default to outerWidth of first item
32011         if ( !this.columnWidth ) {
32012             var firstItem = this.bricks.first();
32013             Roo.log(firstItem);
32014             this.columnWidth  = this.containerWidth;
32015             if (firstItem && firstItem.attr('originalwidth') ) {
32016                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32017             }
32018             // columnWidth fall back to item of first element
32019             Roo.log("set column width?");
32020                         this.initialColumnWidth = this.columnWidth  ;
32021
32022             // if first elem has no width, default to size of container
32023             
32024         }
32025         
32026         
32027         if (this.initialColumnWidth) {
32028             this.columnWidth = this.initialColumnWidth;
32029         }
32030         
32031         
32032             
32033         // column width is fixed at the top - however if container width get's smaller we should
32034         // reduce it...
32035         
32036         // this bit calcs how man columns..
32037             
32038         var columnWidth = this.columnWidth += this.gutter;
32039       
32040         // calculate columns
32041         var containerWidth = this.containerWidth + this.gutter;
32042         
32043         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32044         // fix rounding errors, typically with gutters
32045         var excess = columnWidth - containerWidth % columnWidth;
32046         
32047         
32048         // if overshoot is less than a pixel, round up, otherwise floor it
32049         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32050         cols = Math[ mathMethod ]( cols );
32051         this.cols = Math.max( cols, 1 );
32052         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32053         
32054          // padding positioning..
32055         var totalColWidth = this.cols * this.columnWidth;
32056         var padavail = this.containerWidth - totalColWidth;
32057         // so for 2 columns - we need 3 'pads'
32058         
32059         var padNeeded = (1+this.cols) * this.padWidth;
32060         
32061         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32062         
32063         this.columnWidth += padExtra
32064         //this.padWidth = Math.floor(padavail /  ( this.cols));
32065         
32066         // adjust colum width so that padding is fixed??
32067         
32068         // we have 3 columns ... total = width * 3
32069         // we have X left over... that should be used by 
32070         
32071         //if (this.expandC) {
32072             
32073         //}
32074         
32075         
32076         
32077     },
32078     
32079     getContainerWidth : function()
32080     {
32081        /* // container is parent if fit width
32082         var container = this.isFitWidth ? this.element.parentNode : this.element;
32083         // check that this.size and size are there
32084         // IE8 triggers resize on body size change, so they might not be
32085         
32086         var size = getSize( container );  //FIXME
32087         this.containerWidth = size && size.innerWidth; //FIXME
32088         */
32089          
32090         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32091         
32092     },
32093     
32094     _getItemLayoutPosition : function( item )  // what is item?
32095     {
32096         // we resize the item to our columnWidth..
32097       
32098         item.setWidth(this.columnWidth);
32099         item.autoBoxAdjust  = false;
32100         
32101         var sz = item.getSize();
32102  
32103         // how many columns does this brick span
32104         var remainder = this.containerWidth % this.columnWidth;
32105         
32106         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32107         // round if off by 1 pixel, otherwise use ceil
32108         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32109         colSpan = Math.min( colSpan, this.cols );
32110         
32111         // normally this should be '1' as we dont' currently allow multi width columns..
32112         
32113         var colGroup = this._getColGroup( colSpan );
32114         // get the minimum Y value from the columns
32115         var minimumY = Math.min.apply( Math, colGroup );
32116         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32117         
32118         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32119          
32120         // position the brick
32121         var position = {
32122             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32123             y: this.currentSize.y + minimumY + this.padHeight
32124         };
32125         
32126         Roo.log(position);
32127         // apply setHeight to necessary columns
32128         var setHeight = minimumY + sz.height + this.padHeight;
32129         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32130         
32131         var setSpan = this.cols + 1 - colGroup.length;
32132         for ( var i = 0; i < setSpan; i++ ) {
32133           this.colYs[ shortColIndex + i ] = setHeight ;
32134         }
32135       
32136         return position;
32137     },
32138     
32139     /**
32140      * @param {Number} colSpan - number of columns the element spans
32141      * @returns {Array} colGroup
32142      */
32143     _getColGroup : function( colSpan )
32144     {
32145         if ( colSpan < 2 ) {
32146           // if brick spans only one column, use all the column Ys
32147           return this.colYs;
32148         }
32149       
32150         var colGroup = [];
32151         // how many different places could this brick fit horizontally
32152         var groupCount = this.cols + 1 - colSpan;
32153         // for each group potential horizontal position
32154         for ( var i = 0; i < groupCount; i++ ) {
32155           // make an array of colY values for that one group
32156           var groupColYs = this.colYs.slice( i, i + colSpan );
32157           // and get the max value of the array
32158           colGroup[i] = Math.max.apply( Math, groupColYs );
32159         }
32160         return colGroup;
32161     },
32162     /*
32163     _manageStamp : function( stamp )
32164     {
32165         var stampSize =  stamp.getSize();
32166         var offset = stamp.getBox();
32167         // get the columns that this stamp affects
32168         var firstX = this.isOriginLeft ? offset.x : offset.right;
32169         var lastX = firstX + stampSize.width;
32170         var firstCol = Math.floor( firstX / this.columnWidth );
32171         firstCol = Math.max( 0, firstCol );
32172         
32173         var lastCol = Math.floor( lastX / this.columnWidth );
32174         // lastCol should not go over if multiple of columnWidth #425
32175         lastCol -= lastX % this.columnWidth ? 0 : 1;
32176         lastCol = Math.min( this.cols - 1, lastCol );
32177         
32178         // set colYs to bottom of the stamp
32179         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32180             stampSize.height;
32181             
32182         for ( var i = firstCol; i <= lastCol; i++ ) {
32183           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32184         }
32185     },
32186     */
32187     
32188     _getContainerSize : function()
32189     {
32190         this.maxY = Math.max.apply( Math, this.colYs );
32191         var size = {
32192             height: this.maxY
32193         };
32194       
32195         if ( this.isFitWidth ) {
32196             size.width = this._getContainerFitWidth();
32197         }
32198       
32199         return size;
32200     },
32201     
32202     _getContainerFitWidth : function()
32203     {
32204         var unusedCols = 0;
32205         // count unused columns
32206         var i = this.cols;
32207         while ( --i ) {
32208           if ( this.colYs[i] !== 0 ) {
32209             break;
32210           }
32211           unusedCols++;
32212         }
32213         // fit container to columns that have been used
32214         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32215     },
32216     
32217     needsResizeLayout : function()
32218     {
32219         var previousWidth = this.containerWidth;
32220         this.getContainerWidth();
32221         return previousWidth !== this.containerWidth;
32222     }
32223  
32224 });
32225
32226  
32227
32228  /*
32229  * - LGPL
32230  *
32231  * element
32232  * 
32233  */
32234
32235 /**
32236  * @class Roo.bootstrap.MasonryBrick
32237  * @extends Roo.bootstrap.Component
32238  * Bootstrap MasonryBrick class
32239  * 
32240  * @constructor
32241  * Create a new MasonryBrick
32242  * @param {Object} config The config object
32243  */
32244
32245 Roo.bootstrap.MasonryBrick = function(config){
32246     
32247     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32248     
32249     Roo.bootstrap.MasonryBrick.register(this);
32250     
32251     this.addEvents({
32252         // raw events
32253         /**
32254          * @event click
32255          * When a MasonryBrick is clcik
32256          * @param {Roo.bootstrap.MasonryBrick} this
32257          * @param {Roo.EventObject} e
32258          */
32259         "click" : true
32260     });
32261 };
32262
32263 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32264     
32265     /**
32266      * @cfg {String} title
32267      */   
32268     title : '',
32269     /**
32270      * @cfg {String} html
32271      */   
32272     html : '',
32273     /**
32274      * @cfg {String} bgimage
32275      */   
32276     bgimage : '',
32277     /**
32278      * @cfg {String} videourl
32279      */   
32280     videourl : '',
32281     /**
32282      * @cfg {String} cls
32283      */   
32284     cls : '',
32285     /**
32286      * @cfg {String} href
32287      */   
32288     href : '',
32289     /**
32290      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32291      */   
32292     size : 'xs',
32293     
32294     /**
32295      * @cfg {String} placetitle (center|bottom)
32296      */   
32297     placetitle : '',
32298     
32299     /**
32300      * @cfg {Boolean} isFitContainer defalut true
32301      */   
32302     isFitContainer : true, 
32303     
32304     /**
32305      * @cfg {Boolean} preventDefault defalut false
32306      */   
32307     preventDefault : false, 
32308     
32309     /**
32310      * @cfg {Boolean} inverse defalut false
32311      */   
32312     maskInverse : false, 
32313     
32314     getAutoCreate : function()
32315     {
32316         if(!this.isFitContainer){
32317             return this.getSplitAutoCreate();
32318         }
32319         
32320         var cls = 'masonry-brick masonry-brick-full';
32321         
32322         if(this.href.length){
32323             cls += ' masonry-brick-link';
32324         }
32325         
32326         if(this.bgimage.length){
32327             cls += ' masonry-brick-image';
32328         }
32329         
32330         if(this.maskInverse){
32331             cls += ' mask-inverse';
32332         }
32333         
32334         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32335             cls += ' enable-mask';
32336         }
32337         
32338         if(this.size){
32339             cls += ' masonry-' + this.size + '-brick';
32340         }
32341         
32342         if(this.placetitle.length){
32343             
32344             switch (this.placetitle) {
32345                 case 'center' :
32346                     cls += ' masonry-center-title';
32347                     break;
32348                 case 'bottom' :
32349                     cls += ' masonry-bottom-title';
32350                     break;
32351                 default:
32352                     break;
32353             }
32354             
32355         } else {
32356             if(!this.html.length && !this.bgimage.length){
32357                 cls += ' masonry-center-title';
32358             }
32359
32360             if(!this.html.length && this.bgimage.length){
32361                 cls += ' masonry-bottom-title';
32362             }
32363         }
32364         
32365         if(this.cls){
32366             cls += ' ' + this.cls;
32367         }
32368         
32369         var cfg = {
32370             tag: (this.href.length) ? 'a' : 'div',
32371             cls: cls,
32372             cn: [
32373                 {
32374                     tag: 'div',
32375                     cls: 'masonry-brick-mask'
32376                 },
32377                 {
32378                     tag: 'div',
32379                     cls: 'masonry-brick-paragraph',
32380                     cn: []
32381                 }
32382             ]
32383         };
32384         
32385         if(this.href.length){
32386             cfg.href = this.href;
32387         }
32388         
32389         var cn = cfg.cn[1].cn;
32390         
32391         if(this.title.length){
32392             cn.push({
32393                 tag: 'h4',
32394                 cls: 'masonry-brick-title',
32395                 html: this.title
32396             });
32397         }
32398         
32399         if(this.html.length){
32400             cn.push({
32401                 tag: 'p',
32402                 cls: 'masonry-brick-text',
32403                 html: this.html
32404             });
32405         }
32406         
32407         if (!this.title.length && !this.html.length) {
32408             cfg.cn[1].cls += ' hide';
32409         }
32410         
32411         if(this.bgimage.length){
32412             cfg.cn.push({
32413                 tag: 'img',
32414                 cls: 'masonry-brick-image-view',
32415                 src: this.bgimage
32416             });
32417         }
32418         
32419         if(this.videourl.length){
32420             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32421             // youtube support only?
32422             cfg.cn.push({
32423                 tag: 'iframe',
32424                 cls: 'masonry-brick-image-view',
32425                 src: vurl,
32426                 frameborder : 0,
32427                 allowfullscreen : true
32428             });
32429         }
32430         
32431         return cfg;
32432         
32433     },
32434     
32435     getSplitAutoCreate : function()
32436     {
32437         var cls = 'masonry-brick masonry-brick-split';
32438         
32439         if(this.href.length){
32440             cls += ' masonry-brick-link';
32441         }
32442         
32443         if(this.bgimage.length){
32444             cls += ' masonry-brick-image';
32445         }
32446         
32447         if(this.size){
32448             cls += ' masonry-' + this.size + '-brick';
32449         }
32450         
32451         switch (this.placetitle) {
32452             case 'center' :
32453                 cls += ' masonry-center-title';
32454                 break;
32455             case 'bottom' :
32456                 cls += ' masonry-bottom-title';
32457                 break;
32458             default:
32459                 if(!this.bgimage.length){
32460                     cls += ' masonry-center-title';
32461                 }
32462
32463                 if(this.bgimage.length){
32464                     cls += ' masonry-bottom-title';
32465                 }
32466                 break;
32467         }
32468         
32469         if(this.cls){
32470             cls += ' ' + this.cls;
32471         }
32472         
32473         var cfg = {
32474             tag: (this.href.length) ? 'a' : 'div',
32475             cls: cls,
32476             cn: [
32477                 {
32478                     tag: 'div',
32479                     cls: 'masonry-brick-split-head',
32480                     cn: [
32481                         {
32482                             tag: 'div',
32483                             cls: 'masonry-brick-paragraph',
32484                             cn: []
32485                         }
32486                     ]
32487                 },
32488                 {
32489                     tag: 'div',
32490                     cls: 'masonry-brick-split-body',
32491                     cn: []
32492                 }
32493             ]
32494         };
32495         
32496         if(this.href.length){
32497             cfg.href = this.href;
32498         }
32499         
32500         if(this.title.length){
32501             cfg.cn[0].cn[0].cn.push({
32502                 tag: 'h4',
32503                 cls: 'masonry-brick-title',
32504                 html: this.title
32505             });
32506         }
32507         
32508         if(this.html.length){
32509             cfg.cn[1].cn.push({
32510                 tag: 'p',
32511                 cls: 'masonry-brick-text',
32512                 html: this.html
32513             });
32514         }
32515
32516         if(this.bgimage.length){
32517             cfg.cn[0].cn.push({
32518                 tag: 'img',
32519                 cls: 'masonry-brick-image-view',
32520                 src: this.bgimage
32521             });
32522         }
32523         
32524         if(this.videourl.length){
32525             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32526             // youtube support only?
32527             cfg.cn[0].cn.cn.push({
32528                 tag: 'iframe',
32529                 cls: 'masonry-brick-image-view',
32530                 src: vurl,
32531                 frameborder : 0,
32532                 allowfullscreen : true
32533             });
32534         }
32535         
32536         return cfg;
32537     },
32538     
32539     initEvents: function() 
32540     {
32541         switch (this.size) {
32542             case 'xs' :
32543                 this.x = 1;
32544                 this.y = 1;
32545                 break;
32546             case 'sm' :
32547                 this.x = 2;
32548                 this.y = 2;
32549                 break;
32550             case 'md' :
32551             case 'md-left' :
32552             case 'md-right' :
32553                 this.x = 3;
32554                 this.y = 3;
32555                 break;
32556             case 'tall' :
32557                 this.x = 2;
32558                 this.y = 3;
32559                 break;
32560             case 'wide' :
32561                 this.x = 3;
32562                 this.y = 2;
32563                 break;
32564             case 'wide-thin' :
32565                 this.x = 3;
32566                 this.y = 1;
32567                 break;
32568                         
32569             default :
32570                 break;
32571         }
32572         
32573         if(Roo.isTouch){
32574             this.el.on('touchstart', this.onTouchStart, this);
32575             this.el.on('touchmove', this.onTouchMove, this);
32576             this.el.on('touchend', this.onTouchEnd, this);
32577             this.el.on('contextmenu', this.onContextMenu, this);
32578         } else {
32579             this.el.on('mouseenter'  ,this.enter, this);
32580             this.el.on('mouseleave', this.leave, this);
32581             this.el.on('click', this.onClick, this);
32582         }
32583         
32584         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32585             this.parent().bricks.push(this);   
32586         }
32587         
32588     },
32589     
32590     onClick: function(e, el)
32591     {
32592         var time = this.endTimer - this.startTimer;
32593         // Roo.log(e.preventDefault());
32594         if(Roo.isTouch){
32595             if(time > 1000){
32596                 e.preventDefault();
32597                 return;
32598             }
32599         }
32600         
32601         if(!this.preventDefault){
32602             return;
32603         }
32604         
32605         e.preventDefault();
32606         
32607         if (this.activcClass != '') {
32608             this.selectBrick();
32609         }
32610         
32611         this.fireEvent('click', this);
32612     },
32613     
32614     enter: function(e, el)
32615     {
32616         e.preventDefault();
32617         
32618         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32619             return;
32620         }
32621         
32622         if(this.bgimage.length && this.html.length){
32623             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32624         }
32625     },
32626     
32627     leave: function(e, el)
32628     {
32629         e.preventDefault();
32630         
32631         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32632             return;
32633         }
32634         
32635         if(this.bgimage.length && this.html.length){
32636             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32637         }
32638     },
32639     
32640     onTouchStart: function(e, el)
32641     {
32642 //        e.preventDefault();
32643         
32644         this.touchmoved = false;
32645         
32646         if(!this.isFitContainer){
32647             return;
32648         }
32649         
32650         if(!this.bgimage.length || !this.html.length){
32651             return;
32652         }
32653         
32654         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32655         
32656         this.timer = new Date().getTime();
32657         
32658     },
32659     
32660     onTouchMove: function(e, el)
32661     {
32662         this.touchmoved = true;
32663     },
32664     
32665     onContextMenu : function(e,el)
32666     {
32667         e.preventDefault();
32668         e.stopPropagation();
32669         return false;
32670     },
32671     
32672     onTouchEnd: function(e, el)
32673     {
32674 //        e.preventDefault();
32675         
32676         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32677         
32678             this.leave(e,el);
32679             
32680             return;
32681         }
32682         
32683         if(!this.bgimage.length || !this.html.length){
32684             
32685             if(this.href.length){
32686                 window.location.href = this.href;
32687             }
32688             
32689             return;
32690         }
32691         
32692         if(!this.isFitContainer){
32693             return;
32694         }
32695         
32696         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32697         
32698         window.location.href = this.href;
32699     },
32700     
32701     //selection on single brick only
32702     selectBrick : function() {
32703         
32704         if (!this.parentId) {
32705             return;
32706         }
32707         
32708         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32709         var index = m.selectedBrick.indexOf(this.id);
32710         
32711         if ( index > -1) {
32712             m.selectedBrick.splice(index,1);
32713             this.el.removeClass(this.activeClass);
32714             return;
32715         }
32716         
32717         for(var i = 0; i < m.selectedBrick.length; i++) {
32718             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32719             b.el.removeClass(b.activeClass);
32720         }
32721         
32722         m.selectedBrick = [];
32723         
32724         m.selectedBrick.push(this.id);
32725         this.el.addClass(this.activeClass);
32726         return;
32727     }
32728     
32729 });
32730
32731 Roo.apply(Roo.bootstrap.MasonryBrick, {
32732     
32733     //groups: {},
32734     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32735      /**
32736     * register a Masonry Brick
32737     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32738     */
32739     
32740     register : function(brick)
32741     {
32742         //this.groups[brick.id] = brick;
32743         this.groups.add(brick.id, brick);
32744     },
32745     /**
32746     * fetch a  masonry brick based on the masonry brick ID
32747     * @param {string} the masonry brick to add
32748     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32749     */
32750     
32751     get: function(brick_id) 
32752     {
32753         // if (typeof(this.groups[brick_id]) == 'undefined') {
32754         //     return false;
32755         // }
32756         // return this.groups[brick_id] ;
32757         
32758         if(this.groups.key(brick_id)) {
32759             return this.groups.key(brick_id);
32760         }
32761         
32762         return false;
32763     }
32764     
32765     
32766     
32767 });
32768
32769  /*
32770  * - LGPL
32771  *
32772  * element
32773  * 
32774  */
32775
32776 /**
32777  * @class Roo.bootstrap.Brick
32778  * @extends Roo.bootstrap.Component
32779  * Bootstrap Brick class
32780  * 
32781  * @constructor
32782  * Create a new Brick
32783  * @param {Object} config The config object
32784  */
32785
32786 Roo.bootstrap.Brick = function(config){
32787     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
32788     
32789     this.addEvents({
32790         // raw events
32791         /**
32792          * @event click
32793          * When a Brick is click
32794          * @param {Roo.bootstrap.Brick} this
32795          * @param {Roo.EventObject} e
32796          */
32797         "click" : true
32798     });
32799 };
32800
32801 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
32802     
32803     /**
32804      * @cfg {String} title
32805      */   
32806     title : '',
32807     /**
32808      * @cfg {String} html
32809      */   
32810     html : '',
32811     /**
32812      * @cfg {String} bgimage
32813      */   
32814     bgimage : '',
32815     /**
32816      * @cfg {String} cls
32817      */   
32818     cls : '',
32819     /**
32820      * @cfg {String} href
32821      */   
32822     href : '',
32823     /**
32824      * @cfg {String} video
32825      */   
32826     video : '',
32827     /**
32828      * @cfg {Boolean} square
32829      */   
32830     square : true,
32831     
32832     getAutoCreate : function()
32833     {
32834         var cls = 'roo-brick';
32835         
32836         if(this.href.length){
32837             cls += ' roo-brick-link';
32838         }
32839         
32840         if(this.bgimage.length){
32841             cls += ' roo-brick-image';
32842         }
32843         
32844         if(!this.html.length && !this.bgimage.length){
32845             cls += ' roo-brick-center-title';
32846         }
32847         
32848         if(!this.html.length && this.bgimage.length){
32849             cls += ' roo-brick-bottom-title';
32850         }
32851         
32852         if(this.cls){
32853             cls += ' ' + this.cls;
32854         }
32855         
32856         var cfg = {
32857             tag: (this.href.length) ? 'a' : 'div',
32858             cls: cls,
32859             cn: [
32860                 {
32861                     tag: 'div',
32862                     cls: 'roo-brick-paragraph',
32863                     cn: []
32864                 }
32865             ]
32866         };
32867         
32868         if(this.href.length){
32869             cfg.href = this.href;
32870         }
32871         
32872         var cn = cfg.cn[0].cn;
32873         
32874         if(this.title.length){
32875             cn.push({
32876                 tag: 'h4',
32877                 cls: 'roo-brick-title',
32878                 html: this.title
32879             });
32880         }
32881         
32882         if(this.html.length){
32883             cn.push({
32884                 tag: 'p',
32885                 cls: 'roo-brick-text',
32886                 html: this.html
32887             });
32888         } else {
32889             cn.cls += ' hide';
32890         }
32891         
32892         if(this.bgimage.length){
32893             cfg.cn.push({
32894                 tag: 'img',
32895                 cls: 'roo-brick-image-view',
32896                 src: this.bgimage
32897             });
32898         }
32899         
32900         return cfg;
32901     },
32902     
32903     initEvents: function() 
32904     {
32905         if(this.title.length || this.html.length){
32906             this.el.on('mouseenter'  ,this.enter, this);
32907             this.el.on('mouseleave', this.leave, this);
32908         }
32909         
32910         Roo.EventManager.onWindowResize(this.resize, this); 
32911         
32912         if(this.bgimage.length){
32913             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
32914             this.imageEl.on('load', this.onImageLoad, this);
32915             return;
32916         }
32917         
32918         this.resize();
32919     },
32920     
32921     onImageLoad : function()
32922     {
32923         this.resize();
32924     },
32925     
32926     resize : function()
32927     {
32928         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
32929         
32930         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
32931         
32932         if(this.bgimage.length){
32933             var image = this.el.select('.roo-brick-image-view', true).first();
32934             
32935             image.setWidth(paragraph.getWidth());
32936             
32937             if(this.square){
32938                 image.setHeight(paragraph.getWidth());
32939             }
32940             
32941             this.el.setHeight(image.getHeight());
32942             paragraph.setHeight(image.getHeight());
32943             
32944         }
32945         
32946     },
32947     
32948     enter: function(e, el)
32949     {
32950         e.preventDefault();
32951         
32952         if(this.bgimage.length){
32953             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
32954             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
32955         }
32956     },
32957     
32958     leave: function(e, el)
32959     {
32960         e.preventDefault();
32961         
32962         if(this.bgimage.length){
32963             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
32964             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
32965         }
32966     }
32967     
32968 });
32969
32970  
32971
32972  /*
32973  * - LGPL
32974  *
32975  * Input
32976  * 
32977  */
32978
32979 /**
32980  * @class Roo.bootstrap.NumberField
32981  * @extends Roo.bootstrap.Input
32982  * Bootstrap NumberField class
32983  * 
32984  * 
32985  * 
32986  * 
32987  * @constructor
32988  * Create a new NumberField
32989  * @param {Object} config The config object
32990  */
32991
32992 Roo.bootstrap.NumberField = function(config){
32993     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
32994 };
32995
32996 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
32997     
32998     /**
32999      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33000      */
33001     allowDecimals : true,
33002     /**
33003      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33004      */
33005     decimalSeparator : ".",
33006     /**
33007      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33008      */
33009     decimalPrecision : 2,
33010     /**
33011      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33012      */
33013     allowNegative : true,
33014     /**
33015      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33016      */
33017     minValue : Number.NEGATIVE_INFINITY,
33018     /**
33019      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33020      */
33021     maxValue : Number.MAX_VALUE,
33022     /**
33023      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33024      */
33025     minText : "The minimum value for this field is {0}",
33026     /**
33027      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33028      */
33029     maxText : "The maximum value for this field is {0}",
33030     /**
33031      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33032      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33033      */
33034     nanText : "{0} is not a valid number",
33035     /**
33036      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
33037      */
33038     castInt : true,
33039     /**
33040      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33041      */
33042     thousandsDelimiter : false,
33043     /**
33044      * @cfg {String} valueAlign alignment of value
33045      */
33046     valueAlign : "left",
33047
33048     getAutoCreate : function()
33049     {
33050         var hiddenInput = {
33051             tag: 'input',
33052             type: 'hidden',
33053             id: Roo.id(),
33054             cls: 'hidden-number-input'
33055         };
33056         
33057         if (this.name) {
33058             hiddenInput.name = this.name;
33059         }
33060         
33061         this.name = '';
33062         
33063         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33064         
33065         this.name = hiddenInput.name;
33066         
33067         if(cfg.cn.length > 0) {
33068             cfg.cn.push(hiddenInput);
33069         }
33070         
33071         return cfg;
33072     },
33073
33074     // private
33075     initEvents : function()
33076     {   
33077         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33078         
33079         var allowed = "0123456789";
33080         
33081         if(this.allowDecimals){
33082             allowed += this.decimalSeparator;
33083         }
33084         
33085         if(this.allowNegative){
33086             allowed += "-";
33087         }
33088         
33089         if(this.thousandsDelimiter) {
33090             allowed += ",";
33091         }
33092         
33093         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33094         
33095         var keyPress = function(e){
33096             
33097             var k = e.getKey();
33098             
33099             var c = e.getCharCode();
33100             
33101             if(
33102                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33103                     allowed.indexOf(String.fromCharCode(c)) === -1
33104             ){
33105                 e.stopEvent();
33106                 return;
33107             }
33108             
33109             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33110                 return;
33111             }
33112             
33113             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33114                 e.stopEvent();
33115             }
33116         };
33117         
33118         this.el.on("keypress", keyPress, this);
33119     },
33120     
33121     validateValue : function(value)
33122     {
33123         
33124         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33125             return false;
33126         }
33127         
33128         var num = this.parseValue(value);
33129         
33130         if(isNaN(num)){
33131             this.markInvalid(String.format(this.nanText, value));
33132             return false;
33133         }
33134         
33135         if(num < this.minValue){
33136             this.markInvalid(String.format(this.minText, this.minValue));
33137             return false;
33138         }
33139         
33140         if(num > this.maxValue){
33141             this.markInvalid(String.format(this.maxText, this.maxValue));
33142             return false;
33143         }
33144         
33145         return true;
33146     },
33147
33148     getValue : function()
33149     {
33150         var v = this.hiddenEl().getValue();
33151         
33152         return this.fixPrecision(this.parseValue(v));
33153     },
33154
33155     parseValue : function(value)
33156     {
33157         if(this.thousandsDelimiter) {
33158             value += "";
33159             r = new RegExp(",", "g");
33160             value = value.replace(r, "");
33161         }
33162         
33163         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33164         return isNaN(value) ? '' : value;
33165     },
33166
33167     fixPrecision : function(value)
33168     {
33169         if(this.thousandsDelimiter) {
33170             value += "";
33171             r = new RegExp(",", "g");
33172             value = value.replace(r, "");
33173         }
33174         
33175         var nan = isNaN(value);
33176         
33177         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33178             return nan ? '' : value;
33179         }
33180         return parseFloat(value).toFixed(this.decimalPrecision);
33181     },
33182
33183     setValue : function(v)
33184     {
33185         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33186         
33187         this.value = v;
33188         
33189         if(this.rendered){
33190             
33191             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33192             
33193             this.inputEl().dom.value = Roo.util.Format.number(v, this.decimalPrecision, 
33194                 this.thousandsDelimiter || ''
33195             );
33196             
33197             this.validate();
33198         }
33199     },
33200
33201     decimalPrecisionFcn : function(v)
33202     {
33203         return Math.floor(v);
33204     },
33205
33206     beforeBlur : function()
33207     {
33208         if(!this.castInt){
33209             return;
33210         }
33211         
33212         var v = this.parseValue(this.getRawValue());
33213         if(v){
33214             this.setValue(v);
33215         }
33216     },
33217     
33218     hiddenEl : function()
33219     {
33220         return this.el.select('input.hidden-number-input',true).first();
33221     }
33222     
33223 });
33224
33225  
33226
33227 /*
33228 * Licence: LGPL
33229 */
33230
33231 /**
33232  * @class Roo.bootstrap.DocumentSlider
33233  * @extends Roo.bootstrap.Component
33234  * Bootstrap DocumentSlider class
33235  * 
33236  * @constructor
33237  * Create a new DocumentViewer
33238  * @param {Object} config The config object
33239  */
33240
33241 Roo.bootstrap.DocumentSlider = function(config){
33242     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33243     
33244     this.files = [];
33245     
33246     this.addEvents({
33247         /**
33248          * @event initial
33249          * Fire after initEvent
33250          * @param {Roo.bootstrap.DocumentSlider} this
33251          */
33252         "initial" : true,
33253         /**
33254          * @event update
33255          * Fire after update
33256          * @param {Roo.bootstrap.DocumentSlider} this
33257          */
33258         "update" : true,
33259         /**
33260          * @event click
33261          * Fire after click
33262          * @param {Roo.bootstrap.DocumentSlider} this
33263          */
33264         "click" : true
33265     });
33266 };
33267
33268 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33269     
33270     files : false,
33271     
33272     indicator : 0,
33273     
33274     getAutoCreate : function()
33275     {
33276         var cfg = {
33277             tag : 'div',
33278             cls : 'roo-document-slider',
33279             cn : [
33280                 {
33281                     tag : 'div',
33282                     cls : 'roo-document-slider-header',
33283                     cn : [
33284                         {
33285                             tag : 'div',
33286                             cls : 'roo-document-slider-header-title'
33287                         }
33288                     ]
33289                 },
33290                 {
33291                     tag : 'div',
33292                     cls : 'roo-document-slider-body',
33293                     cn : [
33294                         {
33295                             tag : 'div',
33296                             cls : 'roo-document-slider-prev',
33297                             cn : [
33298                                 {
33299                                     tag : 'i',
33300                                     cls : 'fa fa-chevron-left'
33301                                 }
33302                             ]
33303                         },
33304                         {
33305                             tag : 'div',
33306                             cls : 'roo-document-slider-thumb',
33307                             cn : [
33308                                 {
33309                                     tag : 'img',
33310                                     cls : 'roo-document-slider-image'
33311                                 }
33312                             ]
33313                         },
33314                         {
33315                             tag : 'div',
33316                             cls : 'roo-document-slider-next',
33317                             cn : [
33318                                 {
33319                                     tag : 'i',
33320                                     cls : 'fa fa-chevron-right'
33321                                 }
33322                             ]
33323                         }
33324                     ]
33325                 }
33326             ]
33327         };
33328         
33329         return cfg;
33330     },
33331     
33332     initEvents : function()
33333     {
33334         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33335         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33336         
33337         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33338         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33339         
33340         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33341         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33342         
33343         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33344         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33345         
33346         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33347         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33348         
33349         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33350         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33351         
33352         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33353         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33354         
33355         this.thumbEl.on('click', this.onClick, this);
33356         
33357         this.prevIndicator.on('click', this.prev, this);
33358         
33359         this.nextIndicator.on('click', this.next, this);
33360         
33361     },
33362     
33363     initial : function()
33364     {
33365         if(this.files.length){
33366             this.indicator = 1;
33367             this.update()
33368         }
33369         
33370         this.fireEvent('initial', this);
33371     },
33372     
33373     update : function()
33374     {
33375         this.imageEl.attr('src', this.files[this.indicator - 1]);
33376         
33377         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33378         
33379         this.prevIndicator.show();
33380         
33381         if(this.indicator == 1){
33382             this.prevIndicator.hide();
33383         }
33384         
33385         this.nextIndicator.show();
33386         
33387         if(this.indicator == this.files.length){
33388             this.nextIndicator.hide();
33389         }
33390         
33391         this.thumbEl.scrollTo('top');
33392         
33393         this.fireEvent('update', this);
33394     },
33395     
33396     onClick : function(e)
33397     {
33398         e.preventDefault();
33399         
33400         this.fireEvent('click', this);
33401     },
33402     
33403     prev : function(e)
33404     {
33405         e.preventDefault();
33406         
33407         this.indicator = Math.max(1, this.indicator - 1);
33408         
33409         this.update();
33410     },
33411     
33412     next : function(e)
33413     {
33414         e.preventDefault();
33415         
33416         this.indicator = Math.min(this.files.length, this.indicator + 1);
33417         
33418         this.update();
33419     }
33420 });
33421 /*
33422  * - LGPL
33423  *
33424  * RadioSet
33425  *
33426  *
33427  */
33428
33429 /**
33430  * @class Roo.bootstrap.RadioSet
33431  * @extends Roo.bootstrap.Input
33432  * Bootstrap RadioSet class
33433  * @cfg {String} indicatorpos (left|right) default left
33434  * @cfg {Boolean} inline (true|false) inline the element (default true)
33435  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33436  * @constructor
33437  * Create a new RadioSet
33438  * @param {Object} config The config object
33439  */
33440
33441 Roo.bootstrap.RadioSet = function(config){
33442     
33443     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33444     
33445     this.radioes = [];
33446     
33447     Roo.bootstrap.RadioSet.register(this);
33448     
33449     this.addEvents({
33450         /**
33451         * @event check
33452         * Fires when the element is checked or unchecked.
33453         * @param {Roo.bootstrap.RadioSet} this This radio
33454         * @param {Roo.bootstrap.Radio} item The checked item
33455         */
33456        check : true
33457     });
33458     
33459 };
33460
33461 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33462
33463     radioes : false,
33464     
33465     inline : true,
33466     
33467     weight : '',
33468     
33469     indicatorpos : 'left',
33470     
33471     getAutoCreate : function()
33472     {
33473         var label = {
33474             tag : 'label',
33475             cls : 'roo-radio-set-label',
33476             cn : [
33477                 {
33478                     tag : 'span',
33479                     html : this.fieldLabel
33480                 }
33481             ]
33482         };
33483         
33484         if(this.indicatorpos == 'left'){
33485             label.cn.unshift({
33486                 tag : 'i',
33487                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33488                 tooltip : 'This field is required'
33489             });
33490         } else {
33491             label.cn.push({
33492                 tag : 'i',
33493                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33494                 tooltip : 'This field is required'
33495             });
33496         }
33497         
33498         var items = {
33499             tag : 'div',
33500             cls : 'roo-radio-set-items'
33501         };
33502         
33503         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33504         
33505         if (align === 'left' && this.fieldLabel.length) {
33506             
33507             items = {
33508                 cls : "roo-radio-set-right", 
33509                 cn: [
33510                     items
33511                 ]
33512             };
33513             
33514             if(this.labelWidth > 12){
33515                 label.style = "width: " + this.labelWidth + 'px';
33516             }
33517             
33518             if(this.labelWidth < 13 && this.labelmd == 0){
33519                 this.labelmd = this.labelWidth;
33520             }
33521             
33522             if(this.labellg > 0){
33523                 label.cls += ' col-lg-' + this.labellg;
33524                 items.cls += ' col-lg-' + (12 - this.labellg);
33525             }
33526             
33527             if(this.labelmd > 0){
33528                 label.cls += ' col-md-' + this.labelmd;
33529                 items.cls += ' col-md-' + (12 - this.labelmd);
33530             }
33531             
33532             if(this.labelsm > 0){
33533                 label.cls += ' col-sm-' + this.labelsm;
33534                 items.cls += ' col-sm-' + (12 - this.labelsm);
33535             }
33536             
33537             if(this.labelxs > 0){
33538                 label.cls += ' col-xs-' + this.labelxs;
33539                 items.cls += ' col-xs-' + (12 - this.labelxs);
33540             }
33541         }
33542         
33543         var cfg = {
33544             tag : 'div',
33545             cls : 'roo-radio-set',
33546             cn : [
33547                 {
33548                     tag : 'input',
33549                     cls : 'roo-radio-set-input',
33550                     type : 'hidden',
33551                     name : this.name,
33552                     value : this.value ? this.value :  ''
33553                 },
33554                 label,
33555                 items
33556             ]
33557         };
33558         
33559         if(this.weight.length){
33560             cfg.cls += ' roo-radio-' + this.weight;
33561         }
33562         
33563         if(this.inline) {
33564             cfg.cls += ' roo-radio-set-inline';
33565         }
33566         
33567         var settings=this;
33568         ['xs','sm','md','lg'].map(function(size){
33569             if (settings[size]) {
33570                 cfg.cls += ' col-' + size + '-' + settings[size];
33571             }
33572         });
33573         
33574         return cfg;
33575         
33576     },
33577
33578     initEvents : function()
33579     {
33580         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33581         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33582         
33583         if(!this.fieldLabel.length){
33584             this.labelEl.hide();
33585         }
33586         
33587         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33588         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33589         
33590         this.indicatorEl().addClass('invisible');
33591         
33592         this.originalValue = this.getValue();
33593         
33594     },
33595     
33596     inputEl: function ()
33597     {
33598         return this.el.select('.roo-radio-set-input', true).first();
33599     },
33600     
33601     getChildContainer : function()
33602     {
33603         return this.itemsEl;
33604     },
33605     
33606     register : function(item)
33607     {
33608         this.radioes.push(item);
33609         
33610     },
33611     
33612     validate : function()
33613     {   
33614         var valid = false;
33615         
33616         Roo.each(this.radioes, function(i){
33617             if(!i.checked){
33618                 return;
33619             }
33620             
33621             valid = true;
33622             return false;
33623         });
33624         
33625         if(this.allowBlank) {
33626             return true;
33627         }
33628         
33629         if(this.disabled || valid){
33630             this.markValid();
33631             return true;
33632         }
33633         
33634         this.markInvalid();
33635         return false;
33636         
33637     },
33638     
33639     markValid : function()
33640     {
33641         if(this.labelEl.isVisible(true)){
33642             this.indicatorEl().removeClass('visible');
33643             this.indicatorEl().addClass('invisible');
33644         }
33645         
33646         this.el.removeClass([this.invalidClass, this.validClass]);
33647         this.el.addClass(this.validClass);
33648         
33649         this.fireEvent('valid', this);
33650     },
33651     
33652     markInvalid : function(msg)
33653     {
33654         if(this.allowBlank || this.disabled){
33655             return;
33656         }
33657         
33658         if(this.labelEl.isVisible(true)){
33659             this.indicatorEl().removeClass('invisible');
33660             this.indicatorEl().addClass('visible');
33661         }
33662         
33663         this.el.removeClass([this.invalidClass, this.validClass]);
33664         this.el.addClass(this.invalidClass);
33665         
33666         this.fireEvent('invalid', this, msg);
33667         
33668     },
33669     
33670     setValue : function(v, suppressEvent)
33671     {   
33672         if(this.value === v){
33673             return;
33674         }
33675         
33676         this.value = v;
33677         
33678         if(this.rendered){
33679             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33680         }
33681         
33682         Roo.each(this.radioes, function(i){
33683             i.checked = false;
33684             i.el.removeClass('checked');
33685         });
33686         
33687         Roo.each(this.radioes, function(i){
33688             
33689             if(i.value === v || i.value.toString() === v.toString()){
33690                 i.checked = true;
33691                 i.el.addClass('checked');
33692                 
33693                 if(suppressEvent !== true){
33694                     this.fireEvent('check', this, i);
33695                 }
33696                 
33697                 return false;
33698             }
33699             
33700         }, this);
33701         
33702         this.validate();
33703     },
33704     
33705     clearInvalid : function(){
33706         
33707         if(!this.el || this.preventMark){
33708             return;
33709         }
33710         
33711         this.el.removeClass([this.invalidClass]);
33712         
33713         this.fireEvent('valid', this);
33714     }
33715     
33716 });
33717
33718 Roo.apply(Roo.bootstrap.RadioSet, {
33719     
33720     groups: {},
33721     
33722     register : function(set)
33723     {
33724         this.groups[set.name] = set;
33725     },
33726     
33727     get: function(name) 
33728     {
33729         if (typeof(this.groups[name]) == 'undefined') {
33730             return false;
33731         }
33732         
33733         return this.groups[name] ;
33734     }
33735     
33736 });
33737 /*
33738  * Based on:
33739  * Ext JS Library 1.1.1
33740  * Copyright(c) 2006-2007, Ext JS, LLC.
33741  *
33742  * Originally Released Under LGPL - original licence link has changed is not relivant.
33743  *
33744  * Fork - LGPL
33745  * <script type="text/javascript">
33746  */
33747
33748
33749 /**
33750  * @class Roo.bootstrap.SplitBar
33751  * @extends Roo.util.Observable
33752  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33753  * <br><br>
33754  * Usage:
33755  * <pre><code>
33756 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33757                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33758 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
33759 split.minSize = 100;
33760 split.maxSize = 600;
33761 split.animate = true;
33762 split.on('moved', splitterMoved);
33763 </code></pre>
33764  * @constructor
33765  * Create a new SplitBar
33766  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
33767  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
33768  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33769  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
33770                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
33771                         position of the SplitBar).
33772  */
33773 Roo.bootstrap.SplitBar = function(cfg){
33774     
33775     /** @private */
33776     
33777     //{
33778     //  dragElement : elm
33779     //  resizingElement: el,
33780         // optional..
33781     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
33782     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
33783         // existingProxy ???
33784     //}
33785     
33786     this.el = Roo.get(cfg.dragElement, true);
33787     this.el.dom.unselectable = "on";
33788     /** @private */
33789     this.resizingEl = Roo.get(cfg.resizingElement, true);
33790
33791     /**
33792      * @private
33793      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33794      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
33795      * @type Number
33796      */
33797     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
33798     
33799     /**
33800      * The minimum size of the resizing element. (Defaults to 0)
33801      * @type Number
33802      */
33803     this.minSize = 0;
33804     
33805     /**
33806      * The maximum size of the resizing element. (Defaults to 2000)
33807      * @type Number
33808      */
33809     this.maxSize = 2000;
33810     
33811     /**
33812      * Whether to animate the transition to the new size
33813      * @type Boolean
33814      */
33815     this.animate = false;
33816     
33817     /**
33818      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
33819      * @type Boolean
33820      */
33821     this.useShim = false;
33822     
33823     /** @private */
33824     this.shim = null;
33825     
33826     if(!cfg.existingProxy){
33827         /** @private */
33828         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
33829     }else{
33830         this.proxy = Roo.get(cfg.existingProxy).dom;
33831     }
33832     /** @private */
33833     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
33834     
33835     /** @private */
33836     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
33837     
33838     /** @private */
33839     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
33840     
33841     /** @private */
33842     this.dragSpecs = {};
33843     
33844     /**
33845      * @private The adapter to use to positon and resize elements
33846      */
33847     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33848     this.adapter.init(this);
33849     
33850     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33851         /** @private */
33852         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
33853         this.el.addClass("roo-splitbar-h");
33854     }else{
33855         /** @private */
33856         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
33857         this.el.addClass("roo-splitbar-v");
33858     }
33859     
33860     this.addEvents({
33861         /**
33862          * @event resize
33863          * Fires when the splitter is moved (alias for {@link #event-moved})
33864          * @param {Roo.bootstrap.SplitBar} this
33865          * @param {Number} newSize the new width or height
33866          */
33867         "resize" : true,
33868         /**
33869          * @event moved
33870          * Fires when the splitter is moved
33871          * @param {Roo.bootstrap.SplitBar} this
33872          * @param {Number} newSize the new width or height
33873          */
33874         "moved" : true,
33875         /**
33876          * @event beforeresize
33877          * Fires before the splitter is dragged
33878          * @param {Roo.bootstrap.SplitBar} this
33879          */
33880         "beforeresize" : true,
33881
33882         "beforeapply" : true
33883     });
33884
33885     Roo.util.Observable.call(this);
33886 };
33887
33888 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
33889     onStartProxyDrag : function(x, y){
33890         this.fireEvent("beforeresize", this);
33891         if(!this.overlay){
33892             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
33893             o.unselectable();
33894             o.enableDisplayMode("block");
33895             // all splitbars share the same overlay
33896             Roo.bootstrap.SplitBar.prototype.overlay = o;
33897         }
33898         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33899         this.overlay.show();
33900         Roo.get(this.proxy).setDisplayed("block");
33901         var size = this.adapter.getElementSize(this);
33902         this.activeMinSize = this.getMinimumSize();;
33903         this.activeMaxSize = this.getMaximumSize();;
33904         var c1 = size - this.activeMinSize;
33905         var c2 = Math.max(this.activeMaxSize - size, 0);
33906         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33907             this.dd.resetConstraints();
33908             this.dd.setXConstraint(
33909                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
33910                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
33911             );
33912             this.dd.setYConstraint(0, 0);
33913         }else{
33914             this.dd.resetConstraints();
33915             this.dd.setXConstraint(0, 0);
33916             this.dd.setYConstraint(
33917                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
33918                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
33919             );
33920          }
33921         this.dragSpecs.startSize = size;
33922         this.dragSpecs.startPoint = [x, y];
33923         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
33924     },
33925     
33926     /** 
33927      * @private Called after the drag operation by the DDProxy
33928      */
33929     onEndProxyDrag : function(e){
33930         Roo.get(this.proxy).setDisplayed(false);
33931         var endPoint = Roo.lib.Event.getXY(e);
33932         if(this.overlay){
33933             this.overlay.hide();
33934         }
33935         var newSize;
33936         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33937             newSize = this.dragSpecs.startSize + 
33938                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
33939                     endPoint[0] - this.dragSpecs.startPoint[0] :
33940                     this.dragSpecs.startPoint[0] - endPoint[0]
33941                 );
33942         }else{
33943             newSize = this.dragSpecs.startSize + 
33944                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
33945                     endPoint[1] - this.dragSpecs.startPoint[1] :
33946                     this.dragSpecs.startPoint[1] - endPoint[1]
33947                 );
33948         }
33949         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
33950         if(newSize != this.dragSpecs.startSize){
33951             if(this.fireEvent('beforeapply', this, newSize) !== false){
33952                 this.adapter.setElementSize(this, newSize);
33953                 this.fireEvent("moved", this, newSize);
33954                 this.fireEvent("resize", this, newSize);
33955             }
33956         }
33957     },
33958     
33959     /**
33960      * Get the adapter this SplitBar uses
33961      * @return The adapter object
33962      */
33963     getAdapter : function(){
33964         return this.adapter;
33965     },
33966     
33967     /**
33968      * Set the adapter this SplitBar uses
33969      * @param {Object} adapter A SplitBar adapter object
33970      */
33971     setAdapter : function(adapter){
33972         this.adapter = adapter;
33973         this.adapter.init(this);
33974     },
33975     
33976     /**
33977      * Gets the minimum size for the resizing element
33978      * @return {Number} The minimum size
33979      */
33980     getMinimumSize : function(){
33981         return this.minSize;
33982     },
33983     
33984     /**
33985      * Sets the minimum size for the resizing element
33986      * @param {Number} minSize The minimum size
33987      */
33988     setMinimumSize : function(minSize){
33989         this.minSize = minSize;
33990     },
33991     
33992     /**
33993      * Gets the maximum size for the resizing element
33994      * @return {Number} The maximum size
33995      */
33996     getMaximumSize : function(){
33997         return this.maxSize;
33998     },
33999     
34000     /**
34001      * Sets the maximum size for the resizing element
34002      * @param {Number} maxSize The maximum size
34003      */
34004     setMaximumSize : function(maxSize){
34005         this.maxSize = maxSize;
34006     },
34007     
34008     /**
34009      * Sets the initialize size for the resizing element
34010      * @param {Number} size The initial size
34011      */
34012     setCurrentSize : function(size){
34013         var oldAnimate = this.animate;
34014         this.animate = false;
34015         this.adapter.setElementSize(this, size);
34016         this.animate = oldAnimate;
34017     },
34018     
34019     /**
34020      * Destroy this splitbar. 
34021      * @param {Boolean} removeEl True to remove the element
34022      */
34023     destroy : function(removeEl){
34024         if(this.shim){
34025             this.shim.remove();
34026         }
34027         this.dd.unreg();
34028         this.proxy.parentNode.removeChild(this.proxy);
34029         if(removeEl){
34030             this.el.remove();
34031         }
34032     }
34033 });
34034
34035 /**
34036  * @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.
34037  */
34038 Roo.bootstrap.SplitBar.createProxy = function(dir){
34039     var proxy = new Roo.Element(document.createElement("div"));
34040     proxy.unselectable();
34041     var cls = 'roo-splitbar-proxy';
34042     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34043     document.body.appendChild(proxy.dom);
34044     return proxy.dom;
34045 };
34046
34047 /** 
34048  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34049  * Default Adapter. It assumes the splitter and resizing element are not positioned
34050  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34051  */
34052 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34053 };
34054
34055 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34056     // do nothing for now
34057     init : function(s){
34058     
34059     },
34060     /**
34061      * Called before drag operations to get the current size of the resizing element. 
34062      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34063      */
34064      getElementSize : function(s){
34065         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34066             return s.resizingEl.getWidth();
34067         }else{
34068             return s.resizingEl.getHeight();
34069         }
34070     },
34071     
34072     /**
34073      * Called after drag operations to set the size of the resizing element.
34074      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34075      * @param {Number} newSize The new size to set
34076      * @param {Function} onComplete A function to be invoked when resizing is complete
34077      */
34078     setElementSize : function(s, newSize, onComplete){
34079         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34080             if(!s.animate){
34081                 s.resizingEl.setWidth(newSize);
34082                 if(onComplete){
34083                     onComplete(s, newSize);
34084                 }
34085             }else{
34086                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34087             }
34088         }else{
34089             
34090             if(!s.animate){
34091                 s.resizingEl.setHeight(newSize);
34092                 if(onComplete){
34093                     onComplete(s, newSize);
34094                 }
34095             }else{
34096                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34097             }
34098         }
34099     }
34100 };
34101
34102 /** 
34103  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34104  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34105  * Adapter that  moves the splitter element to align with the resized sizing element. 
34106  * Used with an absolute positioned SplitBar.
34107  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34108  * document.body, make sure you assign an id to the body element.
34109  */
34110 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34111     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34112     this.container = Roo.get(container);
34113 };
34114
34115 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34116     init : function(s){
34117         this.basic.init(s);
34118     },
34119     
34120     getElementSize : function(s){
34121         return this.basic.getElementSize(s);
34122     },
34123     
34124     setElementSize : function(s, newSize, onComplete){
34125         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34126     },
34127     
34128     moveSplitter : function(s){
34129         var yes = Roo.bootstrap.SplitBar;
34130         switch(s.placement){
34131             case yes.LEFT:
34132                 s.el.setX(s.resizingEl.getRight());
34133                 break;
34134             case yes.RIGHT:
34135                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34136                 break;
34137             case yes.TOP:
34138                 s.el.setY(s.resizingEl.getBottom());
34139                 break;
34140             case yes.BOTTOM:
34141                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34142                 break;
34143         }
34144     }
34145 };
34146
34147 /**
34148  * Orientation constant - Create a vertical SplitBar
34149  * @static
34150  * @type Number
34151  */
34152 Roo.bootstrap.SplitBar.VERTICAL = 1;
34153
34154 /**
34155  * Orientation constant - Create a horizontal SplitBar
34156  * @static
34157  * @type Number
34158  */
34159 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34160
34161 /**
34162  * Placement constant - The resizing element is to the left of the splitter element
34163  * @static
34164  * @type Number
34165  */
34166 Roo.bootstrap.SplitBar.LEFT = 1;
34167
34168 /**
34169  * Placement constant - The resizing element is to the right of the splitter element
34170  * @static
34171  * @type Number
34172  */
34173 Roo.bootstrap.SplitBar.RIGHT = 2;
34174
34175 /**
34176  * Placement constant - The resizing element is positioned above the splitter element
34177  * @static
34178  * @type Number
34179  */
34180 Roo.bootstrap.SplitBar.TOP = 3;
34181
34182 /**
34183  * Placement constant - The resizing element is positioned under splitter element
34184  * @static
34185  * @type Number
34186  */
34187 Roo.bootstrap.SplitBar.BOTTOM = 4;
34188 Roo.namespace("Roo.bootstrap.layout");/*
34189  * Based on:
34190  * Ext JS Library 1.1.1
34191  * Copyright(c) 2006-2007, Ext JS, LLC.
34192  *
34193  * Originally Released Under LGPL - original licence link has changed is not relivant.
34194  *
34195  * Fork - LGPL
34196  * <script type="text/javascript">
34197  */
34198
34199 /**
34200  * @class Roo.bootstrap.layout.Manager
34201  * @extends Roo.bootstrap.Component
34202  * Base class for layout managers.
34203  */
34204 Roo.bootstrap.layout.Manager = function(config)
34205 {
34206     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34207
34208
34209
34210
34211
34212     /** false to disable window resize monitoring @type Boolean */
34213     this.monitorWindowResize = true;
34214     this.regions = {};
34215     this.addEvents({
34216         /**
34217          * @event layout
34218          * Fires when a layout is performed.
34219          * @param {Roo.LayoutManager} this
34220          */
34221         "layout" : true,
34222         /**
34223          * @event regionresized
34224          * Fires when the user resizes a region.
34225          * @param {Roo.LayoutRegion} region The resized region
34226          * @param {Number} newSize The new size (width for east/west, height for north/south)
34227          */
34228         "regionresized" : true,
34229         /**
34230          * @event regioncollapsed
34231          * Fires when a region is collapsed.
34232          * @param {Roo.LayoutRegion} region The collapsed region
34233          */
34234         "regioncollapsed" : true,
34235         /**
34236          * @event regionexpanded
34237          * Fires when a region is expanded.
34238          * @param {Roo.LayoutRegion} region The expanded region
34239          */
34240         "regionexpanded" : true
34241     });
34242     this.updating = false;
34243
34244     if (config.el) {
34245         this.el = Roo.get(config.el);
34246         this.initEvents();
34247     }
34248
34249 };
34250
34251 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34252
34253
34254     regions : null,
34255
34256     monitorWindowResize : true,
34257
34258
34259     updating : false,
34260
34261
34262     onRender : function(ct, position)
34263     {
34264         if(!this.el){
34265             this.el = Roo.get(ct);
34266             this.initEvents();
34267         }
34268         //this.fireEvent('render',this);
34269     },
34270
34271
34272     initEvents: function()
34273     {
34274
34275
34276         // ie scrollbar fix
34277         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34278             document.body.scroll = "no";
34279         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34280             this.el.position('relative');
34281         }
34282         this.id = this.el.id;
34283         this.el.addClass("roo-layout-container");
34284         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34285         if(this.el.dom != document.body ) {
34286             this.el.on('resize', this.layout,this);
34287             this.el.on('show', this.layout,this);
34288         }
34289
34290     },
34291
34292     /**
34293      * Returns true if this layout is currently being updated
34294      * @return {Boolean}
34295      */
34296     isUpdating : function(){
34297         return this.updating;
34298     },
34299
34300     /**
34301      * Suspend the LayoutManager from doing auto-layouts while
34302      * making multiple add or remove calls
34303      */
34304     beginUpdate : function(){
34305         this.updating = true;
34306     },
34307
34308     /**
34309      * Restore auto-layouts and optionally disable the manager from performing a layout
34310      * @param {Boolean} noLayout true to disable a layout update
34311      */
34312     endUpdate : function(noLayout){
34313         this.updating = false;
34314         if(!noLayout){
34315             this.layout();
34316         }
34317     },
34318
34319     layout: function(){
34320         // abstract...
34321     },
34322
34323     onRegionResized : function(region, newSize){
34324         this.fireEvent("regionresized", region, newSize);
34325         this.layout();
34326     },
34327
34328     onRegionCollapsed : function(region){
34329         this.fireEvent("regioncollapsed", region);
34330     },
34331
34332     onRegionExpanded : function(region){
34333         this.fireEvent("regionexpanded", region);
34334     },
34335
34336     /**
34337      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34338      * performs box-model adjustments.
34339      * @return {Object} The size as an object {width: (the width), height: (the height)}
34340      */
34341     getViewSize : function()
34342     {
34343         var size;
34344         if(this.el.dom != document.body){
34345             size = this.el.getSize();
34346         }else{
34347             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34348         }
34349         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34350         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34351         return size;
34352     },
34353
34354     /**
34355      * Returns the Element this layout is bound to.
34356      * @return {Roo.Element}
34357      */
34358     getEl : function(){
34359         return this.el;
34360     },
34361
34362     /**
34363      * Returns the specified region.
34364      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34365      * @return {Roo.LayoutRegion}
34366      */
34367     getRegion : function(target){
34368         return this.regions[target.toLowerCase()];
34369     },
34370
34371     onWindowResize : function(){
34372         if(this.monitorWindowResize){
34373             this.layout();
34374         }
34375     }
34376 });
34377 /*
34378  * Based on:
34379  * Ext JS Library 1.1.1
34380  * Copyright(c) 2006-2007, Ext JS, LLC.
34381  *
34382  * Originally Released Under LGPL - original licence link has changed is not relivant.
34383  *
34384  * Fork - LGPL
34385  * <script type="text/javascript">
34386  */
34387 /**
34388  * @class Roo.bootstrap.layout.Border
34389  * @extends Roo.bootstrap.layout.Manager
34390  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34391  * please see: examples/bootstrap/nested.html<br><br>
34392  
34393 <b>The container the layout is rendered into can be either the body element or any other element.
34394 If it is not the body element, the container needs to either be an absolute positioned element,
34395 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34396 the container size if it is not the body element.</b>
34397
34398 * @constructor
34399 * Create a new Border
34400 * @param {Object} config Configuration options
34401  */
34402 Roo.bootstrap.layout.Border = function(config){
34403     config = config || {};
34404     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34405     
34406     
34407     
34408     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34409         if(config[region]){
34410             config[region].region = region;
34411             this.addRegion(config[region]);
34412         }
34413     },this);
34414     
34415 };
34416
34417 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34418
34419 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34420     /**
34421      * Creates and adds a new region if it doesn't already exist.
34422      * @param {String} target The target region key (north, south, east, west or center).
34423      * @param {Object} config The regions config object
34424      * @return {BorderLayoutRegion} The new region
34425      */
34426     addRegion : function(config)
34427     {
34428         if(!this.regions[config.region]){
34429             var r = this.factory(config);
34430             this.bindRegion(r);
34431         }
34432         return this.regions[config.region];
34433     },
34434
34435     // private (kinda)
34436     bindRegion : function(r){
34437         this.regions[r.config.region] = r;
34438         
34439         r.on("visibilitychange",    this.layout, this);
34440         r.on("paneladded",          this.layout, this);
34441         r.on("panelremoved",        this.layout, this);
34442         r.on("invalidated",         this.layout, this);
34443         r.on("resized",             this.onRegionResized, this);
34444         r.on("collapsed",           this.onRegionCollapsed, this);
34445         r.on("expanded",            this.onRegionExpanded, this);
34446     },
34447
34448     /**
34449      * Performs a layout update.
34450      */
34451     layout : function()
34452     {
34453         if(this.updating) {
34454             return;
34455         }
34456         
34457         // render all the rebions if they have not been done alreayd?
34458         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34459             if(this.regions[region] && !this.regions[region].bodyEl){
34460                 this.regions[region].onRender(this.el)
34461             }
34462         },this);
34463         
34464         var size = this.getViewSize();
34465         var w = size.width;
34466         var h = size.height;
34467         var centerW = w;
34468         var centerH = h;
34469         var centerY = 0;
34470         var centerX = 0;
34471         //var x = 0, y = 0;
34472
34473         var rs = this.regions;
34474         var north = rs["north"];
34475         var south = rs["south"]; 
34476         var west = rs["west"];
34477         var east = rs["east"];
34478         var center = rs["center"];
34479         //if(this.hideOnLayout){ // not supported anymore
34480             //c.el.setStyle("display", "none");
34481         //}
34482         if(north && north.isVisible()){
34483             var b = north.getBox();
34484             var m = north.getMargins();
34485             b.width = w - (m.left+m.right);
34486             b.x = m.left;
34487             b.y = m.top;
34488             centerY = b.height + b.y + m.bottom;
34489             centerH -= centerY;
34490             north.updateBox(this.safeBox(b));
34491         }
34492         if(south && south.isVisible()){
34493             var b = south.getBox();
34494             var m = south.getMargins();
34495             b.width = w - (m.left+m.right);
34496             b.x = m.left;
34497             var totalHeight = (b.height + m.top + m.bottom);
34498             b.y = h - totalHeight + m.top;
34499             centerH -= totalHeight;
34500             south.updateBox(this.safeBox(b));
34501         }
34502         if(west && west.isVisible()){
34503             var b = west.getBox();
34504             var m = west.getMargins();
34505             b.height = centerH - (m.top+m.bottom);
34506             b.x = m.left;
34507             b.y = centerY + m.top;
34508             var totalWidth = (b.width + m.left + m.right);
34509             centerX += totalWidth;
34510             centerW -= totalWidth;
34511             west.updateBox(this.safeBox(b));
34512         }
34513         if(east && east.isVisible()){
34514             var b = east.getBox();
34515             var m = east.getMargins();
34516             b.height = centerH - (m.top+m.bottom);
34517             var totalWidth = (b.width + m.left + m.right);
34518             b.x = w - totalWidth + m.left;
34519             b.y = centerY + m.top;
34520             centerW -= totalWidth;
34521             east.updateBox(this.safeBox(b));
34522         }
34523         if(center){
34524             var m = center.getMargins();
34525             var centerBox = {
34526                 x: centerX + m.left,
34527                 y: centerY + m.top,
34528                 width: centerW - (m.left+m.right),
34529                 height: centerH - (m.top+m.bottom)
34530             };
34531             //if(this.hideOnLayout){
34532                 //center.el.setStyle("display", "block");
34533             //}
34534             center.updateBox(this.safeBox(centerBox));
34535         }
34536         this.el.repaint();
34537         this.fireEvent("layout", this);
34538     },
34539
34540     // private
34541     safeBox : function(box){
34542         box.width = Math.max(0, box.width);
34543         box.height = Math.max(0, box.height);
34544         return box;
34545     },
34546
34547     /**
34548      * Adds a ContentPanel (or subclass) to this layout.
34549      * @param {String} target The target region key (north, south, east, west or center).
34550      * @param {Roo.ContentPanel} panel The panel to add
34551      * @return {Roo.ContentPanel} The added panel
34552      */
34553     add : function(target, panel){
34554          
34555         target = target.toLowerCase();
34556         return this.regions[target].add(panel);
34557     },
34558
34559     /**
34560      * Remove a ContentPanel (or subclass) to this layout.
34561      * @param {String} target The target region key (north, south, east, west or center).
34562      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34563      * @return {Roo.ContentPanel} The removed panel
34564      */
34565     remove : function(target, panel){
34566         target = target.toLowerCase();
34567         return this.regions[target].remove(panel);
34568     },
34569
34570     /**
34571      * Searches all regions for a panel with the specified id
34572      * @param {String} panelId
34573      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34574      */
34575     findPanel : function(panelId){
34576         var rs = this.regions;
34577         for(var target in rs){
34578             if(typeof rs[target] != "function"){
34579                 var p = rs[target].getPanel(panelId);
34580                 if(p){
34581                     return p;
34582                 }
34583             }
34584         }
34585         return null;
34586     },
34587
34588     /**
34589      * Searches all regions for a panel with the specified id and activates (shows) it.
34590      * @param {String/ContentPanel} panelId The panels id or the panel itself
34591      * @return {Roo.ContentPanel} The shown panel or null
34592      */
34593     showPanel : function(panelId) {
34594       var rs = this.regions;
34595       for(var target in rs){
34596          var r = rs[target];
34597          if(typeof r != "function"){
34598             if(r.hasPanel(panelId)){
34599                return r.showPanel(panelId);
34600             }
34601          }
34602       }
34603       return null;
34604    },
34605
34606    /**
34607      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34608      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34609      */
34610    /*
34611     restoreState : function(provider){
34612         if(!provider){
34613             provider = Roo.state.Manager;
34614         }
34615         var sm = new Roo.LayoutStateManager();
34616         sm.init(this, provider);
34617     },
34618 */
34619  
34620  
34621     /**
34622      * Adds a xtype elements to the layout.
34623      * <pre><code>
34624
34625 layout.addxtype({
34626        xtype : 'ContentPanel',
34627        region: 'west',
34628        items: [ .... ]
34629    }
34630 );
34631
34632 layout.addxtype({
34633         xtype : 'NestedLayoutPanel',
34634         region: 'west',
34635         layout: {
34636            center: { },
34637            west: { }   
34638         },
34639         items : [ ... list of content panels or nested layout panels.. ]
34640    }
34641 );
34642 </code></pre>
34643      * @param {Object} cfg Xtype definition of item to add.
34644      */
34645     addxtype : function(cfg)
34646     {
34647         // basically accepts a pannel...
34648         // can accept a layout region..!?!?
34649         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34650         
34651         
34652         // theory?  children can only be panels??
34653         
34654         //if (!cfg.xtype.match(/Panel$/)) {
34655         //    return false;
34656         //}
34657         var ret = false;
34658         
34659         if (typeof(cfg.region) == 'undefined') {
34660             Roo.log("Failed to add Panel, region was not set");
34661             Roo.log(cfg);
34662             return false;
34663         }
34664         var region = cfg.region;
34665         delete cfg.region;
34666         
34667           
34668         var xitems = [];
34669         if (cfg.items) {
34670             xitems = cfg.items;
34671             delete cfg.items;
34672         }
34673         var nb = false;
34674         
34675         switch(cfg.xtype) 
34676         {
34677             case 'Content':  // ContentPanel (el, cfg)
34678             case 'Scroll':  // ContentPanel (el, cfg)
34679             case 'View': 
34680                 cfg.autoCreate = true;
34681                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34682                 //} else {
34683                 //    var el = this.el.createChild();
34684                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34685                 //}
34686                 
34687                 this.add(region, ret);
34688                 break;
34689             
34690             /*
34691             case 'TreePanel': // our new panel!
34692                 cfg.el = this.el.createChild();
34693                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34694                 this.add(region, ret);
34695                 break;
34696             */
34697             
34698             case 'Nest': 
34699                 // create a new Layout (which is  a Border Layout...
34700                 
34701                 var clayout = cfg.layout;
34702                 clayout.el  = this.el.createChild();
34703                 clayout.items   = clayout.items  || [];
34704                 
34705                 delete cfg.layout;
34706                 
34707                 // replace this exitems with the clayout ones..
34708                 xitems = clayout.items;
34709                  
34710                 // force background off if it's in center...
34711                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34712                     cfg.background = false;
34713                 }
34714                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
34715                 
34716                 
34717                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34718                 //console.log('adding nested layout panel '  + cfg.toSource());
34719                 this.add(region, ret);
34720                 nb = {}; /// find first...
34721                 break;
34722             
34723             case 'Grid':
34724                 
34725                 // needs grid and region
34726                 
34727                 //var el = this.getRegion(region).el.createChild();
34728                 /*
34729                  *var el = this.el.createChild();
34730                 // create the grid first...
34731                 cfg.grid.container = el;
34732                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34733                 */
34734                 
34735                 if (region == 'center' && this.active ) {
34736                     cfg.background = false;
34737                 }
34738                 
34739                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34740                 
34741                 this.add(region, ret);
34742                 /*
34743                 if (cfg.background) {
34744                     // render grid on panel activation (if panel background)
34745                     ret.on('activate', function(gp) {
34746                         if (!gp.grid.rendered) {
34747                     //        gp.grid.render(el);
34748                         }
34749                     });
34750                 } else {
34751                   //  cfg.grid.render(el);
34752                 }
34753                 */
34754                 break;
34755            
34756            
34757             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34758                 // it was the old xcomponent building that caused this before.
34759                 // espeically if border is the top element in the tree.
34760                 ret = this;
34761                 break; 
34762                 
34763                     
34764                 
34765                 
34766                 
34767             default:
34768                 /*
34769                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34770                     
34771                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34772                     this.add(region, ret);
34773                 } else {
34774                 */
34775                     Roo.log(cfg);
34776                     throw "Can not add '" + cfg.xtype + "' to Border";
34777                     return null;
34778              
34779                                 
34780              
34781         }
34782         this.beginUpdate();
34783         // add children..
34784         var region = '';
34785         var abn = {};
34786         Roo.each(xitems, function(i)  {
34787             region = nb && i.region ? i.region : false;
34788             
34789             var add = ret.addxtype(i);
34790            
34791             if (region) {
34792                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
34793                 if (!i.background) {
34794                     abn[region] = nb[region] ;
34795                 }
34796             }
34797             
34798         });
34799         this.endUpdate();
34800
34801         // make the last non-background panel active..
34802         //if (nb) { Roo.log(abn); }
34803         if (nb) {
34804             
34805             for(var r in abn) {
34806                 region = this.getRegion(r);
34807                 if (region) {
34808                     // tried using nb[r], but it does not work..
34809                      
34810                     region.showPanel(abn[r]);
34811                    
34812                 }
34813             }
34814         }
34815         return ret;
34816         
34817     },
34818     
34819     
34820 // private
34821     factory : function(cfg)
34822     {
34823         
34824         var validRegions = Roo.bootstrap.layout.Border.regions;
34825
34826         var target = cfg.region;
34827         cfg.mgr = this;
34828         
34829         var r = Roo.bootstrap.layout;
34830         Roo.log(target);
34831         switch(target){
34832             case "north":
34833                 return new r.North(cfg);
34834             case "south":
34835                 return new r.South(cfg);
34836             case "east":
34837                 return new r.East(cfg);
34838             case "west":
34839                 return new r.West(cfg);
34840             case "center":
34841                 return new r.Center(cfg);
34842         }
34843         throw 'Layout region "'+target+'" not supported.';
34844     }
34845     
34846     
34847 });
34848  /*
34849  * Based on:
34850  * Ext JS Library 1.1.1
34851  * Copyright(c) 2006-2007, Ext JS, LLC.
34852  *
34853  * Originally Released Under LGPL - original licence link has changed is not relivant.
34854  *
34855  * Fork - LGPL
34856  * <script type="text/javascript">
34857  */
34858  
34859 /**
34860  * @class Roo.bootstrap.layout.Basic
34861  * @extends Roo.util.Observable
34862  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
34863  * and does not have a titlebar, tabs or any other features. All it does is size and position 
34864  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
34865  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34866  * @cfg {string}   region  the region that it inhabits..
34867  * @cfg {bool}   skipConfig skip config?
34868  * 
34869
34870  */
34871 Roo.bootstrap.layout.Basic = function(config){
34872     
34873     this.mgr = config.mgr;
34874     
34875     this.position = config.region;
34876     
34877     var skipConfig = config.skipConfig;
34878     
34879     this.events = {
34880         /**
34881          * @scope Roo.BasicLayoutRegion
34882          */
34883         
34884         /**
34885          * @event beforeremove
34886          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
34887          * @param {Roo.LayoutRegion} this
34888          * @param {Roo.ContentPanel} panel The panel
34889          * @param {Object} e The cancel event object
34890          */
34891         "beforeremove" : true,
34892         /**
34893          * @event invalidated
34894          * Fires when the layout for this region is changed.
34895          * @param {Roo.LayoutRegion} this
34896          */
34897         "invalidated" : true,
34898         /**
34899          * @event visibilitychange
34900          * Fires when this region is shown or hidden 
34901          * @param {Roo.LayoutRegion} this
34902          * @param {Boolean} visibility true or false
34903          */
34904         "visibilitychange" : true,
34905         /**
34906          * @event paneladded
34907          * Fires when a panel is added. 
34908          * @param {Roo.LayoutRegion} this
34909          * @param {Roo.ContentPanel} panel The panel
34910          */
34911         "paneladded" : true,
34912         /**
34913          * @event panelremoved
34914          * Fires when a panel is removed. 
34915          * @param {Roo.LayoutRegion} this
34916          * @param {Roo.ContentPanel} panel The panel
34917          */
34918         "panelremoved" : true,
34919         /**
34920          * @event beforecollapse
34921          * Fires when this region before collapse.
34922          * @param {Roo.LayoutRegion} this
34923          */
34924         "beforecollapse" : true,
34925         /**
34926          * @event collapsed
34927          * Fires when this region is collapsed.
34928          * @param {Roo.LayoutRegion} this
34929          */
34930         "collapsed" : true,
34931         /**
34932          * @event expanded
34933          * Fires when this region is expanded.
34934          * @param {Roo.LayoutRegion} this
34935          */
34936         "expanded" : true,
34937         /**
34938          * @event slideshow
34939          * Fires when this region is slid into view.
34940          * @param {Roo.LayoutRegion} this
34941          */
34942         "slideshow" : true,
34943         /**
34944          * @event slidehide
34945          * Fires when this region slides out of view. 
34946          * @param {Roo.LayoutRegion} this
34947          */
34948         "slidehide" : true,
34949         /**
34950          * @event panelactivated
34951          * Fires when a panel is activated. 
34952          * @param {Roo.LayoutRegion} this
34953          * @param {Roo.ContentPanel} panel The activated panel
34954          */
34955         "panelactivated" : true,
34956         /**
34957          * @event resized
34958          * Fires when the user resizes this region. 
34959          * @param {Roo.LayoutRegion} this
34960          * @param {Number} newSize The new size (width for east/west, height for north/south)
34961          */
34962         "resized" : true
34963     };
34964     /** A collection of panels in this region. @type Roo.util.MixedCollection */
34965     this.panels = new Roo.util.MixedCollection();
34966     this.panels.getKey = this.getPanelId.createDelegate(this);
34967     this.box = null;
34968     this.activePanel = null;
34969     // ensure listeners are added...
34970     
34971     if (config.listeners || config.events) {
34972         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
34973             listeners : config.listeners || {},
34974             events : config.events || {}
34975         });
34976     }
34977     
34978     if(skipConfig !== true){
34979         this.applyConfig(config);
34980     }
34981 };
34982
34983 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
34984 {
34985     getPanelId : function(p){
34986         return p.getId();
34987     },
34988     
34989     applyConfig : function(config){
34990         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34991         this.config = config;
34992         
34993     },
34994     
34995     /**
34996      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
34997      * the width, for horizontal (north, south) the height.
34998      * @param {Number} newSize The new width or height
34999      */
35000     resizeTo : function(newSize){
35001         var el = this.el ? this.el :
35002                  (this.activePanel ? this.activePanel.getEl() : null);
35003         if(el){
35004             switch(this.position){
35005                 case "east":
35006                 case "west":
35007                     el.setWidth(newSize);
35008                     this.fireEvent("resized", this, newSize);
35009                 break;
35010                 case "north":
35011                 case "south":
35012                     el.setHeight(newSize);
35013                     this.fireEvent("resized", this, newSize);
35014                 break;                
35015             }
35016         }
35017     },
35018     
35019     getBox : function(){
35020         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35021     },
35022     
35023     getMargins : function(){
35024         return this.margins;
35025     },
35026     
35027     updateBox : function(box){
35028         this.box = box;
35029         var el = this.activePanel.getEl();
35030         el.dom.style.left = box.x + "px";
35031         el.dom.style.top = box.y + "px";
35032         this.activePanel.setSize(box.width, box.height);
35033     },
35034     
35035     /**
35036      * Returns the container element for this region.
35037      * @return {Roo.Element}
35038      */
35039     getEl : function(){
35040         return this.activePanel;
35041     },
35042     
35043     /**
35044      * Returns true if this region is currently visible.
35045      * @return {Boolean}
35046      */
35047     isVisible : function(){
35048         return this.activePanel ? true : false;
35049     },
35050     
35051     setActivePanel : function(panel){
35052         panel = this.getPanel(panel);
35053         if(this.activePanel && this.activePanel != panel){
35054             this.activePanel.setActiveState(false);
35055             this.activePanel.getEl().setLeftTop(-10000,-10000);
35056         }
35057         this.activePanel = panel;
35058         panel.setActiveState(true);
35059         if(this.box){
35060             panel.setSize(this.box.width, this.box.height);
35061         }
35062         this.fireEvent("panelactivated", this, panel);
35063         this.fireEvent("invalidated");
35064     },
35065     
35066     /**
35067      * Show the specified panel.
35068      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35069      * @return {Roo.ContentPanel} The shown panel or null
35070      */
35071     showPanel : function(panel){
35072         panel = this.getPanel(panel);
35073         if(panel){
35074             this.setActivePanel(panel);
35075         }
35076         return panel;
35077     },
35078     
35079     /**
35080      * Get the active panel for this region.
35081      * @return {Roo.ContentPanel} The active panel or null
35082      */
35083     getActivePanel : function(){
35084         return this.activePanel;
35085     },
35086     
35087     /**
35088      * Add the passed ContentPanel(s)
35089      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35090      * @return {Roo.ContentPanel} The panel added (if only one was added)
35091      */
35092     add : function(panel){
35093         if(arguments.length > 1){
35094             for(var i = 0, len = arguments.length; i < len; i++) {
35095                 this.add(arguments[i]);
35096             }
35097             return null;
35098         }
35099         if(this.hasPanel(panel)){
35100             this.showPanel(panel);
35101             return panel;
35102         }
35103         var el = panel.getEl();
35104         if(el.dom.parentNode != this.mgr.el.dom){
35105             this.mgr.el.dom.appendChild(el.dom);
35106         }
35107         if(panel.setRegion){
35108             panel.setRegion(this);
35109         }
35110         this.panels.add(panel);
35111         el.setStyle("position", "absolute");
35112         if(!panel.background){
35113             this.setActivePanel(panel);
35114             if(this.config.initialSize && this.panels.getCount()==1){
35115                 this.resizeTo(this.config.initialSize);
35116             }
35117         }
35118         this.fireEvent("paneladded", this, panel);
35119         return panel;
35120     },
35121     
35122     /**
35123      * Returns true if the panel is in this region.
35124      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35125      * @return {Boolean}
35126      */
35127     hasPanel : function(panel){
35128         if(typeof panel == "object"){ // must be panel obj
35129             panel = panel.getId();
35130         }
35131         return this.getPanel(panel) ? true : false;
35132     },
35133     
35134     /**
35135      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35136      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35137      * @param {Boolean} preservePanel Overrides the config preservePanel option
35138      * @return {Roo.ContentPanel} The panel that was removed
35139      */
35140     remove : function(panel, preservePanel){
35141         panel = this.getPanel(panel);
35142         if(!panel){
35143             return null;
35144         }
35145         var e = {};
35146         this.fireEvent("beforeremove", this, panel, e);
35147         if(e.cancel === true){
35148             return null;
35149         }
35150         var panelId = panel.getId();
35151         this.panels.removeKey(panelId);
35152         return panel;
35153     },
35154     
35155     /**
35156      * Returns the panel specified or null if it's not in this region.
35157      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35158      * @return {Roo.ContentPanel}
35159      */
35160     getPanel : function(id){
35161         if(typeof id == "object"){ // must be panel obj
35162             return id;
35163         }
35164         return this.panels.get(id);
35165     },
35166     
35167     /**
35168      * Returns this regions position (north/south/east/west/center).
35169      * @return {String} 
35170      */
35171     getPosition: function(){
35172         return this.position;    
35173     }
35174 });/*
35175  * Based on:
35176  * Ext JS Library 1.1.1
35177  * Copyright(c) 2006-2007, Ext JS, LLC.
35178  *
35179  * Originally Released Under LGPL - original licence link has changed is not relivant.
35180  *
35181  * Fork - LGPL
35182  * <script type="text/javascript">
35183  */
35184  
35185 /**
35186  * @class Roo.bootstrap.layout.Region
35187  * @extends Roo.bootstrap.layout.Basic
35188  * This class represents a region in a layout manager.
35189  
35190  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35191  * @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})
35192  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35193  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35194  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35195  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35196  * @cfg {String}    title           The title for the region (overrides panel titles)
35197  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35198  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35199  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35200  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35201  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35202  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35203  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35204  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35205  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35206  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35207
35208  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35209  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35210  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35211  * @cfg {Number}    width           For East/West panels
35212  * @cfg {Number}    height          For North/South panels
35213  * @cfg {Boolean}   split           To show the splitter
35214  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35215  * 
35216  * @cfg {string}   cls             Extra CSS classes to add to region
35217  * 
35218  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35219  * @cfg {string}   region  the region that it inhabits..
35220  *
35221
35222  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35223  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35224
35225  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35226  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35227  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35228  */
35229 Roo.bootstrap.layout.Region = function(config)
35230 {
35231     this.applyConfig(config);
35232
35233     var mgr = config.mgr;
35234     var pos = config.region;
35235     config.skipConfig = true;
35236     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35237     
35238     if (mgr.el) {
35239         this.onRender(mgr.el);   
35240     }
35241      
35242     this.visible = true;
35243     this.collapsed = false;
35244     this.unrendered_panels = [];
35245 };
35246
35247 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35248
35249     position: '', // set by wrapper (eg. north/south etc..)
35250     unrendered_panels : null,  // unrendered panels.
35251     createBody : function(){
35252         /** This region's body element 
35253         * @type Roo.Element */
35254         this.bodyEl = this.el.createChild({
35255                 tag: "div",
35256                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35257         });
35258     },
35259
35260     onRender: function(ctr, pos)
35261     {
35262         var dh = Roo.DomHelper;
35263         /** This region's container element 
35264         * @type Roo.Element */
35265         this.el = dh.append(ctr.dom, {
35266                 tag: "div",
35267                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35268             }, true);
35269         /** This region's title element 
35270         * @type Roo.Element */
35271     
35272         this.titleEl = dh.append(this.el.dom,
35273             {
35274                     tag: "div",
35275                     unselectable: "on",
35276                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35277                     children:[
35278                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35279                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35280                     ]}, true);
35281         
35282         this.titleEl.enableDisplayMode();
35283         /** This region's title text element 
35284         * @type HTMLElement */
35285         this.titleTextEl = this.titleEl.dom.firstChild;
35286         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35287         /*
35288         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35289         this.closeBtn.enableDisplayMode();
35290         this.closeBtn.on("click", this.closeClicked, this);
35291         this.closeBtn.hide();
35292     */
35293         this.createBody(this.config);
35294         if(this.config.hideWhenEmpty){
35295             this.hide();
35296             this.on("paneladded", this.validateVisibility, this);
35297             this.on("panelremoved", this.validateVisibility, this);
35298         }
35299         if(this.autoScroll){
35300             this.bodyEl.setStyle("overflow", "auto");
35301         }else{
35302             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35303         }
35304         //if(c.titlebar !== false){
35305             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35306                 this.titleEl.hide();
35307             }else{
35308                 this.titleEl.show();
35309                 if(this.config.title){
35310                     this.titleTextEl.innerHTML = this.config.title;
35311                 }
35312             }
35313         //}
35314         if(this.config.collapsed){
35315             this.collapse(true);
35316         }
35317         if(this.config.hidden){
35318             this.hide();
35319         }
35320         
35321         if (this.unrendered_panels && this.unrendered_panels.length) {
35322             for (var i =0;i< this.unrendered_panels.length; i++) {
35323                 this.add(this.unrendered_panels[i]);
35324             }
35325             this.unrendered_panels = null;
35326             
35327         }
35328         
35329     },
35330     
35331     applyConfig : function(c)
35332     {
35333         /*
35334          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35335             var dh = Roo.DomHelper;
35336             if(c.titlebar !== false){
35337                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35338                 this.collapseBtn.on("click", this.collapse, this);
35339                 this.collapseBtn.enableDisplayMode();
35340                 /*
35341                 if(c.showPin === true || this.showPin){
35342                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35343                     this.stickBtn.enableDisplayMode();
35344                     this.stickBtn.on("click", this.expand, this);
35345                     this.stickBtn.hide();
35346                 }
35347                 
35348             }
35349             */
35350             /** This region's collapsed element
35351             * @type Roo.Element */
35352             /*
35353              *
35354             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35355                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35356             ]}, true);
35357             
35358             if(c.floatable !== false){
35359                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35360                this.collapsedEl.on("click", this.collapseClick, this);
35361             }
35362
35363             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35364                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35365                    id: "message", unselectable: "on", style:{"float":"left"}});
35366                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35367              }
35368             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35369             this.expandBtn.on("click", this.expand, this);
35370             
35371         }
35372         
35373         if(this.collapseBtn){
35374             this.collapseBtn.setVisible(c.collapsible == true);
35375         }
35376         
35377         this.cmargins = c.cmargins || this.cmargins ||
35378                          (this.position == "west" || this.position == "east" ?
35379                              {top: 0, left: 2, right:2, bottom: 0} :
35380                              {top: 2, left: 0, right:0, bottom: 2});
35381         */
35382         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35383         
35384         
35385         this.bottomTabs = c.tabPosition != "top";
35386         
35387         this.autoScroll = c.autoScroll || false;
35388         
35389         
35390        
35391         
35392         this.duration = c.duration || .30;
35393         this.slideDuration = c.slideDuration || .45;
35394         this.config = c;
35395        
35396     },
35397     /**
35398      * Returns true if this region is currently visible.
35399      * @return {Boolean}
35400      */
35401     isVisible : function(){
35402         return this.visible;
35403     },
35404
35405     /**
35406      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35407      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35408      */
35409     //setCollapsedTitle : function(title){
35410     //    title = title || "&#160;";
35411      //   if(this.collapsedTitleTextEl){
35412       //      this.collapsedTitleTextEl.innerHTML = title;
35413        // }
35414     //},
35415
35416     getBox : function(){
35417         var b;
35418       //  if(!this.collapsed){
35419             b = this.el.getBox(false, true);
35420        // }else{
35421           //  b = this.collapsedEl.getBox(false, true);
35422         //}
35423         return b;
35424     },
35425
35426     getMargins : function(){
35427         return this.margins;
35428         //return this.collapsed ? this.cmargins : this.margins;
35429     },
35430 /*
35431     highlight : function(){
35432         this.el.addClass("x-layout-panel-dragover");
35433     },
35434
35435     unhighlight : function(){
35436         this.el.removeClass("x-layout-panel-dragover");
35437     },
35438 */
35439     updateBox : function(box)
35440     {
35441         if (!this.bodyEl) {
35442             return; // not rendered yet..
35443         }
35444         
35445         this.box = box;
35446         if(!this.collapsed){
35447             this.el.dom.style.left = box.x + "px";
35448             this.el.dom.style.top = box.y + "px";
35449             this.updateBody(box.width, box.height);
35450         }else{
35451             this.collapsedEl.dom.style.left = box.x + "px";
35452             this.collapsedEl.dom.style.top = box.y + "px";
35453             this.collapsedEl.setSize(box.width, box.height);
35454         }
35455         if(this.tabs){
35456             this.tabs.autoSizeTabs();
35457         }
35458     },
35459
35460     updateBody : function(w, h)
35461     {
35462         if(w !== null){
35463             this.el.setWidth(w);
35464             w -= this.el.getBorderWidth("rl");
35465             if(this.config.adjustments){
35466                 w += this.config.adjustments[0];
35467             }
35468         }
35469         if(h !== null && h > 0){
35470             this.el.setHeight(h);
35471             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35472             h -= this.el.getBorderWidth("tb");
35473             if(this.config.adjustments){
35474                 h += this.config.adjustments[1];
35475             }
35476             this.bodyEl.setHeight(h);
35477             if(this.tabs){
35478                 h = this.tabs.syncHeight(h);
35479             }
35480         }
35481         if(this.panelSize){
35482             w = w !== null ? w : this.panelSize.width;
35483             h = h !== null ? h : this.panelSize.height;
35484         }
35485         if(this.activePanel){
35486             var el = this.activePanel.getEl();
35487             w = w !== null ? w : el.getWidth();
35488             h = h !== null ? h : el.getHeight();
35489             this.panelSize = {width: w, height: h};
35490             this.activePanel.setSize(w, h);
35491         }
35492         if(Roo.isIE && this.tabs){
35493             this.tabs.el.repaint();
35494         }
35495     },
35496
35497     /**
35498      * Returns the container element for this region.
35499      * @return {Roo.Element}
35500      */
35501     getEl : function(){
35502         return this.el;
35503     },
35504
35505     /**
35506      * Hides this region.
35507      */
35508     hide : function(){
35509         //if(!this.collapsed){
35510             this.el.dom.style.left = "-2000px";
35511             this.el.hide();
35512         //}else{
35513          //   this.collapsedEl.dom.style.left = "-2000px";
35514          //   this.collapsedEl.hide();
35515        // }
35516         this.visible = false;
35517         this.fireEvent("visibilitychange", this, false);
35518     },
35519
35520     /**
35521      * Shows this region if it was previously hidden.
35522      */
35523     show : function(){
35524         //if(!this.collapsed){
35525             this.el.show();
35526         //}else{
35527         //    this.collapsedEl.show();
35528        // }
35529         this.visible = true;
35530         this.fireEvent("visibilitychange", this, true);
35531     },
35532 /*
35533     closeClicked : function(){
35534         if(this.activePanel){
35535             this.remove(this.activePanel);
35536         }
35537     },
35538
35539     collapseClick : function(e){
35540         if(this.isSlid){
35541            e.stopPropagation();
35542            this.slideIn();
35543         }else{
35544            e.stopPropagation();
35545            this.slideOut();
35546         }
35547     },
35548 */
35549     /**
35550      * Collapses this region.
35551      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35552      */
35553     /*
35554     collapse : function(skipAnim, skipCheck = false){
35555         if(this.collapsed) {
35556             return;
35557         }
35558         
35559         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35560             
35561             this.collapsed = true;
35562             if(this.split){
35563                 this.split.el.hide();
35564             }
35565             if(this.config.animate && skipAnim !== true){
35566                 this.fireEvent("invalidated", this);
35567                 this.animateCollapse();
35568             }else{
35569                 this.el.setLocation(-20000,-20000);
35570                 this.el.hide();
35571                 this.collapsedEl.show();
35572                 this.fireEvent("collapsed", this);
35573                 this.fireEvent("invalidated", this);
35574             }
35575         }
35576         
35577     },
35578 */
35579     animateCollapse : function(){
35580         // overridden
35581     },
35582
35583     /**
35584      * Expands this region if it was previously collapsed.
35585      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35586      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35587      */
35588     /*
35589     expand : function(e, skipAnim){
35590         if(e) {
35591             e.stopPropagation();
35592         }
35593         if(!this.collapsed || this.el.hasActiveFx()) {
35594             return;
35595         }
35596         if(this.isSlid){
35597             this.afterSlideIn();
35598             skipAnim = true;
35599         }
35600         this.collapsed = false;
35601         if(this.config.animate && skipAnim !== true){
35602             this.animateExpand();
35603         }else{
35604             this.el.show();
35605             if(this.split){
35606                 this.split.el.show();
35607             }
35608             this.collapsedEl.setLocation(-2000,-2000);
35609             this.collapsedEl.hide();
35610             this.fireEvent("invalidated", this);
35611             this.fireEvent("expanded", this);
35612         }
35613     },
35614 */
35615     animateExpand : function(){
35616         // overridden
35617     },
35618
35619     initTabs : function()
35620     {
35621         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35622         
35623         var ts = new Roo.bootstrap.panel.Tabs({
35624                 el: this.bodyEl.dom,
35625                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35626                 disableTooltips: this.config.disableTabTips,
35627                 toolbar : this.config.toolbar
35628             });
35629         
35630         if(this.config.hideTabs){
35631             ts.stripWrap.setDisplayed(false);
35632         }
35633         this.tabs = ts;
35634         ts.resizeTabs = this.config.resizeTabs === true;
35635         ts.minTabWidth = this.config.minTabWidth || 40;
35636         ts.maxTabWidth = this.config.maxTabWidth || 250;
35637         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35638         ts.monitorResize = false;
35639         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35640         ts.bodyEl.addClass('roo-layout-tabs-body');
35641         this.panels.each(this.initPanelAsTab, this);
35642     },
35643
35644     initPanelAsTab : function(panel){
35645         var ti = this.tabs.addTab(
35646             panel.getEl().id,
35647             panel.getTitle(),
35648             null,
35649             this.config.closeOnTab && panel.isClosable(),
35650             panel.tpl
35651         );
35652         if(panel.tabTip !== undefined){
35653             ti.setTooltip(panel.tabTip);
35654         }
35655         ti.on("activate", function(){
35656               this.setActivePanel(panel);
35657         }, this);
35658         
35659         if(this.config.closeOnTab){
35660             ti.on("beforeclose", function(t, e){
35661                 e.cancel = true;
35662                 this.remove(panel);
35663             }, this);
35664         }
35665         
35666         panel.tabItem = ti;
35667         
35668         return ti;
35669     },
35670
35671     updatePanelTitle : function(panel, title)
35672     {
35673         if(this.activePanel == panel){
35674             this.updateTitle(title);
35675         }
35676         if(this.tabs){
35677             var ti = this.tabs.getTab(panel.getEl().id);
35678             ti.setText(title);
35679             if(panel.tabTip !== undefined){
35680                 ti.setTooltip(panel.tabTip);
35681             }
35682         }
35683     },
35684
35685     updateTitle : function(title){
35686         if(this.titleTextEl && !this.config.title){
35687             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
35688         }
35689     },
35690
35691     setActivePanel : function(panel)
35692     {
35693         panel = this.getPanel(panel);
35694         if(this.activePanel && this.activePanel != panel){
35695             if(this.activePanel.setActiveState(false) === false){
35696                 return;
35697             }
35698         }
35699         this.activePanel = panel;
35700         panel.setActiveState(true);
35701         if(this.panelSize){
35702             panel.setSize(this.panelSize.width, this.panelSize.height);
35703         }
35704         if(this.closeBtn){
35705             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35706         }
35707         this.updateTitle(panel.getTitle());
35708         if(this.tabs){
35709             this.fireEvent("invalidated", this);
35710         }
35711         this.fireEvent("panelactivated", this, panel);
35712     },
35713
35714     /**
35715      * Shows the specified panel.
35716      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35717      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35718      */
35719     showPanel : function(panel)
35720     {
35721         panel = this.getPanel(panel);
35722         if(panel){
35723             if(this.tabs){
35724                 var tab = this.tabs.getTab(panel.getEl().id);
35725                 if(tab.isHidden()){
35726                     this.tabs.unhideTab(tab.id);
35727                 }
35728                 tab.activate();
35729             }else{
35730                 this.setActivePanel(panel);
35731             }
35732         }
35733         return panel;
35734     },
35735
35736     /**
35737      * Get the active panel for this region.
35738      * @return {Roo.ContentPanel} The active panel or null
35739      */
35740     getActivePanel : function(){
35741         return this.activePanel;
35742     },
35743
35744     validateVisibility : function(){
35745         if(this.panels.getCount() < 1){
35746             this.updateTitle("&#160;");
35747             this.closeBtn.hide();
35748             this.hide();
35749         }else{
35750             if(!this.isVisible()){
35751                 this.show();
35752             }
35753         }
35754     },
35755
35756     /**
35757      * Adds the passed ContentPanel(s) to this region.
35758      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35759      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35760      */
35761     add : function(panel)
35762     {
35763         if(arguments.length > 1){
35764             for(var i = 0, len = arguments.length; i < len; i++) {
35765                 this.add(arguments[i]);
35766             }
35767             return null;
35768         }
35769         
35770         // if we have not been rendered yet, then we can not really do much of this..
35771         if (!this.bodyEl) {
35772             this.unrendered_panels.push(panel);
35773             return panel;
35774         }
35775         
35776         
35777         
35778         
35779         if(this.hasPanel(panel)){
35780             this.showPanel(panel);
35781             return panel;
35782         }
35783         panel.setRegion(this);
35784         this.panels.add(panel);
35785        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
35786             // sinle panel - no tab...?? would it not be better to render it with the tabs,
35787             // and hide them... ???
35788             this.bodyEl.dom.appendChild(panel.getEl().dom);
35789             if(panel.background !== true){
35790                 this.setActivePanel(panel);
35791             }
35792             this.fireEvent("paneladded", this, panel);
35793             return panel;
35794         }
35795         */
35796         if(!this.tabs){
35797             this.initTabs();
35798         }else{
35799             this.initPanelAsTab(panel);
35800         }
35801         
35802         
35803         if(panel.background !== true){
35804             this.tabs.activate(panel.getEl().id);
35805         }
35806         this.fireEvent("paneladded", this, panel);
35807         return panel;
35808     },
35809
35810     /**
35811      * Hides the tab for the specified panel.
35812      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35813      */
35814     hidePanel : function(panel){
35815         if(this.tabs && (panel = this.getPanel(panel))){
35816             this.tabs.hideTab(panel.getEl().id);
35817         }
35818     },
35819
35820     /**
35821      * Unhides the tab for a previously hidden panel.
35822      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35823      */
35824     unhidePanel : function(panel){
35825         if(this.tabs && (panel = this.getPanel(panel))){
35826             this.tabs.unhideTab(panel.getEl().id);
35827         }
35828     },
35829
35830     clearPanels : function(){
35831         while(this.panels.getCount() > 0){
35832              this.remove(this.panels.first());
35833         }
35834     },
35835
35836     /**
35837      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35838      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35839      * @param {Boolean} preservePanel Overrides the config preservePanel option
35840      * @return {Roo.ContentPanel} The panel that was removed
35841      */
35842     remove : function(panel, preservePanel)
35843     {
35844         panel = this.getPanel(panel);
35845         if(!panel){
35846             return null;
35847         }
35848         var e = {};
35849         this.fireEvent("beforeremove", this, panel, e);
35850         if(e.cancel === true){
35851             return null;
35852         }
35853         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
35854         var panelId = panel.getId();
35855         this.panels.removeKey(panelId);
35856         if(preservePanel){
35857             document.body.appendChild(panel.getEl().dom);
35858         }
35859         if(this.tabs){
35860             this.tabs.removeTab(panel.getEl().id);
35861         }else if (!preservePanel){
35862             this.bodyEl.dom.removeChild(panel.getEl().dom);
35863         }
35864         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
35865             var p = this.panels.first();
35866             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
35867             tempEl.appendChild(p.getEl().dom);
35868             this.bodyEl.update("");
35869             this.bodyEl.dom.appendChild(p.getEl().dom);
35870             tempEl = null;
35871             this.updateTitle(p.getTitle());
35872             this.tabs = null;
35873             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
35874             this.setActivePanel(p);
35875         }
35876         panel.setRegion(null);
35877         if(this.activePanel == panel){
35878             this.activePanel = null;
35879         }
35880         if(this.config.autoDestroy !== false && preservePanel !== true){
35881             try{panel.destroy();}catch(e){}
35882         }
35883         this.fireEvent("panelremoved", this, panel);
35884         return panel;
35885     },
35886
35887     /**
35888      * Returns the TabPanel component used by this region
35889      * @return {Roo.TabPanel}
35890      */
35891     getTabs : function(){
35892         return this.tabs;
35893     },
35894
35895     createTool : function(parentEl, className){
35896         var btn = Roo.DomHelper.append(parentEl, {
35897             tag: "div",
35898             cls: "x-layout-tools-button",
35899             children: [ {
35900                 tag: "div",
35901                 cls: "roo-layout-tools-button-inner " + className,
35902                 html: "&#160;"
35903             }]
35904         }, true);
35905         btn.addClassOnOver("roo-layout-tools-button-over");
35906         return btn;
35907     }
35908 });/*
35909  * Based on:
35910  * Ext JS Library 1.1.1
35911  * Copyright(c) 2006-2007, Ext JS, LLC.
35912  *
35913  * Originally Released Under LGPL - original licence link has changed is not relivant.
35914  *
35915  * Fork - LGPL
35916  * <script type="text/javascript">
35917  */
35918  
35919
35920
35921 /**
35922  * @class Roo.SplitLayoutRegion
35923  * @extends Roo.LayoutRegion
35924  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
35925  */
35926 Roo.bootstrap.layout.Split = function(config){
35927     this.cursor = config.cursor;
35928     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
35929 };
35930
35931 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
35932 {
35933     splitTip : "Drag to resize.",
35934     collapsibleSplitTip : "Drag to resize. Double click to hide.",
35935     useSplitTips : false,
35936
35937     applyConfig : function(config){
35938         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
35939     },
35940     
35941     onRender : function(ctr,pos) {
35942         
35943         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
35944         if(!this.config.split){
35945             return;
35946         }
35947         if(!this.split){
35948             
35949             var splitEl = Roo.DomHelper.append(ctr.dom,  {
35950                             tag: "div",
35951                             id: this.el.id + "-split",
35952                             cls: "roo-layout-split roo-layout-split-"+this.position,
35953                             html: "&#160;"
35954             });
35955             /** The SplitBar for this region 
35956             * @type Roo.SplitBar */
35957             // does not exist yet...
35958             Roo.log([this.position, this.orientation]);
35959             
35960             this.split = new Roo.bootstrap.SplitBar({
35961                 dragElement : splitEl,
35962                 resizingElement: this.el,
35963                 orientation : this.orientation
35964             });
35965             
35966             this.split.on("moved", this.onSplitMove, this);
35967             this.split.useShim = this.config.useShim === true;
35968             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
35969             if(this.useSplitTips){
35970                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
35971             }
35972             //if(config.collapsible){
35973             //    this.split.el.on("dblclick", this.collapse,  this);
35974             //}
35975         }
35976         if(typeof this.config.minSize != "undefined"){
35977             this.split.minSize = this.config.minSize;
35978         }
35979         if(typeof this.config.maxSize != "undefined"){
35980             this.split.maxSize = this.config.maxSize;
35981         }
35982         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
35983             this.hideSplitter();
35984         }
35985         
35986     },
35987
35988     getHMaxSize : function(){
35989          var cmax = this.config.maxSize || 10000;
35990          var center = this.mgr.getRegion("center");
35991          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
35992     },
35993
35994     getVMaxSize : function(){
35995          var cmax = this.config.maxSize || 10000;
35996          var center = this.mgr.getRegion("center");
35997          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
35998     },
35999
36000     onSplitMove : function(split, newSize){
36001         this.fireEvent("resized", this, newSize);
36002     },
36003     
36004     /** 
36005      * Returns the {@link Roo.SplitBar} for this region.
36006      * @return {Roo.SplitBar}
36007      */
36008     getSplitBar : function(){
36009         return this.split;
36010     },
36011     
36012     hide : function(){
36013         this.hideSplitter();
36014         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36015     },
36016
36017     hideSplitter : function(){
36018         if(this.split){
36019             this.split.el.setLocation(-2000,-2000);
36020             this.split.el.hide();
36021         }
36022     },
36023
36024     show : function(){
36025         if(this.split){
36026             this.split.el.show();
36027         }
36028         Roo.bootstrap.layout.Split.superclass.show.call(this);
36029     },
36030     
36031     beforeSlide: function(){
36032         if(Roo.isGecko){// firefox overflow auto bug workaround
36033             this.bodyEl.clip();
36034             if(this.tabs) {
36035                 this.tabs.bodyEl.clip();
36036             }
36037             if(this.activePanel){
36038                 this.activePanel.getEl().clip();
36039                 
36040                 if(this.activePanel.beforeSlide){
36041                     this.activePanel.beforeSlide();
36042                 }
36043             }
36044         }
36045     },
36046     
36047     afterSlide : function(){
36048         if(Roo.isGecko){// firefox overflow auto bug workaround
36049             this.bodyEl.unclip();
36050             if(this.tabs) {
36051                 this.tabs.bodyEl.unclip();
36052             }
36053             if(this.activePanel){
36054                 this.activePanel.getEl().unclip();
36055                 if(this.activePanel.afterSlide){
36056                     this.activePanel.afterSlide();
36057                 }
36058             }
36059         }
36060     },
36061
36062     initAutoHide : function(){
36063         if(this.autoHide !== false){
36064             if(!this.autoHideHd){
36065                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36066                 this.autoHideHd = {
36067                     "mouseout": function(e){
36068                         if(!e.within(this.el, true)){
36069                             st.delay(500);
36070                         }
36071                     },
36072                     "mouseover" : function(e){
36073                         st.cancel();
36074                     },
36075                     scope : this
36076                 };
36077             }
36078             this.el.on(this.autoHideHd);
36079         }
36080     },
36081
36082     clearAutoHide : function(){
36083         if(this.autoHide !== false){
36084             this.el.un("mouseout", this.autoHideHd.mouseout);
36085             this.el.un("mouseover", this.autoHideHd.mouseover);
36086         }
36087     },
36088
36089     clearMonitor : function(){
36090         Roo.get(document).un("click", this.slideInIf, this);
36091     },
36092
36093     // these names are backwards but not changed for compat
36094     slideOut : function(){
36095         if(this.isSlid || this.el.hasActiveFx()){
36096             return;
36097         }
36098         this.isSlid = true;
36099         if(this.collapseBtn){
36100             this.collapseBtn.hide();
36101         }
36102         this.closeBtnState = this.closeBtn.getStyle('display');
36103         this.closeBtn.hide();
36104         if(this.stickBtn){
36105             this.stickBtn.show();
36106         }
36107         this.el.show();
36108         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36109         this.beforeSlide();
36110         this.el.setStyle("z-index", 10001);
36111         this.el.slideIn(this.getSlideAnchor(), {
36112             callback: function(){
36113                 this.afterSlide();
36114                 this.initAutoHide();
36115                 Roo.get(document).on("click", this.slideInIf, this);
36116                 this.fireEvent("slideshow", this);
36117             },
36118             scope: this,
36119             block: true
36120         });
36121     },
36122
36123     afterSlideIn : function(){
36124         this.clearAutoHide();
36125         this.isSlid = false;
36126         this.clearMonitor();
36127         this.el.setStyle("z-index", "");
36128         if(this.collapseBtn){
36129             this.collapseBtn.show();
36130         }
36131         this.closeBtn.setStyle('display', this.closeBtnState);
36132         if(this.stickBtn){
36133             this.stickBtn.hide();
36134         }
36135         this.fireEvent("slidehide", this);
36136     },
36137
36138     slideIn : function(cb){
36139         if(!this.isSlid || this.el.hasActiveFx()){
36140             Roo.callback(cb);
36141             return;
36142         }
36143         this.isSlid = false;
36144         this.beforeSlide();
36145         this.el.slideOut(this.getSlideAnchor(), {
36146             callback: function(){
36147                 this.el.setLeftTop(-10000, -10000);
36148                 this.afterSlide();
36149                 this.afterSlideIn();
36150                 Roo.callback(cb);
36151             },
36152             scope: this,
36153             block: true
36154         });
36155     },
36156     
36157     slideInIf : function(e){
36158         if(!e.within(this.el)){
36159             this.slideIn();
36160         }
36161     },
36162
36163     animateCollapse : function(){
36164         this.beforeSlide();
36165         this.el.setStyle("z-index", 20000);
36166         var anchor = this.getSlideAnchor();
36167         this.el.slideOut(anchor, {
36168             callback : function(){
36169                 this.el.setStyle("z-index", "");
36170                 this.collapsedEl.slideIn(anchor, {duration:.3});
36171                 this.afterSlide();
36172                 this.el.setLocation(-10000,-10000);
36173                 this.el.hide();
36174                 this.fireEvent("collapsed", this);
36175             },
36176             scope: this,
36177             block: true
36178         });
36179     },
36180
36181     animateExpand : function(){
36182         this.beforeSlide();
36183         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36184         this.el.setStyle("z-index", 20000);
36185         this.collapsedEl.hide({
36186             duration:.1
36187         });
36188         this.el.slideIn(this.getSlideAnchor(), {
36189             callback : function(){
36190                 this.el.setStyle("z-index", "");
36191                 this.afterSlide();
36192                 if(this.split){
36193                     this.split.el.show();
36194                 }
36195                 this.fireEvent("invalidated", this);
36196                 this.fireEvent("expanded", this);
36197             },
36198             scope: this,
36199             block: true
36200         });
36201     },
36202
36203     anchors : {
36204         "west" : "left",
36205         "east" : "right",
36206         "north" : "top",
36207         "south" : "bottom"
36208     },
36209
36210     sanchors : {
36211         "west" : "l",
36212         "east" : "r",
36213         "north" : "t",
36214         "south" : "b"
36215     },
36216
36217     canchors : {
36218         "west" : "tl-tr",
36219         "east" : "tr-tl",
36220         "north" : "tl-bl",
36221         "south" : "bl-tl"
36222     },
36223
36224     getAnchor : function(){
36225         return this.anchors[this.position];
36226     },
36227
36228     getCollapseAnchor : function(){
36229         return this.canchors[this.position];
36230     },
36231
36232     getSlideAnchor : function(){
36233         return this.sanchors[this.position];
36234     },
36235
36236     getAlignAdj : function(){
36237         var cm = this.cmargins;
36238         switch(this.position){
36239             case "west":
36240                 return [0, 0];
36241             break;
36242             case "east":
36243                 return [0, 0];
36244             break;
36245             case "north":
36246                 return [0, 0];
36247             break;
36248             case "south":
36249                 return [0, 0];
36250             break;
36251         }
36252     },
36253
36254     getExpandAdj : function(){
36255         var c = this.collapsedEl, cm = this.cmargins;
36256         switch(this.position){
36257             case "west":
36258                 return [-(cm.right+c.getWidth()+cm.left), 0];
36259             break;
36260             case "east":
36261                 return [cm.right+c.getWidth()+cm.left, 0];
36262             break;
36263             case "north":
36264                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36265             break;
36266             case "south":
36267                 return [0, cm.top+cm.bottom+c.getHeight()];
36268             break;
36269         }
36270     }
36271 });/*
36272  * Based on:
36273  * Ext JS Library 1.1.1
36274  * Copyright(c) 2006-2007, Ext JS, LLC.
36275  *
36276  * Originally Released Under LGPL - original licence link has changed is not relivant.
36277  *
36278  * Fork - LGPL
36279  * <script type="text/javascript">
36280  */
36281 /*
36282  * These classes are private internal classes
36283  */
36284 Roo.bootstrap.layout.Center = function(config){
36285     config.region = "center";
36286     Roo.bootstrap.layout.Region.call(this, config);
36287     this.visible = true;
36288     this.minWidth = config.minWidth || 20;
36289     this.minHeight = config.minHeight || 20;
36290 };
36291
36292 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36293     hide : function(){
36294         // center panel can't be hidden
36295     },
36296     
36297     show : function(){
36298         // center panel can't be hidden
36299     },
36300     
36301     getMinWidth: function(){
36302         return this.minWidth;
36303     },
36304     
36305     getMinHeight: function(){
36306         return this.minHeight;
36307     }
36308 });
36309
36310
36311
36312
36313  
36314
36315
36316
36317
36318
36319 Roo.bootstrap.layout.North = function(config)
36320 {
36321     config.region = 'north';
36322     config.cursor = 'n-resize';
36323     
36324     Roo.bootstrap.layout.Split.call(this, config);
36325     
36326     
36327     if(this.split){
36328         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36329         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36330         this.split.el.addClass("roo-layout-split-v");
36331     }
36332     var size = config.initialSize || config.height;
36333     if(typeof size != "undefined"){
36334         this.el.setHeight(size);
36335     }
36336 };
36337 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36338 {
36339     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36340     
36341     
36342     
36343     getBox : function(){
36344         if(this.collapsed){
36345             return this.collapsedEl.getBox();
36346         }
36347         var box = this.el.getBox();
36348         if(this.split){
36349             box.height += this.split.el.getHeight();
36350         }
36351         return box;
36352     },
36353     
36354     updateBox : function(box){
36355         if(this.split && !this.collapsed){
36356             box.height -= this.split.el.getHeight();
36357             this.split.el.setLeft(box.x);
36358             this.split.el.setTop(box.y+box.height);
36359             this.split.el.setWidth(box.width);
36360         }
36361         if(this.collapsed){
36362             this.updateBody(box.width, null);
36363         }
36364         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36365     }
36366 });
36367
36368
36369
36370
36371
36372 Roo.bootstrap.layout.South = function(config){
36373     config.region = 'south';
36374     config.cursor = 's-resize';
36375     Roo.bootstrap.layout.Split.call(this, config);
36376     if(this.split){
36377         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36378         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36379         this.split.el.addClass("roo-layout-split-v");
36380     }
36381     var size = config.initialSize || config.height;
36382     if(typeof size != "undefined"){
36383         this.el.setHeight(size);
36384     }
36385 };
36386
36387 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36388     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36389     getBox : function(){
36390         if(this.collapsed){
36391             return this.collapsedEl.getBox();
36392         }
36393         var box = this.el.getBox();
36394         if(this.split){
36395             var sh = this.split.el.getHeight();
36396             box.height += sh;
36397             box.y -= sh;
36398         }
36399         return box;
36400     },
36401     
36402     updateBox : function(box){
36403         if(this.split && !this.collapsed){
36404             var sh = this.split.el.getHeight();
36405             box.height -= sh;
36406             box.y += sh;
36407             this.split.el.setLeft(box.x);
36408             this.split.el.setTop(box.y-sh);
36409             this.split.el.setWidth(box.width);
36410         }
36411         if(this.collapsed){
36412             this.updateBody(box.width, null);
36413         }
36414         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36415     }
36416 });
36417
36418 Roo.bootstrap.layout.East = function(config){
36419     config.region = "east";
36420     config.cursor = "e-resize";
36421     Roo.bootstrap.layout.Split.call(this, config);
36422     if(this.split){
36423         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36424         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36425         this.split.el.addClass("roo-layout-split-h");
36426     }
36427     var size = config.initialSize || config.width;
36428     if(typeof size != "undefined"){
36429         this.el.setWidth(size);
36430     }
36431 };
36432 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36433     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36434     getBox : function(){
36435         if(this.collapsed){
36436             return this.collapsedEl.getBox();
36437         }
36438         var box = this.el.getBox();
36439         if(this.split){
36440             var sw = this.split.el.getWidth();
36441             box.width += sw;
36442             box.x -= sw;
36443         }
36444         return box;
36445     },
36446
36447     updateBox : function(box){
36448         if(this.split && !this.collapsed){
36449             var sw = this.split.el.getWidth();
36450             box.width -= sw;
36451             this.split.el.setLeft(box.x);
36452             this.split.el.setTop(box.y);
36453             this.split.el.setHeight(box.height);
36454             box.x += sw;
36455         }
36456         if(this.collapsed){
36457             this.updateBody(null, box.height);
36458         }
36459         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36460     }
36461 });
36462
36463 Roo.bootstrap.layout.West = function(config){
36464     config.region = "west";
36465     config.cursor = "w-resize";
36466     
36467     Roo.bootstrap.layout.Split.call(this, config);
36468     if(this.split){
36469         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36470         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36471         this.split.el.addClass("roo-layout-split-h");
36472     }
36473     
36474 };
36475 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36476     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36477     
36478     onRender: function(ctr, pos)
36479     {
36480         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36481         var size = this.config.initialSize || this.config.width;
36482         if(typeof size != "undefined"){
36483             this.el.setWidth(size);
36484         }
36485     },
36486     
36487     getBox : function(){
36488         if(this.collapsed){
36489             return this.collapsedEl.getBox();
36490         }
36491         var box = this.el.getBox();
36492         if(this.split){
36493             box.width += this.split.el.getWidth();
36494         }
36495         return box;
36496     },
36497     
36498     updateBox : function(box){
36499         if(this.split && !this.collapsed){
36500             var sw = this.split.el.getWidth();
36501             box.width -= sw;
36502             this.split.el.setLeft(box.x+box.width);
36503             this.split.el.setTop(box.y);
36504             this.split.el.setHeight(box.height);
36505         }
36506         if(this.collapsed){
36507             this.updateBody(null, box.height);
36508         }
36509         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36510     }
36511 });
36512 Roo.namespace("Roo.bootstrap.panel");/*
36513  * Based on:
36514  * Ext JS Library 1.1.1
36515  * Copyright(c) 2006-2007, Ext JS, LLC.
36516  *
36517  * Originally Released Under LGPL - original licence link has changed is not relivant.
36518  *
36519  * Fork - LGPL
36520  * <script type="text/javascript">
36521  */
36522 /**
36523  * @class Roo.ContentPanel
36524  * @extends Roo.util.Observable
36525  * A basic ContentPanel element.
36526  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36527  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36528  * @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
36529  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36530  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36531  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36532  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36533  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36534  * @cfg {String} title          The title for this panel
36535  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36536  * @cfg {String} url            Calls {@link #setUrl} with this value
36537  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36538  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36539  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36540  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36541  * @cfg {Boolean} badges render the badges
36542
36543  * @constructor
36544  * Create a new ContentPanel.
36545  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36546  * @param {String/Object} config A string to set only the title or a config object
36547  * @param {String} content (optional) Set the HTML content for this panel
36548  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36549  */
36550 Roo.bootstrap.panel.Content = function( config){
36551     
36552     this.tpl = config.tpl || false;
36553     
36554     var el = config.el;
36555     var content = config.content;
36556
36557     if(config.autoCreate){ // xtype is available if this is called from factory
36558         el = Roo.id();
36559     }
36560     this.el = Roo.get(el);
36561     if(!this.el && config && config.autoCreate){
36562         if(typeof config.autoCreate == "object"){
36563             if(!config.autoCreate.id){
36564                 config.autoCreate.id = config.id||el;
36565             }
36566             this.el = Roo.DomHelper.append(document.body,
36567                         config.autoCreate, true);
36568         }else{
36569             var elcfg =  {   tag: "div",
36570                             cls: "roo-layout-inactive-content",
36571                             id: config.id||el
36572                             };
36573             if (config.html) {
36574                 elcfg.html = config.html;
36575                 
36576             }
36577                         
36578             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36579         }
36580     } 
36581     this.closable = false;
36582     this.loaded = false;
36583     this.active = false;
36584    
36585       
36586     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36587         
36588         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36589         
36590         this.wrapEl = this.el; //this.el.wrap();
36591         var ti = [];
36592         if (config.toolbar.items) {
36593             ti = config.toolbar.items ;
36594             delete config.toolbar.items ;
36595         }
36596         
36597         var nitems = [];
36598         this.toolbar.render(this.wrapEl, 'before');
36599         for(var i =0;i < ti.length;i++) {
36600           //  Roo.log(['add child', items[i]]);
36601             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36602         }
36603         this.toolbar.items = nitems;
36604         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36605         delete config.toolbar;
36606         
36607     }
36608     /*
36609     // xtype created footer. - not sure if will work as we normally have to render first..
36610     if (this.footer && !this.footer.el && this.footer.xtype) {
36611         if (!this.wrapEl) {
36612             this.wrapEl = this.el.wrap();
36613         }
36614     
36615         this.footer.container = this.wrapEl.createChild();
36616          
36617         this.footer = Roo.factory(this.footer, Roo);
36618         
36619     }
36620     */
36621     
36622      if(typeof config == "string"){
36623         this.title = config;
36624     }else{
36625         Roo.apply(this, config);
36626     }
36627     
36628     if(this.resizeEl){
36629         this.resizeEl = Roo.get(this.resizeEl, true);
36630     }else{
36631         this.resizeEl = this.el;
36632     }
36633     // handle view.xtype
36634     
36635  
36636     
36637     
36638     this.addEvents({
36639         /**
36640          * @event activate
36641          * Fires when this panel is activated. 
36642          * @param {Roo.ContentPanel} this
36643          */
36644         "activate" : true,
36645         /**
36646          * @event deactivate
36647          * Fires when this panel is activated. 
36648          * @param {Roo.ContentPanel} this
36649          */
36650         "deactivate" : true,
36651
36652         /**
36653          * @event resize
36654          * Fires when this panel is resized if fitToFrame is true.
36655          * @param {Roo.ContentPanel} this
36656          * @param {Number} width The width after any component adjustments
36657          * @param {Number} height The height after any component adjustments
36658          */
36659         "resize" : true,
36660         
36661          /**
36662          * @event render
36663          * Fires when this tab is created
36664          * @param {Roo.ContentPanel} this
36665          */
36666         "render" : true
36667         
36668         
36669         
36670     });
36671     
36672
36673     
36674     
36675     if(this.autoScroll){
36676         this.resizeEl.setStyle("overflow", "auto");
36677     } else {
36678         // fix randome scrolling
36679         //this.el.on('scroll', function() {
36680         //    Roo.log('fix random scolling');
36681         //    this.scrollTo('top',0); 
36682         //});
36683     }
36684     content = content || this.content;
36685     if(content){
36686         this.setContent(content);
36687     }
36688     if(config && config.url){
36689         this.setUrl(this.url, this.params, this.loadOnce);
36690     }
36691     
36692     
36693     
36694     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36695     
36696     if (this.view && typeof(this.view.xtype) != 'undefined') {
36697         this.view.el = this.el.appendChild(document.createElement("div"));
36698         this.view = Roo.factory(this.view); 
36699         this.view.render  &&  this.view.render(false, '');  
36700     }
36701     
36702     
36703     this.fireEvent('render', this);
36704 };
36705
36706 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36707     
36708     tabTip : '',
36709     
36710     setRegion : function(region){
36711         this.region = region;
36712         this.setActiveClass(region && !this.background);
36713     },
36714     
36715     
36716     setActiveClass: function(state)
36717     {
36718         if(state){
36719            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36720            this.el.setStyle('position','relative');
36721         }else{
36722            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36723            this.el.setStyle('position', 'absolute');
36724         } 
36725     },
36726     
36727     /**
36728      * Returns the toolbar for this Panel if one was configured. 
36729      * @return {Roo.Toolbar} 
36730      */
36731     getToolbar : function(){
36732         return this.toolbar;
36733     },
36734     
36735     setActiveState : function(active)
36736     {
36737         this.active = active;
36738         this.setActiveClass(active);
36739         if(!active){
36740             if(this.fireEvent("deactivate", this) === false){
36741                 return false;
36742             }
36743             return true;
36744         }
36745         this.fireEvent("activate", this);
36746         return true;
36747     },
36748     /**
36749      * Updates this panel's element
36750      * @param {String} content The new content
36751      * @param {Boolean} loadScripts (optional) true to look for and process scripts
36752     */
36753     setContent : function(content, loadScripts){
36754         this.el.update(content, loadScripts);
36755     },
36756
36757     ignoreResize : function(w, h){
36758         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36759             return true;
36760         }else{
36761             this.lastSize = {width: w, height: h};
36762             return false;
36763         }
36764     },
36765     /**
36766      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36767      * @return {Roo.UpdateManager} The UpdateManager
36768      */
36769     getUpdateManager : function(){
36770         return this.el.getUpdateManager();
36771     },
36772      /**
36773      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36774      * @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:
36775 <pre><code>
36776 panel.load({
36777     url: "your-url.php",
36778     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36779     callback: yourFunction,
36780     scope: yourObject, //(optional scope)
36781     discardUrl: false,
36782     nocache: false,
36783     text: "Loading...",
36784     timeout: 30,
36785     scripts: false
36786 });
36787 </code></pre>
36788      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
36789      * 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.
36790      * @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}
36791      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
36792      * @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.
36793      * @return {Roo.ContentPanel} this
36794      */
36795     load : function(){
36796         var um = this.el.getUpdateManager();
36797         um.update.apply(um, arguments);
36798         return this;
36799     },
36800
36801
36802     /**
36803      * 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.
36804      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
36805      * @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)
36806      * @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)
36807      * @return {Roo.UpdateManager} The UpdateManager
36808      */
36809     setUrl : function(url, params, loadOnce){
36810         if(this.refreshDelegate){
36811             this.removeListener("activate", this.refreshDelegate);
36812         }
36813         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36814         this.on("activate", this.refreshDelegate);
36815         return this.el.getUpdateManager();
36816     },
36817     
36818     _handleRefresh : function(url, params, loadOnce){
36819         if(!loadOnce || !this.loaded){
36820             var updater = this.el.getUpdateManager();
36821             updater.update(url, params, this._setLoaded.createDelegate(this));
36822         }
36823     },
36824     
36825     _setLoaded : function(){
36826         this.loaded = true;
36827     }, 
36828     
36829     /**
36830      * Returns this panel's id
36831      * @return {String} 
36832      */
36833     getId : function(){
36834         return this.el.id;
36835     },
36836     
36837     /** 
36838      * Returns this panel's element - used by regiosn to add.
36839      * @return {Roo.Element} 
36840      */
36841     getEl : function(){
36842         return this.wrapEl || this.el;
36843     },
36844     
36845    
36846     
36847     adjustForComponents : function(width, height)
36848     {
36849         //Roo.log('adjustForComponents ');
36850         if(this.resizeEl != this.el){
36851             width -= this.el.getFrameWidth('lr');
36852             height -= this.el.getFrameWidth('tb');
36853         }
36854         if(this.toolbar){
36855             var te = this.toolbar.getEl();
36856             te.setWidth(width);
36857             height -= te.getHeight();
36858         }
36859         if(this.footer){
36860             var te = this.footer.getEl();
36861             te.setWidth(width);
36862             height -= te.getHeight();
36863         }
36864         
36865         
36866         if(this.adjustments){
36867             width += this.adjustments[0];
36868             height += this.adjustments[1];
36869         }
36870         return {"width": width, "height": height};
36871     },
36872     
36873     setSize : function(width, height){
36874         if(this.fitToFrame && !this.ignoreResize(width, height)){
36875             if(this.fitContainer && this.resizeEl != this.el){
36876                 this.el.setSize(width, height);
36877             }
36878             var size = this.adjustForComponents(width, height);
36879             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
36880             this.fireEvent('resize', this, size.width, size.height);
36881         }
36882     },
36883     
36884     /**
36885      * Returns this panel's title
36886      * @return {String} 
36887      */
36888     getTitle : function(){
36889         
36890         if (typeof(this.title) != 'object') {
36891             return this.title;
36892         }
36893         
36894         var t = '';
36895         for (var k in this.title) {
36896             if (!this.title.hasOwnProperty(k)) {
36897                 continue;
36898             }
36899             
36900             if (k.indexOf('-') >= 0) {
36901                 var s = k.split('-');
36902                 for (var i = 0; i<s.length; i++) {
36903                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
36904                 }
36905             } else {
36906                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
36907             }
36908         }
36909         return t;
36910     },
36911     
36912     /**
36913      * Set this panel's title
36914      * @param {String} title
36915      */
36916     setTitle : function(title){
36917         this.title = title;
36918         if(this.region){
36919             this.region.updatePanelTitle(this, title);
36920         }
36921     },
36922     
36923     /**
36924      * Returns true is this panel was configured to be closable
36925      * @return {Boolean} 
36926      */
36927     isClosable : function(){
36928         return this.closable;
36929     },
36930     
36931     beforeSlide : function(){
36932         this.el.clip();
36933         this.resizeEl.clip();
36934     },
36935     
36936     afterSlide : function(){
36937         this.el.unclip();
36938         this.resizeEl.unclip();
36939     },
36940     
36941     /**
36942      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
36943      *   Will fail silently if the {@link #setUrl} method has not been called.
36944      *   This does not activate the panel, just updates its content.
36945      */
36946     refresh : function(){
36947         if(this.refreshDelegate){
36948            this.loaded = false;
36949            this.refreshDelegate();
36950         }
36951     },
36952     
36953     /**
36954      * Destroys this panel
36955      */
36956     destroy : function(){
36957         this.el.removeAllListeners();
36958         var tempEl = document.createElement("span");
36959         tempEl.appendChild(this.el.dom);
36960         tempEl.innerHTML = "";
36961         this.el.remove();
36962         this.el = null;
36963     },
36964     
36965     /**
36966      * form - if the content panel contains a form - this is a reference to it.
36967      * @type {Roo.form.Form}
36968      */
36969     form : false,
36970     /**
36971      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
36972      *    This contains a reference to it.
36973      * @type {Roo.View}
36974      */
36975     view : false,
36976     
36977       /**
36978      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
36979      * <pre><code>
36980
36981 layout.addxtype({
36982        xtype : 'Form',
36983        items: [ .... ]
36984    }
36985 );
36986
36987 </code></pre>
36988      * @param {Object} cfg Xtype definition of item to add.
36989      */
36990     
36991     
36992     getChildContainer: function () {
36993         return this.getEl();
36994     }
36995     
36996     
36997     /*
36998         var  ret = new Roo.factory(cfg);
36999         return ret;
37000         
37001         
37002         // add form..
37003         if (cfg.xtype.match(/^Form$/)) {
37004             
37005             var el;
37006             //if (this.footer) {
37007             //    el = this.footer.container.insertSibling(false, 'before');
37008             //} else {
37009                 el = this.el.createChild();
37010             //}
37011
37012             this.form = new  Roo.form.Form(cfg);
37013             
37014             
37015             if ( this.form.allItems.length) {
37016                 this.form.render(el.dom);
37017             }
37018             return this.form;
37019         }
37020         // should only have one of theses..
37021         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37022             // views.. should not be just added - used named prop 'view''
37023             
37024             cfg.el = this.el.appendChild(document.createElement("div"));
37025             // factory?
37026             
37027             var ret = new Roo.factory(cfg);
37028              
37029              ret.render && ret.render(false, ''); // render blank..
37030             this.view = ret;
37031             return ret;
37032         }
37033         return false;
37034     }
37035     \*/
37036 });
37037  
37038 /**
37039  * @class Roo.bootstrap.panel.Grid
37040  * @extends Roo.bootstrap.panel.Content
37041  * @constructor
37042  * Create a new GridPanel.
37043  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37044  * @param {Object} config A the config object
37045   
37046  */
37047
37048
37049
37050 Roo.bootstrap.panel.Grid = function(config)
37051 {
37052     
37053       
37054     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37055         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37056
37057     config.el = this.wrapper;
37058     //this.el = this.wrapper;
37059     
37060       if (config.container) {
37061         // ctor'ed from a Border/panel.grid
37062         
37063         
37064         this.wrapper.setStyle("overflow", "hidden");
37065         this.wrapper.addClass('roo-grid-container');
37066
37067     }
37068     
37069     
37070     if(config.toolbar){
37071         var tool_el = this.wrapper.createChild();    
37072         this.toolbar = Roo.factory(config.toolbar);
37073         var ti = [];
37074         if (config.toolbar.items) {
37075             ti = config.toolbar.items ;
37076             delete config.toolbar.items ;
37077         }
37078         
37079         var nitems = [];
37080         this.toolbar.render(tool_el);
37081         for(var i =0;i < ti.length;i++) {
37082           //  Roo.log(['add child', items[i]]);
37083             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37084         }
37085         this.toolbar.items = nitems;
37086         
37087         delete config.toolbar;
37088     }
37089     
37090     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37091     config.grid.scrollBody = true;;
37092     config.grid.monitorWindowResize = false; // turn off autosizing
37093     config.grid.autoHeight = false;
37094     config.grid.autoWidth = false;
37095     
37096     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37097     
37098     if (config.background) {
37099         // render grid on panel activation (if panel background)
37100         this.on('activate', function(gp) {
37101             if (!gp.grid.rendered) {
37102                 gp.grid.render(this.wrapper);
37103                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37104             }
37105         });
37106             
37107     } else {
37108         this.grid.render(this.wrapper);
37109         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37110
37111     }
37112     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37113     // ??? needed ??? config.el = this.wrapper;
37114     
37115     
37116     
37117   
37118     // xtype created footer. - not sure if will work as we normally have to render first..
37119     if (this.footer && !this.footer.el && this.footer.xtype) {
37120         
37121         var ctr = this.grid.getView().getFooterPanel(true);
37122         this.footer.dataSource = this.grid.dataSource;
37123         this.footer = Roo.factory(this.footer, Roo);
37124         this.footer.render(ctr);
37125         
37126     }
37127     
37128     
37129     
37130     
37131      
37132 };
37133
37134 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37135     getId : function(){
37136         return this.grid.id;
37137     },
37138     
37139     /**
37140      * Returns the grid for this panel
37141      * @return {Roo.bootstrap.Table} 
37142      */
37143     getGrid : function(){
37144         return this.grid;    
37145     },
37146     
37147     setSize : function(width, height){
37148         if(!this.ignoreResize(width, height)){
37149             var grid = this.grid;
37150             var size = this.adjustForComponents(width, height);
37151             var gridel = grid.getGridEl();
37152             gridel.setSize(size.width, size.height);
37153             /*
37154             var thd = grid.getGridEl().select('thead',true).first();
37155             var tbd = grid.getGridEl().select('tbody', true).first();
37156             if (tbd) {
37157                 tbd.setSize(width, height - thd.getHeight());
37158             }
37159             */
37160             grid.autoSize();
37161         }
37162     },
37163      
37164     
37165     
37166     beforeSlide : function(){
37167         this.grid.getView().scroller.clip();
37168     },
37169     
37170     afterSlide : function(){
37171         this.grid.getView().scroller.unclip();
37172     },
37173     
37174     destroy : function(){
37175         this.grid.destroy();
37176         delete this.grid;
37177         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37178     }
37179 });
37180
37181 /**
37182  * @class Roo.bootstrap.panel.Nest
37183  * @extends Roo.bootstrap.panel.Content
37184  * @constructor
37185  * Create a new Panel, that can contain a layout.Border.
37186  * 
37187  * 
37188  * @param {Roo.BorderLayout} layout The layout for this panel
37189  * @param {String/Object} config A string to set only the title or a config object
37190  */
37191 Roo.bootstrap.panel.Nest = function(config)
37192 {
37193     // construct with only one argument..
37194     /* FIXME - implement nicer consturctors
37195     if (layout.layout) {
37196         config = layout;
37197         layout = config.layout;
37198         delete config.layout;
37199     }
37200     if (layout.xtype && !layout.getEl) {
37201         // then layout needs constructing..
37202         layout = Roo.factory(layout, Roo);
37203     }
37204     */
37205     
37206     config.el =  config.layout.getEl();
37207     
37208     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37209     
37210     config.layout.monitorWindowResize = false; // turn off autosizing
37211     this.layout = config.layout;
37212     this.layout.getEl().addClass("roo-layout-nested-layout");
37213     
37214     
37215     
37216     
37217 };
37218
37219 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37220
37221     setSize : function(width, height){
37222         if(!this.ignoreResize(width, height)){
37223             var size = this.adjustForComponents(width, height);
37224             var el = this.layout.getEl();
37225             if (size.height < 1) {
37226                 el.setWidth(size.width);   
37227             } else {
37228                 el.setSize(size.width, size.height);
37229             }
37230             var touch = el.dom.offsetWidth;
37231             this.layout.layout();
37232             // ie requires a double layout on the first pass
37233             if(Roo.isIE && !this.initialized){
37234                 this.initialized = true;
37235                 this.layout.layout();
37236             }
37237         }
37238     },
37239     
37240     // activate all subpanels if not currently active..
37241     
37242     setActiveState : function(active){
37243         this.active = active;
37244         this.setActiveClass(active);
37245         
37246         if(!active){
37247             this.fireEvent("deactivate", this);
37248             return;
37249         }
37250         
37251         this.fireEvent("activate", this);
37252         // not sure if this should happen before or after..
37253         if (!this.layout) {
37254             return; // should not happen..
37255         }
37256         var reg = false;
37257         for (var r in this.layout.regions) {
37258             reg = this.layout.getRegion(r);
37259             if (reg.getActivePanel()) {
37260                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37261                 reg.setActivePanel(reg.getActivePanel());
37262                 continue;
37263             }
37264             if (!reg.panels.length) {
37265                 continue;
37266             }
37267             reg.showPanel(reg.getPanel(0));
37268         }
37269         
37270         
37271         
37272         
37273     },
37274     
37275     /**
37276      * Returns the nested BorderLayout for this panel
37277      * @return {Roo.BorderLayout} 
37278      */
37279     getLayout : function(){
37280         return this.layout;
37281     },
37282     
37283      /**
37284      * Adds a xtype elements to the layout of the nested panel
37285      * <pre><code>
37286
37287 panel.addxtype({
37288        xtype : 'ContentPanel',
37289        region: 'west',
37290        items: [ .... ]
37291    }
37292 );
37293
37294 panel.addxtype({
37295         xtype : 'NestedLayoutPanel',
37296         region: 'west',
37297         layout: {
37298            center: { },
37299            west: { }   
37300         },
37301         items : [ ... list of content panels or nested layout panels.. ]
37302    }
37303 );
37304 </code></pre>
37305      * @param {Object} cfg Xtype definition of item to add.
37306      */
37307     addxtype : function(cfg) {
37308         return this.layout.addxtype(cfg);
37309     
37310     }
37311 });        /*
37312  * Based on:
37313  * Ext JS Library 1.1.1
37314  * Copyright(c) 2006-2007, Ext JS, LLC.
37315  *
37316  * Originally Released Under LGPL - original licence link has changed is not relivant.
37317  *
37318  * Fork - LGPL
37319  * <script type="text/javascript">
37320  */
37321 /**
37322  * @class Roo.TabPanel
37323  * @extends Roo.util.Observable
37324  * A lightweight tab container.
37325  * <br><br>
37326  * Usage:
37327  * <pre><code>
37328 // basic tabs 1, built from existing content
37329 var tabs = new Roo.TabPanel("tabs1");
37330 tabs.addTab("script", "View Script");
37331 tabs.addTab("markup", "View Markup");
37332 tabs.activate("script");
37333
37334 // more advanced tabs, built from javascript
37335 var jtabs = new Roo.TabPanel("jtabs");
37336 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37337
37338 // set up the UpdateManager
37339 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37340 var updater = tab2.getUpdateManager();
37341 updater.setDefaultUrl("ajax1.htm");
37342 tab2.on('activate', updater.refresh, updater, true);
37343
37344 // Use setUrl for Ajax loading
37345 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37346 tab3.setUrl("ajax2.htm", null, true);
37347
37348 // Disabled tab
37349 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37350 tab4.disable();
37351
37352 jtabs.activate("jtabs-1");
37353  * </code></pre>
37354  * @constructor
37355  * Create a new TabPanel.
37356  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37357  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37358  */
37359 Roo.bootstrap.panel.Tabs = function(config){
37360     /**
37361     * The container element for this TabPanel.
37362     * @type Roo.Element
37363     */
37364     this.el = Roo.get(config.el);
37365     delete config.el;
37366     if(config){
37367         if(typeof config == "boolean"){
37368             this.tabPosition = config ? "bottom" : "top";
37369         }else{
37370             Roo.apply(this, config);
37371         }
37372     }
37373     
37374     if(this.tabPosition == "bottom"){
37375         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37376         this.el.addClass("roo-tabs-bottom");
37377     }
37378     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37379     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37380     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37381     if(Roo.isIE){
37382         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37383     }
37384     if(this.tabPosition != "bottom"){
37385         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37386          * @type Roo.Element
37387          */
37388         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37389         this.el.addClass("roo-tabs-top");
37390     }
37391     this.items = [];
37392
37393     this.bodyEl.setStyle("position", "relative");
37394
37395     this.active = null;
37396     this.activateDelegate = this.activate.createDelegate(this);
37397
37398     this.addEvents({
37399         /**
37400          * @event tabchange
37401          * Fires when the active tab changes
37402          * @param {Roo.TabPanel} this
37403          * @param {Roo.TabPanelItem} activePanel The new active tab
37404          */
37405         "tabchange": true,
37406         /**
37407          * @event beforetabchange
37408          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37409          * @param {Roo.TabPanel} this
37410          * @param {Object} e Set cancel to true on this object to cancel the tab change
37411          * @param {Roo.TabPanelItem} tab The tab being changed to
37412          */
37413         "beforetabchange" : true
37414     });
37415
37416     Roo.EventManager.onWindowResize(this.onResize, this);
37417     this.cpad = this.el.getPadding("lr");
37418     this.hiddenCount = 0;
37419
37420
37421     // toolbar on the tabbar support...
37422     if (this.toolbar) {
37423         alert("no toolbar support yet");
37424         this.toolbar  = false;
37425         /*
37426         var tcfg = this.toolbar;
37427         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37428         this.toolbar = new Roo.Toolbar(tcfg);
37429         if (Roo.isSafari) {
37430             var tbl = tcfg.container.child('table', true);
37431             tbl.setAttribute('width', '100%');
37432         }
37433         */
37434         
37435     }
37436    
37437
37438
37439     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37440 };
37441
37442 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37443     /*
37444      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37445      */
37446     tabPosition : "top",
37447     /*
37448      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37449      */
37450     currentTabWidth : 0,
37451     /*
37452      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37453      */
37454     minTabWidth : 40,
37455     /*
37456      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37457      */
37458     maxTabWidth : 250,
37459     /*
37460      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37461      */
37462     preferredTabWidth : 175,
37463     /*
37464      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37465      */
37466     resizeTabs : false,
37467     /*
37468      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37469      */
37470     monitorResize : true,
37471     /*
37472      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37473      */
37474     toolbar : false,
37475
37476     /**
37477      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37478      * @param {String} id The id of the div to use <b>or create</b>
37479      * @param {String} text The text for the tab
37480      * @param {String} content (optional) Content to put in the TabPanelItem body
37481      * @param {Boolean} closable (optional) True to create a close icon on the tab
37482      * @return {Roo.TabPanelItem} The created TabPanelItem
37483      */
37484     addTab : function(id, text, content, closable, tpl)
37485     {
37486         var item = new Roo.bootstrap.panel.TabItem({
37487             panel: this,
37488             id : id,
37489             text : text,
37490             closable : closable,
37491             tpl : tpl
37492         });
37493         this.addTabItem(item);
37494         if(content){
37495             item.setContent(content);
37496         }
37497         return item;
37498     },
37499
37500     /**
37501      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37502      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37503      * @return {Roo.TabPanelItem}
37504      */
37505     getTab : function(id){
37506         return this.items[id];
37507     },
37508
37509     /**
37510      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37511      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37512      */
37513     hideTab : function(id){
37514         var t = this.items[id];
37515         if(!t.isHidden()){
37516            t.setHidden(true);
37517            this.hiddenCount++;
37518            this.autoSizeTabs();
37519         }
37520     },
37521
37522     /**
37523      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37524      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37525      */
37526     unhideTab : function(id){
37527         var t = this.items[id];
37528         if(t.isHidden()){
37529            t.setHidden(false);
37530            this.hiddenCount--;
37531            this.autoSizeTabs();
37532         }
37533     },
37534
37535     /**
37536      * Adds an existing {@link Roo.TabPanelItem}.
37537      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37538      */
37539     addTabItem : function(item){
37540         this.items[item.id] = item;
37541         this.items.push(item);
37542       //  if(this.resizeTabs){
37543     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37544   //         this.autoSizeTabs();
37545 //        }else{
37546 //            item.autoSize();
37547        // }
37548     },
37549
37550     /**
37551      * Removes a {@link Roo.TabPanelItem}.
37552      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37553      */
37554     removeTab : function(id){
37555         var items = this.items;
37556         var tab = items[id];
37557         if(!tab) { return; }
37558         var index = items.indexOf(tab);
37559         if(this.active == tab && items.length > 1){
37560             var newTab = this.getNextAvailable(index);
37561             if(newTab) {
37562                 newTab.activate();
37563             }
37564         }
37565         this.stripEl.dom.removeChild(tab.pnode.dom);
37566         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37567             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37568         }
37569         items.splice(index, 1);
37570         delete this.items[tab.id];
37571         tab.fireEvent("close", tab);
37572         tab.purgeListeners();
37573         this.autoSizeTabs();
37574     },
37575
37576     getNextAvailable : function(start){
37577         var items = this.items;
37578         var index = start;
37579         // look for a next tab that will slide over to
37580         // replace the one being removed
37581         while(index < items.length){
37582             var item = items[++index];
37583             if(item && !item.isHidden()){
37584                 return item;
37585             }
37586         }
37587         // if one isn't found select the previous tab (on the left)
37588         index = start;
37589         while(index >= 0){
37590             var item = items[--index];
37591             if(item && !item.isHidden()){
37592                 return item;
37593             }
37594         }
37595         return null;
37596     },
37597
37598     /**
37599      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37600      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37601      */
37602     disableTab : function(id){
37603         var tab = this.items[id];
37604         if(tab && this.active != tab){
37605             tab.disable();
37606         }
37607     },
37608
37609     /**
37610      * Enables a {@link Roo.TabPanelItem} that is disabled.
37611      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37612      */
37613     enableTab : function(id){
37614         var tab = this.items[id];
37615         tab.enable();
37616     },
37617
37618     /**
37619      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37620      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37621      * @return {Roo.TabPanelItem} The TabPanelItem.
37622      */
37623     activate : function(id){
37624         var tab = this.items[id];
37625         if(!tab){
37626             return null;
37627         }
37628         if(tab == this.active || tab.disabled){
37629             return tab;
37630         }
37631         var e = {};
37632         this.fireEvent("beforetabchange", this, e, tab);
37633         if(e.cancel !== true && !tab.disabled){
37634             if(this.active){
37635                 this.active.hide();
37636             }
37637             this.active = this.items[id];
37638             this.active.show();
37639             this.fireEvent("tabchange", this, this.active);
37640         }
37641         return tab;
37642     },
37643
37644     /**
37645      * Gets the active {@link Roo.TabPanelItem}.
37646      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37647      */
37648     getActiveTab : function(){
37649         return this.active;
37650     },
37651
37652     /**
37653      * Updates the tab body element to fit the height of the container element
37654      * for overflow scrolling
37655      * @param {Number} targetHeight (optional) Override the starting height from the elements height
37656      */
37657     syncHeight : function(targetHeight){
37658         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37659         var bm = this.bodyEl.getMargins();
37660         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37661         this.bodyEl.setHeight(newHeight);
37662         return newHeight;
37663     },
37664
37665     onResize : function(){
37666         if(this.monitorResize){
37667             this.autoSizeTabs();
37668         }
37669     },
37670
37671     /**
37672      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37673      */
37674     beginUpdate : function(){
37675         this.updating = true;
37676     },
37677
37678     /**
37679      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37680      */
37681     endUpdate : function(){
37682         this.updating = false;
37683         this.autoSizeTabs();
37684     },
37685
37686     /**
37687      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37688      */
37689     autoSizeTabs : function(){
37690         var count = this.items.length;
37691         var vcount = count - this.hiddenCount;
37692         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37693             return;
37694         }
37695         var w = Math.max(this.el.getWidth() - this.cpad, 10);
37696         var availWidth = Math.floor(w / vcount);
37697         var b = this.stripBody;
37698         if(b.getWidth() > w){
37699             var tabs = this.items;
37700             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37701             if(availWidth < this.minTabWidth){
37702                 /*if(!this.sleft){    // incomplete scrolling code
37703                     this.createScrollButtons();
37704                 }
37705                 this.showScroll();
37706                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37707             }
37708         }else{
37709             if(this.currentTabWidth < this.preferredTabWidth){
37710                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37711             }
37712         }
37713     },
37714
37715     /**
37716      * Returns the number of tabs in this TabPanel.
37717      * @return {Number}
37718      */
37719      getCount : function(){
37720          return this.items.length;
37721      },
37722
37723     /**
37724      * Resizes all the tabs to the passed width
37725      * @param {Number} The new width
37726      */
37727     setTabWidth : function(width){
37728         this.currentTabWidth = width;
37729         for(var i = 0, len = this.items.length; i < len; i++) {
37730                 if(!this.items[i].isHidden()) {
37731                 this.items[i].setWidth(width);
37732             }
37733         }
37734     },
37735
37736     /**
37737      * Destroys this TabPanel
37738      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37739      */
37740     destroy : function(removeEl){
37741         Roo.EventManager.removeResizeListener(this.onResize, this);
37742         for(var i = 0, len = this.items.length; i < len; i++){
37743             this.items[i].purgeListeners();
37744         }
37745         if(removeEl === true){
37746             this.el.update("");
37747             this.el.remove();
37748         }
37749     },
37750     
37751     createStrip : function(container)
37752     {
37753         var strip = document.createElement("nav");
37754         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37755         container.appendChild(strip);
37756         return strip;
37757     },
37758     
37759     createStripList : function(strip)
37760     {
37761         // div wrapper for retard IE
37762         // returns the "tr" element.
37763         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
37764         //'<div class="x-tabs-strip-wrap">'+
37765           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
37766           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
37767         return strip.firstChild; //.firstChild.firstChild.firstChild;
37768     },
37769     createBody : function(container)
37770     {
37771         var body = document.createElement("div");
37772         Roo.id(body, "tab-body");
37773         //Roo.fly(body).addClass("x-tabs-body");
37774         Roo.fly(body).addClass("tab-content");
37775         container.appendChild(body);
37776         return body;
37777     },
37778     createItemBody :function(bodyEl, id){
37779         var body = Roo.getDom(id);
37780         if(!body){
37781             body = document.createElement("div");
37782             body.id = id;
37783         }
37784         //Roo.fly(body).addClass("x-tabs-item-body");
37785         Roo.fly(body).addClass("tab-pane");
37786          bodyEl.insertBefore(body, bodyEl.firstChild);
37787         return body;
37788     },
37789     /** @private */
37790     createStripElements :  function(stripEl, text, closable, tpl)
37791     {
37792         var td = document.createElement("li"); // was td..
37793         
37794         
37795         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
37796         
37797         
37798         stripEl.appendChild(td);
37799         /*if(closable){
37800             td.className = "x-tabs-closable";
37801             if(!this.closeTpl){
37802                 this.closeTpl = new Roo.Template(
37803                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37804                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
37805                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
37806                 );
37807             }
37808             var el = this.closeTpl.overwrite(td, {"text": text});
37809             var close = el.getElementsByTagName("div")[0];
37810             var inner = el.getElementsByTagName("em")[0];
37811             return {"el": el, "close": close, "inner": inner};
37812         } else {
37813         */
37814         // not sure what this is..
37815 //            if(!this.tabTpl){
37816                 //this.tabTpl = new Roo.Template(
37817                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37818                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
37819                 //);
37820 //                this.tabTpl = new Roo.Template(
37821 //                   '<a href="#">' +
37822 //                   '<span unselectable="on"' +
37823 //                            (this.disableTooltips ? '' : ' title="{text}"') +
37824 //                            ' >{text}</span></a>'
37825 //                );
37826 //                
37827 //            }
37828
37829
37830             var template = tpl || this.tabTpl || false;
37831             
37832             if(!template){
37833                 
37834                 template = new Roo.Template(
37835                    '<a href="#">' +
37836                    '<span unselectable="on"' +
37837                             (this.disableTooltips ? '' : ' title="{text}"') +
37838                             ' >{text}</span></a>'
37839                 );
37840             }
37841             
37842             switch (typeof(template)) {
37843                 case 'object' :
37844                     break;
37845                 case 'string' :
37846                     template = new Roo.Template(template);
37847                     break;
37848                 default :
37849                     break;
37850             }
37851             
37852             var el = template.overwrite(td, {"text": text});
37853             
37854             var inner = el.getElementsByTagName("span")[0];
37855             
37856             return {"el": el, "inner": inner};
37857             
37858     }
37859         
37860     
37861 });
37862
37863 /**
37864  * @class Roo.TabPanelItem
37865  * @extends Roo.util.Observable
37866  * Represents an individual item (tab plus body) in a TabPanel.
37867  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
37868  * @param {String} id The id of this TabPanelItem
37869  * @param {String} text The text for the tab of this TabPanelItem
37870  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
37871  */
37872 Roo.bootstrap.panel.TabItem = function(config){
37873     /**
37874      * The {@link Roo.TabPanel} this TabPanelItem belongs to
37875      * @type Roo.TabPanel
37876      */
37877     this.tabPanel = config.panel;
37878     /**
37879      * The id for this TabPanelItem
37880      * @type String
37881      */
37882     this.id = config.id;
37883     /** @private */
37884     this.disabled = false;
37885     /** @private */
37886     this.text = config.text;
37887     /** @private */
37888     this.loaded = false;
37889     this.closable = config.closable;
37890
37891     /**
37892      * The body element for this TabPanelItem.
37893      * @type Roo.Element
37894      */
37895     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
37896     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
37897     this.bodyEl.setStyle("display", "block");
37898     this.bodyEl.setStyle("zoom", "1");
37899     //this.hideAction();
37900
37901     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
37902     /** @private */
37903     this.el = Roo.get(els.el);
37904     this.inner = Roo.get(els.inner, true);
37905     this.textEl = Roo.get(this.el.dom.firstChild, true);
37906     this.pnode = Roo.get(els.el.parentNode, true);
37907 //    this.el.on("mousedown", this.onTabMouseDown, this);
37908     this.el.on("click", this.onTabClick, this);
37909     /** @private */
37910     if(config.closable){
37911         var c = Roo.get(els.close, true);
37912         c.dom.title = this.closeText;
37913         c.addClassOnOver("close-over");
37914         c.on("click", this.closeClick, this);
37915      }
37916
37917     this.addEvents({
37918          /**
37919          * @event activate
37920          * Fires when this tab becomes the active tab.
37921          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37922          * @param {Roo.TabPanelItem} this
37923          */
37924         "activate": true,
37925         /**
37926          * @event beforeclose
37927          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
37928          * @param {Roo.TabPanelItem} this
37929          * @param {Object} e Set cancel to true on this object to cancel the close.
37930          */
37931         "beforeclose": true,
37932         /**
37933          * @event close
37934          * Fires when this tab is closed.
37935          * @param {Roo.TabPanelItem} this
37936          */
37937          "close": true,
37938         /**
37939          * @event deactivate
37940          * Fires when this tab is no longer the active tab.
37941          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37942          * @param {Roo.TabPanelItem} this
37943          */
37944          "deactivate" : true
37945     });
37946     this.hidden = false;
37947
37948     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
37949 };
37950
37951 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
37952            {
37953     purgeListeners : function(){
37954        Roo.util.Observable.prototype.purgeListeners.call(this);
37955        this.el.removeAllListeners();
37956     },
37957     /**
37958      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
37959      */
37960     show : function(){
37961         this.pnode.addClass("active");
37962         this.showAction();
37963         if(Roo.isOpera){
37964             this.tabPanel.stripWrap.repaint();
37965         }
37966         this.fireEvent("activate", this.tabPanel, this);
37967     },
37968
37969     /**
37970      * Returns true if this tab is the active tab.
37971      * @return {Boolean}
37972      */
37973     isActive : function(){
37974         return this.tabPanel.getActiveTab() == this;
37975     },
37976
37977     /**
37978      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
37979      */
37980     hide : function(){
37981         this.pnode.removeClass("active");
37982         this.hideAction();
37983         this.fireEvent("deactivate", this.tabPanel, this);
37984     },
37985
37986     hideAction : function(){
37987         this.bodyEl.hide();
37988         this.bodyEl.setStyle("position", "absolute");
37989         this.bodyEl.setLeft("-20000px");
37990         this.bodyEl.setTop("-20000px");
37991     },
37992
37993     showAction : function(){
37994         this.bodyEl.setStyle("position", "relative");
37995         this.bodyEl.setTop("");
37996         this.bodyEl.setLeft("");
37997         this.bodyEl.show();
37998     },
37999
38000     /**
38001      * Set the tooltip for the tab.
38002      * @param {String} tooltip The tab's tooltip
38003      */
38004     setTooltip : function(text){
38005         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38006             this.textEl.dom.qtip = text;
38007             this.textEl.dom.removeAttribute('title');
38008         }else{
38009             this.textEl.dom.title = text;
38010         }
38011     },
38012
38013     onTabClick : function(e){
38014         e.preventDefault();
38015         this.tabPanel.activate(this.id);
38016     },
38017
38018     onTabMouseDown : function(e){
38019         e.preventDefault();
38020         this.tabPanel.activate(this.id);
38021     },
38022 /*
38023     getWidth : function(){
38024         return this.inner.getWidth();
38025     },
38026
38027     setWidth : function(width){
38028         var iwidth = width - this.pnode.getPadding("lr");
38029         this.inner.setWidth(iwidth);
38030         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38031         this.pnode.setWidth(width);
38032     },
38033 */
38034     /**
38035      * Show or hide the tab
38036      * @param {Boolean} hidden True to hide or false to show.
38037      */
38038     setHidden : function(hidden){
38039         this.hidden = hidden;
38040         this.pnode.setStyle("display", hidden ? "none" : "");
38041     },
38042
38043     /**
38044      * Returns true if this tab is "hidden"
38045      * @return {Boolean}
38046      */
38047     isHidden : function(){
38048         return this.hidden;
38049     },
38050
38051     /**
38052      * Returns the text for this tab
38053      * @return {String}
38054      */
38055     getText : function(){
38056         return this.text;
38057     },
38058     /*
38059     autoSize : function(){
38060         //this.el.beginMeasure();
38061         this.textEl.setWidth(1);
38062         /*
38063          *  #2804 [new] Tabs in Roojs
38064          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38065          */
38066         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38067         //this.el.endMeasure();
38068     //},
38069
38070     /**
38071      * Sets the text for the tab (Note: this also sets the tooltip text)
38072      * @param {String} text The tab's text and tooltip
38073      */
38074     setText : function(text){
38075         this.text = text;
38076         this.textEl.update(text);
38077         this.setTooltip(text);
38078         //if(!this.tabPanel.resizeTabs){
38079         //    this.autoSize();
38080         //}
38081     },
38082     /**
38083      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38084      */
38085     activate : function(){
38086         this.tabPanel.activate(this.id);
38087     },
38088
38089     /**
38090      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38091      */
38092     disable : function(){
38093         if(this.tabPanel.active != this){
38094             this.disabled = true;
38095             this.pnode.addClass("disabled");
38096         }
38097     },
38098
38099     /**
38100      * Enables this TabPanelItem if it was previously disabled.
38101      */
38102     enable : function(){
38103         this.disabled = false;
38104         this.pnode.removeClass("disabled");
38105     },
38106
38107     /**
38108      * Sets the content for this TabPanelItem.
38109      * @param {String} content The content
38110      * @param {Boolean} loadScripts true to look for and load scripts
38111      */
38112     setContent : function(content, loadScripts){
38113         this.bodyEl.update(content, loadScripts);
38114     },
38115
38116     /**
38117      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38118      * @return {Roo.UpdateManager} The UpdateManager
38119      */
38120     getUpdateManager : function(){
38121         return this.bodyEl.getUpdateManager();
38122     },
38123
38124     /**
38125      * Set a URL to be used to load the content for this TabPanelItem.
38126      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38127      * @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)
38128      * @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)
38129      * @return {Roo.UpdateManager} The UpdateManager
38130      */
38131     setUrl : function(url, params, loadOnce){
38132         if(this.refreshDelegate){
38133             this.un('activate', this.refreshDelegate);
38134         }
38135         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38136         this.on("activate", this.refreshDelegate);
38137         return this.bodyEl.getUpdateManager();
38138     },
38139
38140     /** @private */
38141     _handleRefresh : function(url, params, loadOnce){
38142         if(!loadOnce || !this.loaded){
38143             var updater = this.bodyEl.getUpdateManager();
38144             updater.update(url, params, this._setLoaded.createDelegate(this));
38145         }
38146     },
38147
38148     /**
38149      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38150      *   Will fail silently if the setUrl method has not been called.
38151      *   This does not activate the panel, just updates its content.
38152      */
38153     refresh : function(){
38154         if(this.refreshDelegate){
38155            this.loaded = false;
38156            this.refreshDelegate();
38157         }
38158     },
38159
38160     /** @private */
38161     _setLoaded : function(){
38162         this.loaded = true;
38163     },
38164
38165     /** @private */
38166     closeClick : function(e){
38167         var o = {};
38168         e.stopEvent();
38169         this.fireEvent("beforeclose", this, o);
38170         if(o.cancel !== true){
38171             this.tabPanel.removeTab(this.id);
38172         }
38173     },
38174     /**
38175      * The text displayed in the tooltip for the close icon.
38176      * @type String
38177      */
38178     closeText : "Close this tab"
38179 });
38180 /**
38181 *    This script refer to:
38182 *    Title: International Telephone Input
38183 *    Author: Jack O'Connor
38184 *    Code version:  v12.1.12
38185 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38186 **/
38187
38188 Roo.bootstrap.PhoneInputData = function() {
38189     var d = [
38190       [
38191         "Afghanistan (‫افغانستان‬‎)",
38192         "af",
38193         "93"
38194       ],
38195       [
38196         "Albania (Shqipëri)",
38197         "al",
38198         "355"
38199       ],
38200       [
38201         "Algeria (‫الجزائر‬‎)",
38202         "dz",
38203         "213"
38204       ],
38205       [
38206         "American Samoa",
38207         "as",
38208         "1684"
38209       ],
38210       [
38211         "Andorra",
38212         "ad",
38213         "376"
38214       ],
38215       [
38216         "Angola",
38217         "ao",
38218         "244"
38219       ],
38220       [
38221         "Anguilla",
38222         "ai",
38223         "1264"
38224       ],
38225       [
38226         "Antigua and Barbuda",
38227         "ag",
38228         "1268"
38229       ],
38230       [
38231         "Argentina",
38232         "ar",
38233         "54"
38234       ],
38235       [
38236         "Armenia (Հայաստան)",
38237         "am",
38238         "374"
38239       ],
38240       [
38241         "Aruba",
38242         "aw",
38243         "297"
38244       ],
38245       [
38246         "Australia",
38247         "au",
38248         "61",
38249         0
38250       ],
38251       [
38252         "Austria (Österreich)",
38253         "at",
38254         "43"
38255       ],
38256       [
38257         "Azerbaijan (Azərbaycan)",
38258         "az",
38259         "994"
38260       ],
38261       [
38262         "Bahamas",
38263         "bs",
38264         "1242"
38265       ],
38266       [
38267         "Bahrain (‫البحرين‬‎)",
38268         "bh",
38269         "973"
38270       ],
38271       [
38272         "Bangladesh (বাংলাদেশ)",
38273         "bd",
38274         "880"
38275       ],
38276       [
38277         "Barbados",
38278         "bb",
38279         "1246"
38280       ],
38281       [
38282         "Belarus (Беларусь)",
38283         "by",
38284         "375"
38285       ],
38286       [
38287         "Belgium (België)",
38288         "be",
38289         "32"
38290       ],
38291       [
38292         "Belize",
38293         "bz",
38294         "501"
38295       ],
38296       [
38297         "Benin (Bénin)",
38298         "bj",
38299         "229"
38300       ],
38301       [
38302         "Bermuda",
38303         "bm",
38304         "1441"
38305       ],
38306       [
38307         "Bhutan (འབྲུག)",
38308         "bt",
38309         "975"
38310       ],
38311       [
38312         "Bolivia",
38313         "bo",
38314         "591"
38315       ],
38316       [
38317         "Bosnia and Herzegovina (Босна и Херцеговина)",
38318         "ba",
38319         "387"
38320       ],
38321       [
38322         "Botswana",
38323         "bw",
38324         "267"
38325       ],
38326       [
38327         "Brazil (Brasil)",
38328         "br",
38329         "55"
38330       ],
38331       [
38332         "British Indian Ocean Territory",
38333         "io",
38334         "246"
38335       ],
38336       [
38337         "British Virgin Islands",
38338         "vg",
38339         "1284"
38340       ],
38341       [
38342         "Brunei",
38343         "bn",
38344         "673"
38345       ],
38346       [
38347         "Bulgaria (България)",
38348         "bg",
38349         "359"
38350       ],
38351       [
38352         "Burkina Faso",
38353         "bf",
38354         "226"
38355       ],
38356       [
38357         "Burundi (Uburundi)",
38358         "bi",
38359         "257"
38360       ],
38361       [
38362         "Cambodia (កម្ពុជា)",
38363         "kh",
38364         "855"
38365       ],
38366       [
38367         "Cameroon (Cameroun)",
38368         "cm",
38369         "237"
38370       ],
38371       [
38372         "Canada",
38373         "ca",
38374         "1",
38375         1,
38376         ["204", "226", "236", "249", "250", "289", "306", "343", "365", "387", "403", "416", "418", "431", "437", "438", "450", "506", "514", "519", "548", "579", "581", "587", "604", "613", "639", "647", "672", "705", "709", "742", "778", "780", "782", "807", "819", "825", "867", "873", "902", "905"]
38377       ],
38378       [
38379         "Cape Verde (Kabu Verdi)",
38380         "cv",
38381         "238"
38382       ],
38383       [
38384         "Caribbean Netherlands",
38385         "bq",
38386         "599",
38387         1
38388       ],
38389       [
38390         "Cayman Islands",
38391         "ky",
38392         "1345"
38393       ],
38394       [
38395         "Central African Republic (République centrafricaine)",
38396         "cf",
38397         "236"
38398       ],
38399       [
38400         "Chad (Tchad)",
38401         "td",
38402         "235"
38403       ],
38404       [
38405         "Chile",
38406         "cl",
38407         "56"
38408       ],
38409       [
38410         "China (中国)",
38411         "cn",
38412         "86"
38413       ],
38414       [
38415         "Christmas Island",
38416         "cx",
38417         "61",
38418         2
38419       ],
38420       [
38421         "Cocos (Keeling) Islands",
38422         "cc",
38423         "61",
38424         1
38425       ],
38426       [
38427         "Colombia",
38428         "co",
38429         "57"
38430       ],
38431       [
38432         "Comoros (‫جزر القمر‬‎)",
38433         "km",
38434         "269"
38435       ],
38436       [
38437         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38438         "cd",
38439         "243"
38440       ],
38441       [
38442         "Congo (Republic) (Congo-Brazzaville)",
38443         "cg",
38444         "242"
38445       ],
38446       [
38447         "Cook Islands",
38448         "ck",
38449         "682"
38450       ],
38451       [
38452         "Costa Rica",
38453         "cr",
38454         "506"
38455       ],
38456       [
38457         "Côte d’Ivoire",
38458         "ci",
38459         "225"
38460       ],
38461       [
38462         "Croatia (Hrvatska)",
38463         "hr",
38464         "385"
38465       ],
38466       [
38467         "Cuba",
38468         "cu",
38469         "53"
38470       ],
38471       [
38472         "Curaçao",
38473         "cw",
38474         "599",
38475         0
38476       ],
38477       [
38478         "Cyprus (Κύπρος)",
38479         "cy",
38480         "357"
38481       ],
38482       [
38483         "Czech Republic (Česká republika)",
38484         "cz",
38485         "420"
38486       ],
38487       [
38488         "Denmark (Danmark)",
38489         "dk",
38490         "45"
38491       ],
38492       [
38493         "Djibouti",
38494         "dj",
38495         "253"
38496       ],
38497       [
38498         "Dominica",
38499         "dm",
38500         "1767"
38501       ],
38502       [
38503         "Dominican Republic (República Dominicana)",
38504         "do",
38505         "1",
38506         2,
38507         ["809", "829", "849"]
38508       ],
38509       [
38510         "Ecuador",
38511         "ec",
38512         "593"
38513       ],
38514       [
38515         "Egypt (‫مصر‬‎)",
38516         "eg",
38517         "20"
38518       ],
38519       [
38520         "El Salvador",
38521         "sv",
38522         "503"
38523       ],
38524       [
38525         "Equatorial Guinea (Guinea Ecuatorial)",
38526         "gq",
38527         "240"
38528       ],
38529       [
38530         "Eritrea",
38531         "er",
38532         "291"
38533       ],
38534       [
38535         "Estonia (Eesti)",
38536         "ee",
38537         "372"
38538       ],
38539       [
38540         "Ethiopia",
38541         "et",
38542         "251"
38543       ],
38544       [
38545         "Falkland Islands (Islas Malvinas)",
38546         "fk",
38547         "500"
38548       ],
38549       [
38550         "Faroe Islands (Føroyar)",
38551         "fo",
38552         "298"
38553       ],
38554       [
38555         "Fiji",
38556         "fj",
38557         "679"
38558       ],
38559       [
38560         "Finland (Suomi)",
38561         "fi",
38562         "358",
38563         0
38564       ],
38565       [
38566         "France",
38567         "fr",
38568         "33"
38569       ],
38570       [
38571         "French Guiana (Guyane française)",
38572         "gf",
38573         "594"
38574       ],
38575       [
38576         "French Polynesia (Polynésie française)",
38577         "pf",
38578         "689"
38579       ],
38580       [
38581         "Gabon",
38582         "ga",
38583         "241"
38584       ],
38585       [
38586         "Gambia",
38587         "gm",
38588         "220"
38589       ],
38590       [
38591         "Georgia (საქართველო)",
38592         "ge",
38593         "995"
38594       ],
38595       [
38596         "Germany (Deutschland)",
38597         "de",
38598         "49"
38599       ],
38600       [
38601         "Ghana (Gaana)",
38602         "gh",
38603         "233"
38604       ],
38605       [
38606         "Gibraltar",
38607         "gi",
38608         "350"
38609       ],
38610       [
38611         "Greece (Ελλάδα)",
38612         "gr",
38613         "30"
38614       ],
38615       [
38616         "Greenland (Kalaallit Nunaat)",
38617         "gl",
38618         "299"
38619       ],
38620       [
38621         "Grenada",
38622         "gd",
38623         "1473"
38624       ],
38625       [
38626         "Guadeloupe",
38627         "gp",
38628         "590",
38629         0
38630       ],
38631       [
38632         "Guam",
38633         "gu",
38634         "1671"
38635       ],
38636       [
38637         "Guatemala",
38638         "gt",
38639         "502"
38640       ],
38641       [
38642         "Guernsey",
38643         "gg",
38644         "44",
38645         1
38646       ],
38647       [
38648         "Guinea (Guinée)",
38649         "gn",
38650         "224"
38651       ],
38652       [
38653         "Guinea-Bissau (Guiné Bissau)",
38654         "gw",
38655         "245"
38656       ],
38657       [
38658         "Guyana",
38659         "gy",
38660         "592"
38661       ],
38662       [
38663         "Haiti",
38664         "ht",
38665         "509"
38666       ],
38667       [
38668         "Honduras",
38669         "hn",
38670         "504"
38671       ],
38672       [
38673         "Hong Kong (香港)",
38674         "hk",
38675         "852"
38676       ],
38677       [
38678         "Hungary (Magyarország)",
38679         "hu",
38680         "36"
38681       ],
38682       [
38683         "Iceland (Ísland)",
38684         "is",
38685         "354"
38686       ],
38687       [
38688         "India (भारत)",
38689         "in",
38690         "91"
38691       ],
38692       [
38693         "Indonesia",
38694         "id",
38695         "62"
38696       ],
38697       [
38698         "Iran (‫ایران‬‎)",
38699         "ir",
38700         "98"
38701       ],
38702       [
38703         "Iraq (‫العراق‬‎)",
38704         "iq",
38705         "964"
38706       ],
38707       [
38708         "Ireland",
38709         "ie",
38710         "353"
38711       ],
38712       [
38713         "Isle of Man",
38714         "im",
38715         "44",
38716         2
38717       ],
38718       [
38719         "Israel (‫ישראל‬‎)",
38720         "il",
38721         "972"
38722       ],
38723       [
38724         "Italy (Italia)",
38725         "it",
38726         "39",
38727         0
38728       ],
38729       [
38730         "Jamaica",
38731         "jm",
38732         "1876"
38733       ],
38734       [
38735         "Japan (日本)",
38736         "jp",
38737         "81"
38738       ],
38739       [
38740         "Jersey",
38741         "je",
38742         "44",
38743         3
38744       ],
38745       [
38746         "Jordan (‫الأردن‬‎)",
38747         "jo",
38748         "962"
38749       ],
38750       [
38751         "Kazakhstan (Казахстан)",
38752         "kz",
38753         "7",
38754         1
38755       ],
38756       [
38757         "Kenya",
38758         "ke",
38759         "254"
38760       ],
38761       [
38762         "Kiribati",
38763         "ki",
38764         "686"
38765       ],
38766       [
38767         "Kosovo",
38768         "xk",
38769         "383"
38770       ],
38771       [
38772         "Kuwait (‫الكويت‬‎)",
38773         "kw",
38774         "965"
38775       ],
38776       [
38777         "Kyrgyzstan (Кыргызстан)",
38778         "kg",
38779         "996"
38780       ],
38781       [
38782         "Laos (ລາວ)",
38783         "la",
38784         "856"
38785       ],
38786       [
38787         "Latvia (Latvija)",
38788         "lv",
38789         "371"
38790       ],
38791       [
38792         "Lebanon (‫لبنان‬‎)",
38793         "lb",
38794         "961"
38795       ],
38796       [
38797         "Lesotho",
38798         "ls",
38799         "266"
38800       ],
38801       [
38802         "Liberia",
38803         "lr",
38804         "231"
38805       ],
38806       [
38807         "Libya (‫ليبيا‬‎)",
38808         "ly",
38809         "218"
38810       ],
38811       [
38812         "Liechtenstein",
38813         "li",
38814         "423"
38815       ],
38816       [
38817         "Lithuania (Lietuva)",
38818         "lt",
38819         "370"
38820       ],
38821       [
38822         "Luxembourg",
38823         "lu",
38824         "352"
38825       ],
38826       [
38827         "Macau (澳門)",
38828         "mo",
38829         "853"
38830       ],
38831       [
38832         "Macedonia (FYROM) (Македонија)",
38833         "mk",
38834         "389"
38835       ],
38836       [
38837         "Madagascar (Madagasikara)",
38838         "mg",
38839         "261"
38840       ],
38841       [
38842         "Malawi",
38843         "mw",
38844         "265"
38845       ],
38846       [
38847         "Malaysia",
38848         "my",
38849         "60"
38850       ],
38851       [
38852         "Maldives",
38853         "mv",
38854         "960"
38855       ],
38856       [
38857         "Mali",
38858         "ml",
38859         "223"
38860       ],
38861       [
38862         "Malta",
38863         "mt",
38864         "356"
38865       ],
38866       [
38867         "Marshall Islands",
38868         "mh",
38869         "692"
38870       ],
38871       [
38872         "Martinique",
38873         "mq",
38874         "596"
38875       ],
38876       [
38877         "Mauritania (‫موريتانيا‬‎)",
38878         "mr",
38879         "222"
38880       ],
38881       [
38882         "Mauritius (Moris)",
38883         "mu",
38884         "230"
38885       ],
38886       [
38887         "Mayotte",
38888         "yt",
38889         "262",
38890         1
38891       ],
38892       [
38893         "Mexico (México)",
38894         "mx",
38895         "52"
38896       ],
38897       [
38898         "Micronesia",
38899         "fm",
38900         "691"
38901       ],
38902       [
38903         "Moldova (Republica Moldova)",
38904         "md",
38905         "373"
38906       ],
38907       [
38908         "Monaco",
38909         "mc",
38910         "377"
38911       ],
38912       [
38913         "Mongolia (Монгол)",
38914         "mn",
38915         "976"
38916       ],
38917       [
38918         "Montenegro (Crna Gora)",
38919         "me",
38920         "382"
38921       ],
38922       [
38923         "Montserrat",
38924         "ms",
38925         "1664"
38926       ],
38927       [
38928         "Morocco (‫المغرب‬‎)",
38929         "ma",
38930         "212",
38931         0
38932       ],
38933       [
38934         "Mozambique (Moçambique)",
38935         "mz",
38936         "258"
38937       ],
38938       [
38939         "Myanmar (Burma) (မြန်မာ)",
38940         "mm",
38941         "95"
38942       ],
38943       [
38944         "Namibia (Namibië)",
38945         "na",
38946         "264"
38947       ],
38948       [
38949         "Nauru",
38950         "nr",
38951         "674"
38952       ],
38953       [
38954         "Nepal (नेपाल)",
38955         "np",
38956         "977"
38957       ],
38958       [
38959         "Netherlands (Nederland)",
38960         "nl",
38961         "31"
38962       ],
38963       [
38964         "New Caledonia (Nouvelle-Calédonie)",
38965         "nc",
38966         "687"
38967       ],
38968       [
38969         "New Zealand",
38970         "nz",
38971         "64"
38972       ],
38973       [
38974         "Nicaragua",
38975         "ni",
38976         "505"
38977       ],
38978       [
38979         "Niger (Nijar)",
38980         "ne",
38981         "227"
38982       ],
38983       [
38984         "Nigeria",
38985         "ng",
38986         "234"
38987       ],
38988       [
38989         "Niue",
38990         "nu",
38991         "683"
38992       ],
38993       [
38994         "Norfolk Island",
38995         "nf",
38996         "672"
38997       ],
38998       [
38999         "North Korea (조선 민주주의 인민 공화국)",
39000         "kp",
39001         "850"
39002       ],
39003       [
39004         "Northern Mariana Islands",
39005         "mp",
39006         "1670"
39007       ],
39008       [
39009         "Norway (Norge)",
39010         "no",
39011         "47",
39012         0
39013       ],
39014       [
39015         "Oman (‫عُمان‬‎)",
39016         "om",
39017         "968"
39018       ],
39019       [
39020         "Pakistan (‫پاکستان‬‎)",
39021         "pk",
39022         "92"
39023       ],
39024       [
39025         "Palau",
39026         "pw",
39027         "680"
39028       ],
39029       [
39030         "Palestine (‫فلسطين‬‎)",
39031         "ps",
39032         "970"
39033       ],
39034       [
39035         "Panama (Panamá)",
39036         "pa",
39037         "507"
39038       ],
39039       [
39040         "Papua New Guinea",
39041         "pg",
39042         "675"
39043       ],
39044       [
39045         "Paraguay",
39046         "py",
39047         "595"
39048       ],
39049       [
39050         "Peru (Perú)",
39051         "pe",
39052         "51"
39053       ],
39054       [
39055         "Philippines",
39056         "ph",
39057         "63"
39058       ],
39059       [
39060         "Poland (Polska)",
39061         "pl",
39062         "48"
39063       ],
39064       [
39065         "Portugal",
39066         "pt",
39067         "351"
39068       ],
39069       [
39070         "Puerto Rico",
39071         "pr",
39072         "1",
39073         3,
39074         ["787", "939"]
39075       ],
39076       [
39077         "Qatar (‫قطر‬‎)",
39078         "qa",
39079         "974"
39080       ],
39081       [
39082         "Réunion (La Réunion)",
39083         "re",
39084         "262",
39085         0
39086       ],
39087       [
39088         "Romania (România)",
39089         "ro",
39090         "40"
39091       ],
39092       [
39093         "Russia (Россия)",
39094         "ru",
39095         "7",
39096         0
39097       ],
39098       [
39099         "Rwanda",
39100         "rw",
39101         "250"
39102       ],
39103       [
39104         "Saint Barthélemy",
39105         "bl",
39106         "590",
39107         1
39108       ],
39109       [
39110         "Saint Helena",
39111         "sh",
39112         "290"
39113       ],
39114       [
39115         "Saint Kitts and Nevis",
39116         "kn",
39117         "1869"
39118       ],
39119       [
39120         "Saint Lucia",
39121         "lc",
39122         "1758"
39123       ],
39124       [
39125         "Saint Martin (Saint-Martin (partie française))",
39126         "mf",
39127         "590",
39128         2
39129       ],
39130       [
39131         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39132         "pm",
39133         "508"
39134       ],
39135       [
39136         "Saint Vincent and the Grenadines",
39137         "vc",
39138         "1784"
39139       ],
39140       [
39141         "Samoa",
39142         "ws",
39143         "685"
39144       ],
39145       [
39146         "San Marino",
39147         "sm",
39148         "378"
39149       ],
39150       [
39151         "São Tomé and Príncipe (São Tomé e Príncipe)",
39152         "st",
39153         "239"
39154       ],
39155       [
39156         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39157         "sa",
39158         "966"
39159       ],
39160       [
39161         "Senegal (Sénégal)",
39162         "sn",
39163         "221"
39164       ],
39165       [
39166         "Serbia (Србија)",
39167         "rs",
39168         "381"
39169       ],
39170       [
39171         "Seychelles",
39172         "sc",
39173         "248"
39174       ],
39175       [
39176         "Sierra Leone",
39177         "sl",
39178         "232"
39179       ],
39180       [
39181         "Singapore",
39182         "sg",
39183         "65"
39184       ],
39185       [
39186         "Sint Maarten",
39187         "sx",
39188         "1721"
39189       ],
39190       [
39191         "Slovakia (Slovensko)",
39192         "sk",
39193         "421"
39194       ],
39195       [
39196         "Slovenia (Slovenija)",
39197         "si",
39198         "386"
39199       ],
39200       [
39201         "Solomon Islands",
39202         "sb",
39203         "677"
39204       ],
39205       [
39206         "Somalia (Soomaaliya)",
39207         "so",
39208         "252"
39209       ],
39210       [
39211         "South Africa",
39212         "za",
39213         "27"
39214       ],
39215       [
39216         "South Korea (대한민국)",
39217         "kr",
39218         "82"
39219       ],
39220       [
39221         "South Sudan (‫جنوب السودان‬‎)",
39222         "ss",
39223         "211"
39224       ],
39225       [
39226         "Spain (España)",
39227         "es",
39228         "34"
39229       ],
39230       [
39231         "Sri Lanka (ශ්‍රී ලංකාව)",
39232         "lk",
39233         "94"
39234       ],
39235       [
39236         "Sudan (‫السودان‬‎)",
39237         "sd",
39238         "249"
39239       ],
39240       [
39241         "Suriname",
39242         "sr",
39243         "597"
39244       ],
39245       [
39246         "Svalbard and Jan Mayen",
39247         "sj",
39248         "47",
39249         1
39250       ],
39251       [
39252         "Swaziland",
39253         "sz",
39254         "268"
39255       ],
39256       [
39257         "Sweden (Sverige)",
39258         "se",
39259         "46"
39260       ],
39261       [
39262         "Switzerland (Schweiz)",
39263         "ch",
39264         "41"
39265       ],
39266       [
39267         "Syria (‫سوريا‬‎)",
39268         "sy",
39269         "963"
39270       ],
39271       [
39272         "Taiwan (台灣)",
39273         "tw",
39274         "886"
39275       ],
39276       [
39277         "Tajikistan",
39278         "tj",
39279         "992"
39280       ],
39281       [
39282         "Tanzania",
39283         "tz",
39284         "255"
39285       ],
39286       [
39287         "Thailand (ไทย)",
39288         "th",
39289         "66"
39290       ],
39291       [
39292         "Timor-Leste",
39293         "tl",
39294         "670"
39295       ],
39296       [
39297         "Togo",
39298         "tg",
39299         "228"
39300       ],
39301       [
39302         "Tokelau",
39303         "tk",
39304         "690"
39305       ],
39306       [
39307         "Tonga",
39308         "to",
39309         "676"
39310       ],
39311       [
39312         "Trinidad and Tobago",
39313         "tt",
39314         "1868"
39315       ],
39316       [
39317         "Tunisia (‫تونس‬‎)",
39318         "tn",
39319         "216"
39320       ],
39321       [
39322         "Turkey (Türkiye)",
39323         "tr",
39324         "90"
39325       ],
39326       [
39327         "Turkmenistan",
39328         "tm",
39329         "993"
39330       ],
39331       [
39332         "Turks and Caicos Islands",
39333         "tc",
39334         "1649"
39335       ],
39336       [
39337         "Tuvalu",
39338         "tv",
39339         "688"
39340       ],
39341       [
39342         "U.S. Virgin Islands",
39343         "vi",
39344         "1340"
39345       ],
39346       [
39347         "Uganda",
39348         "ug",
39349         "256"
39350       ],
39351       [
39352         "Ukraine (Україна)",
39353         "ua",
39354         "380"
39355       ],
39356       [
39357         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39358         "ae",
39359         "971"
39360       ],
39361       [
39362         "United Kingdom",
39363         "gb",
39364         "44",
39365         0
39366       ],
39367       [
39368         "United States",
39369         "us",
39370         "1",
39371         0
39372       ],
39373       [
39374         "Uruguay",
39375         "uy",
39376         "598"
39377       ],
39378       [
39379         "Uzbekistan (Oʻzbekiston)",
39380         "uz",
39381         "998"
39382       ],
39383       [
39384         "Vanuatu",
39385         "vu",
39386         "678"
39387       ],
39388       [
39389         "Vatican City (Città del Vaticano)",
39390         "va",
39391         "39",
39392         1
39393       ],
39394       [
39395         "Venezuela",
39396         "ve",
39397         "58"
39398       ],
39399       [
39400         "Vietnam (Việt Nam)",
39401         "vn",
39402         "84"
39403       ],
39404       [
39405         "Wallis and Futuna (Wallis-et-Futuna)",
39406         "wf",
39407         "681"
39408       ],
39409       [
39410         "Western Sahara (‫الصحراء الغربية‬‎)",
39411         "eh",
39412         "212",
39413         1
39414       ],
39415       [
39416         "Yemen (‫اليمن‬‎)",
39417         "ye",
39418         "967"
39419       ],
39420       [
39421         "Zambia",
39422         "zm",
39423         "260"
39424       ],
39425       [
39426         "Zimbabwe",
39427         "zw",
39428         "263"
39429       ],
39430       [
39431         "Åland Islands",
39432         "ax",
39433         "358",
39434         1
39435       ]
39436   ];
39437   
39438   return d;
39439 }/**
39440 *    This script refer to:
39441 *    Title: International Telephone Input
39442 *    Author: Jack O'Connor
39443 *    Code version:  v12.1.12
39444 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39445 **/
39446
39447 /**
39448  * @class Roo.bootstrap.PhoneInput
39449  * @extends Roo.bootstrap.TriggerField
39450  * An input with International dial-code selection
39451  
39452  * @cfg {String} defaultDialCode default '+852'
39453  * @cfg {Array} preferedCountries default []
39454   
39455  * @constructor
39456  * Create a new PhoneInput.
39457  * @param {Object} config Configuration options
39458  */
39459
39460 Roo.bootstrap.PhoneInput = function(config) {
39461     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39462 };
39463
39464 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39465         
39466         listWidth: undefined,
39467         
39468         selectedClass: 'active',
39469         
39470         invalidClass : "has-warning",
39471         
39472         validClass: 'has-success',
39473         
39474         allowed: '0123456789',
39475         
39476         /**
39477          * @cfg {String} defaultDialCode The default dial code when initializing the input
39478          */
39479         defaultDialCode: '+852',
39480         
39481         /**
39482          * @cfg {Array} preferedCountries A list of iso2 in array (e.g. ['hk','us']). Those related countries will show at the top of the input's choices
39483          */
39484         preferedCountries: false,
39485         
39486         getAutoCreate : function()
39487         {
39488             var data = Roo.bootstrap.PhoneInputData();
39489             var align = this.labelAlign || this.parentLabelAlign();
39490             var id = Roo.id();
39491             
39492             this.allCountries = [];
39493             this.dialCodeMapping = [];
39494             
39495             for (var i = 0; i < data.length; i++) {
39496               var c = data[i];
39497               this.allCountries[i] = {
39498                 name: c[0],
39499                 iso2: c[1],
39500                 dialCode: c[2],
39501                 priority: c[3] || 0,
39502                 areaCodes: c[4] || null
39503               };
39504               this.dialCodeMapping[c[2]] = {
39505                   name: c[0],
39506                   iso2: c[1],
39507                   priority: c[3] || 0,
39508                   areaCodes: c[4] || null
39509               };
39510             }
39511             
39512             var cfg = {
39513                 cls: 'form-group',
39514                 cn: []
39515             };
39516             
39517             var input =  {
39518                 tag: 'input',
39519                 id : id,
39520                 cls : 'form-control tel-input',
39521                 autocomplete: 'new-password'
39522             };
39523             
39524             var hiddenInput = {
39525                 tag: 'input',
39526                 type: 'hidden',
39527                 cls: 'hidden-tel-input'
39528             };
39529             
39530             if (this.name) {
39531                 hiddenInput.name = this.name;
39532             }
39533             
39534             if (this.disabled) {
39535                 input.disabled = true;
39536             }
39537             
39538             var flag_container = {
39539                 tag: 'div',
39540                 cls: 'flag-box',
39541                 cn: [
39542                     {
39543                         tag: 'div',
39544                         cls: 'flag'
39545                     },
39546                     {
39547                         tag: 'div',
39548                         cls: 'caret'
39549                     }
39550                 ]
39551             };
39552             
39553             var box = {
39554                 tag: 'div',
39555                 cls: this.hasFeedback ? 'has-feedback' : '',
39556                 cn: [
39557                     hiddenInput,
39558                     input,
39559                     {
39560                         tag: 'input',
39561                         cls: 'dial-code-holder',
39562                         disabled: true
39563                     }
39564                 ]
39565             };
39566             
39567             var container = {
39568                 cls: 'roo-select2-container input-group',
39569                 cn: [
39570                     flag_container,
39571                     box
39572                 ]
39573             };
39574             
39575             if (this.fieldLabel.length) {
39576                 var indicator = {
39577                     tag: 'i',
39578                     tooltip: 'This field is required'
39579                 };
39580                 
39581                 var label = {
39582                     tag: 'label',
39583                     'for':  id,
39584                     cls: 'control-label',
39585                     cn: []
39586                 };
39587                 
39588                 var label_text = {
39589                     tag: 'span',
39590                     html: this.fieldLabel
39591                 };
39592                 
39593                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39594                 label.cn = [
39595                     indicator,
39596                     label_text
39597                 ];
39598                 
39599                 if(this.indicatorpos == 'right') {
39600                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39601                     label.cn = [
39602                         label_text,
39603                         indicator
39604                     ];
39605                 }
39606                 
39607                 if(align == 'left') {
39608                     container = {
39609                         tag: 'div',
39610                         cn: [
39611                             container
39612                         ]
39613                     };
39614                     
39615                     if(this.labelWidth > 12){
39616                         label.style = "width: " + this.labelWidth + 'px';
39617                     }
39618                     if(this.labelWidth < 13 && this.labelmd == 0){
39619                         this.labelmd = this.labelWidth;
39620                     }
39621                     if(this.labellg > 0){
39622                         label.cls += ' col-lg-' + this.labellg;
39623                         input.cls += ' col-lg-' + (12 - this.labellg);
39624                     }
39625                     if(this.labelmd > 0){
39626                         label.cls += ' col-md-' + this.labelmd;
39627                         container.cls += ' col-md-' + (12 - this.labelmd);
39628                     }
39629                     if(this.labelsm > 0){
39630                         label.cls += ' col-sm-' + this.labelsm;
39631                         container.cls += ' col-sm-' + (12 - this.labelsm);
39632                     }
39633                     if(this.labelxs > 0){
39634                         label.cls += ' col-xs-' + this.labelxs;
39635                         container.cls += ' col-xs-' + (12 - this.labelxs);
39636                     }
39637                 }
39638             }
39639             
39640             cfg.cn = [
39641                 label,
39642                 container
39643             ];
39644             
39645             var settings = this;
39646             
39647             ['xs','sm','md','lg'].map(function(size){
39648                 if (settings[size]) {
39649                     cfg.cls += ' col-' + size + '-' + settings[size];
39650                 }
39651             });
39652             
39653             this.store = new Roo.data.Store({
39654                 proxy : new Roo.data.MemoryProxy({}),
39655                 reader : new Roo.data.JsonReader({
39656                     fields : [
39657                         {
39658                             'name' : 'name',
39659                             'type' : 'string'
39660                         },
39661                         {
39662                             'name' : 'iso2',
39663                             'type' : 'string'
39664                         },
39665                         {
39666                             'name' : 'dialCode',
39667                             'type' : 'string'
39668                         },
39669                         {
39670                             'name' : 'priority',
39671                             'type' : 'string'
39672                         },
39673                         {
39674                             'name' : 'areaCodes',
39675                             'type' : 'string'
39676                         }
39677                     ]
39678                 })
39679             });
39680             
39681             if(!this.preferedCountries) {
39682                 this.preferedCountries = [
39683                     'hk',
39684                     'gb',
39685                     'us'
39686                 ];
39687             }
39688             
39689             var p = this.preferedCountries.reverse();
39690             
39691             if(p) {
39692                 for (var i = 0; i < p.length; i++) {
39693                     for (var j = 0; j < this.allCountries.length; j++) {
39694                         if(this.allCountries[j].iso2 == p[i]) {
39695                             var t = this.allCountries[j];
39696                             this.allCountries.splice(j,1);
39697                             this.allCountries.unshift(t);
39698                         }
39699                     } 
39700                 }
39701             }
39702             
39703             this.store.proxy.data = {
39704                 success: true,
39705                 data: this.allCountries
39706             };
39707             
39708             return cfg;
39709         },
39710         
39711         initEvents : function()
39712         {
39713             this.createList();
39714             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39715             
39716             this.indicator = this.indicatorEl();
39717             this.flag = this.flagEl();
39718             this.dialCodeHolder = this.dialCodeHolderEl();
39719             
39720             this.trigger = this.el.select('div.flag-box',true).first();
39721             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39722             
39723             var _this = this;
39724             
39725             (function(){
39726                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39727                 _this.list.setWidth(lw);
39728             }).defer(100);
39729             
39730             this.list.on('mouseover', this.onViewOver, this);
39731             this.list.on('mousemove', this.onViewMove, this);
39732             this.inputEl().on("keyup", this.onKeyUp, this);
39733             
39734             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
39735
39736             this.view = new Roo.View(this.list, this.tpl, {
39737                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
39738             });
39739             
39740             this.view.on('click', this.onViewClick, this);
39741             this.setValue(this.defaultDialCode);
39742         },
39743         
39744         onTriggerClick : function(e)
39745         {
39746             Roo.log('trigger click');
39747             if(this.disabled){
39748                 return;
39749             }
39750             
39751             if(this.isExpanded()){
39752                 this.collapse();
39753                 this.hasFocus = false;
39754             }else {
39755                 this.store.load({});
39756                 this.hasFocus = true;
39757                 this.expand();
39758             }
39759         },
39760         
39761         isExpanded : function()
39762         {
39763             return this.list.isVisible();
39764         },
39765         
39766         collapse : function()
39767         {
39768             if(!this.isExpanded()){
39769                 return;
39770             }
39771             this.list.hide();
39772             Roo.get(document).un('mousedown', this.collapseIf, this);
39773             Roo.get(document).un('mousewheel', this.collapseIf, this);
39774             this.fireEvent('collapse', this);
39775             this.validate();
39776         },
39777         
39778         expand : function()
39779         {
39780             Roo.log('expand');
39781
39782             if(this.isExpanded() || !this.hasFocus){
39783                 return;
39784             }
39785             
39786             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
39787             this.list.setWidth(lw);
39788             
39789             this.list.show();
39790             this.restrictHeight();
39791             
39792             Roo.get(document).on('mousedown', this.collapseIf, this);
39793             Roo.get(document).on('mousewheel', this.collapseIf, this);
39794             
39795             this.fireEvent('expand', this);
39796         },
39797         
39798         restrictHeight : function()
39799         {
39800             this.list.alignTo(this.inputEl(), this.listAlign);
39801             this.list.alignTo(this.inputEl(), this.listAlign);
39802         },
39803         
39804         onViewOver : function(e, t)
39805         {
39806             if(this.inKeyMode){
39807                 return;
39808             }
39809             var item = this.view.findItemFromChild(t);
39810             
39811             if(item){
39812                 var index = this.view.indexOf(item);
39813                 this.select(index, false);
39814             }
39815         },
39816
39817         // private
39818         onViewClick : function(view, doFocus, el, e)
39819         {
39820             var index = this.view.getSelectedIndexes()[0];
39821             
39822             var r = this.store.getAt(index);
39823             
39824             if(r){
39825                 this.onSelect(r, index);
39826             }
39827             if(doFocus !== false && !this.blockFocus){
39828                 this.inputEl().focus();
39829             }
39830         },
39831         
39832         onViewMove : function(e, t)
39833         {
39834             this.inKeyMode = false;
39835         },
39836         
39837         select : function(index, scrollIntoView)
39838         {
39839             this.selectedIndex = index;
39840             this.view.select(index);
39841             if(scrollIntoView !== false){
39842                 var el = this.view.getNode(index);
39843                 if(el){
39844                     this.list.scrollChildIntoView(el, false);
39845                 }
39846             }
39847         },
39848         
39849         createList : function()
39850         {
39851             this.list = Roo.get(document.body).createChild({
39852                 tag: 'ul',
39853                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
39854                 style: 'display:none'
39855             });
39856             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
39857         },
39858         
39859         collapseIf : function(e)
39860         {
39861             var in_combo  = e.within(this.el);
39862             var in_list =  e.within(this.list);
39863             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
39864             
39865             if (in_combo || in_list || is_list) {
39866                 return;
39867             }
39868             this.collapse();
39869         },
39870         
39871         onSelect : function(record, index)
39872         {
39873             if(this.fireEvent('beforeselect', this, record, index) !== false){
39874                 
39875                 this.setFlagClass(record.data.iso2);
39876                 this.setDialCode(record.data.dialCode);
39877                 this.hasFocus = false;
39878                 this.collapse();
39879                 this.fireEvent('select', this, record, index);
39880             }
39881         },
39882         
39883         flagEl : function()
39884         {
39885             var flag = this.el.select('div.flag',true).first();
39886             if(!flag){
39887                 return false;
39888             }
39889             return flag;
39890         },
39891         
39892         dialCodeHolderEl : function()
39893         {
39894             var d = this.el.select('input.dial-code-holder',true).first();
39895             if(!d){
39896                 return false;
39897             }
39898             return d;
39899         },
39900         
39901         setDialCode : function(v)
39902         {
39903             this.dialCodeHolder.dom.value = '+'+v;
39904         },
39905         
39906         setFlagClass : function(n)
39907         {
39908             this.flag.dom.className = 'flag '+n;
39909         },
39910         
39911         getValue : function()
39912         {
39913             var v = this.inputEl().getValue();
39914             if(this.dialCodeHolder) {
39915                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
39916             }
39917             return v;
39918         },
39919         
39920         setValue : function(v)
39921         {
39922             var d = this.getDialCode(v);
39923             
39924             //invalid dial code
39925             if(v.length == 0 || !d || d.length == 0) {
39926                 if(this.rendered){
39927                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
39928                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
39929                 }
39930                 return;
39931             }
39932             
39933             //valid dial code
39934             this.setFlagClass(this.dialCodeMapping[d].iso2);
39935             this.setDialCode(d);
39936             this.inputEl().dom.value = v.replace('+'+d,'');
39937             this.hiddenEl().dom.value = this.getValue();
39938             
39939             this.validate();
39940         },
39941         
39942         getDialCode : function(v = '')
39943         {
39944             if (v.length == 0) {
39945                 return this.dialCodeHolder.dom.value;
39946             }
39947             
39948             var dialCode = "";
39949             if (v.charAt(0) != "+") {
39950                 return false;
39951             }
39952             var numericChars = "";
39953             for (var i = 1; i < v.length; i++) {
39954               var c = v.charAt(i);
39955               if (!isNaN(c)) {
39956                 numericChars += c;
39957                 if (this.dialCodeMapping[numericChars]) {
39958                   dialCode = v.substr(1, i);
39959                 }
39960                 if (numericChars.length == 4) {
39961                   break;
39962                 }
39963               }
39964             }
39965             return dialCode;
39966         },
39967         
39968         reset : function()
39969         {
39970             this.setValue(this.defaultDialCode);
39971             this.validate();
39972         },
39973         
39974         hiddenEl : function()
39975         {
39976             return this.el.select('input.hidden-tel-input',true).first();
39977         },
39978         
39979         onKeyUp : function(e){
39980             
39981             var k = e.getKey();
39982             var c = e.getCharCode();
39983             
39984             if(
39985                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
39986                     this.allowed.indexOf(String.fromCharCode(c)) === -1
39987             ){
39988                 e.stopEvent();
39989             }
39990             
39991             // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
39992             //     return;
39993             // }
39994             if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
39995                 e.stopEvent();
39996             }
39997             
39998             this.setValue(this.getValue());
39999         }
40000         
40001 });
40002 /**
40003  * @class Roo.bootstrap.MoneyField
40004  * @extends Roo.bootstrap.ComboBox
40005  * Bootstrap MoneyField class
40006  * 
40007  * @constructor
40008  * Create a new MoneyField.
40009  * @param {Object} config Configuration options
40010  */
40011
40012 Roo.bootstrap.MoneyField = function(config) {
40013     
40014     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40015     
40016 };
40017
40018 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40019     
40020     /**
40021      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40022      */
40023     allowDecimals : true,
40024     /**
40025      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40026      */
40027     decimalSeparator : ".",
40028     /**
40029      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40030      */
40031     decimalPrecision : 2,
40032     /**
40033      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40034      */
40035     allowNegative : true,
40036     /**
40037      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40038      */
40039     minValue : Number.NEGATIVE_INFINITY,
40040     /**
40041      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40042      */
40043     maxValue : Number.MAX_VALUE,
40044     /**
40045      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40046      */
40047     minText : "The minimum value for this field is {0}",
40048     /**
40049      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40050      */
40051     maxText : "The maximum value for this field is {0}",
40052     /**
40053      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40054      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40055      */
40056     nanText : "{0} is not a valid number",
40057     /**
40058      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40059      */
40060     castInt : true,
40061     /**
40062      * @cfg {String} defaults currency of the MoneyField
40063      * value should be in lkey
40064      */
40065     defaultCurrency : false,
40066     
40067     
40068     inputlg : 9,
40069     inputmd : 9,
40070     inputsm : 9,
40071     inputxs : 6,
40072     
40073     store : false,
40074     
40075     getAutoCreate : function()
40076     {
40077         var align = this.labelAlign || this.parentLabelAlign();
40078         
40079         var id = Roo.id();
40080
40081         var cfg = {
40082             cls: 'form-group',
40083             cn: []
40084         };
40085
40086         var input =  {
40087             tag: 'input',
40088             id : id,
40089             cls : 'form-control roo-money-amount-input',
40090             autocomplete: 'new-password'
40091         };
40092         
40093         if (this.name) {
40094             input.name = this.name;
40095         }
40096
40097         if (this.disabled) {
40098             input.disabled = true;
40099         }
40100
40101         var clg = 12 - this.inputlg;
40102         var cmd = 12 - this.inputmd;
40103         var csm = 12 - this.inputsm;
40104         var cxs = 12 - this.inputxs;
40105         
40106         var container = {
40107             tag : 'div',
40108             cls : 'row roo-money-field',
40109             cn : [
40110                 {
40111                     tag : 'div',
40112                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40113                     cn : [
40114                         {
40115                             tag : 'div',
40116                             cls: 'roo-select2-container input-group',
40117                             cn: [
40118                                 {
40119                                     tag : 'input',
40120                                     cls : 'form-control roo-money-currency-input',
40121                                     autocomplete: 'new-password',
40122                                     readOnly : 1,
40123                                     name : this.currencyName
40124                                 },
40125                                 {
40126                                     tag :'span',
40127                                     cls : 'input-group-addon',
40128                                     cn : [
40129                                         {
40130                                             tag: 'span',
40131                                             cls: 'caret'
40132                                         }
40133                                     ]
40134                                 }
40135                             ]
40136                         }
40137                     ]
40138                 },
40139                 {
40140                     tag : 'div',
40141                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40142                     cn : [
40143                         {
40144                             tag: 'div',
40145                             cls: this.hasFeedback ? 'has-feedback' : '',
40146                             cn: [
40147                                 input
40148                             ]
40149                         }
40150                     ]
40151                 }
40152             ]
40153             
40154         };
40155         
40156         if (this.fieldLabel.length) {
40157             var indicator = {
40158                 tag: 'i',
40159                 tooltip: 'This field is required'
40160             };
40161
40162             var label = {
40163                 tag: 'label',
40164                 'for':  id,
40165                 cls: 'control-label',
40166                 cn: []
40167             };
40168
40169             var label_text = {
40170                 tag: 'span',
40171                 html: this.fieldLabel
40172             };
40173
40174             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40175             label.cn = [
40176                 indicator,
40177                 label_text
40178             ];
40179
40180             if(this.indicatorpos == 'right') {
40181                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40182                 label.cn = [
40183                     label_text,
40184                     indicator
40185                 ];
40186             }
40187
40188             if(align == 'left') {
40189                 container = {
40190                     tag: 'div',
40191                     cn: [
40192                         container
40193                     ]
40194                 };
40195
40196                 if(this.labelWidth > 12){
40197                     label.style = "width: " + this.labelWidth + 'px';
40198                 }
40199                 if(this.labelWidth < 13 && this.labelmd == 0){
40200                     this.labelmd = this.labelWidth;
40201                 }
40202                 if(this.labellg > 0){
40203                     label.cls += ' col-lg-' + this.labellg;
40204                     input.cls += ' col-lg-' + (12 - this.labellg);
40205                 }
40206                 if(this.labelmd > 0){
40207                     label.cls += ' col-md-' + this.labelmd;
40208                     container.cls += ' col-md-' + (12 - this.labelmd);
40209                 }
40210                 if(this.labelsm > 0){
40211                     label.cls += ' col-sm-' + this.labelsm;
40212                     container.cls += ' col-sm-' + (12 - this.labelsm);
40213                 }
40214                 if(this.labelxs > 0){
40215                     label.cls += ' col-xs-' + this.labelxs;
40216                     container.cls += ' col-xs-' + (12 - this.labelxs);
40217                 }
40218             }
40219         }
40220
40221         cfg.cn = [
40222             label,
40223             container
40224         ];
40225
40226         var settings = this;
40227
40228         ['xs','sm','md','lg'].map(function(size){
40229             if (settings[size]) {
40230                 cfg.cls += ' col-' + size + '-' + settings[size];
40231             }
40232         });
40233         
40234         return cfg;
40235         
40236     },
40237     
40238     initEvents : function()
40239     {
40240         this.indicator = this.indicatorEl();
40241         
40242         this.initCurrencyEvent();
40243         
40244         this.initNumberEvent();
40245         
40246     },
40247     
40248     initCurrencyEvent : function()
40249     {
40250         if (!this.store) {
40251             throw "can not find store for combo";
40252         }
40253         
40254         this.store = Roo.factory(this.store, Roo.data);
40255         this.store.parent = this;
40256         
40257         this.createList();
40258         
40259         this.triggerEl = this.el.select('.input-group-addon', true).first();
40260         
40261         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40262         
40263         var _this = this;
40264         
40265         (function(){
40266             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40267             _this.list.setWidth(lw);
40268         }).defer(100);
40269         
40270         this.list.on('mouseover', this.onViewOver, this);
40271         this.list.on('mousemove', this.onViewMove, this);
40272         this.list.on('scroll', this.onViewScroll, this);
40273         
40274         if(!this.tpl){
40275             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40276         }
40277         
40278         this.view = new Roo.View(this.list, this.tpl, {
40279             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40280         });
40281         
40282         this.view.on('click', this.onViewClick, this);
40283         
40284         this.store.on('beforeload', this.onBeforeLoad, this);
40285         this.store.on('load', this.onLoad, this);
40286         this.store.on('loadexception', this.onLoadException, this);
40287         
40288         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40289             "up" : function(e){
40290                 this.inKeyMode = true;
40291                 this.selectPrev();
40292             },
40293
40294             "down" : function(e){
40295                 if(!this.isExpanded()){
40296                     this.onTriggerClick();
40297                 }else{
40298                     this.inKeyMode = true;
40299                     this.selectNext();
40300                 }
40301             },
40302
40303             "enter" : function(e){
40304                 this.collapse();
40305                 
40306                 if(this.fireEvent("specialkey", this, e)){
40307                     this.onViewClick(false);
40308                 }
40309                 
40310                 return true;
40311             },
40312
40313             "esc" : function(e){
40314                 this.collapse();
40315             },
40316
40317             "tab" : function(e){
40318                 this.collapse();
40319                 
40320                 if(this.fireEvent("specialkey", this, e)){
40321                     this.onViewClick(false);
40322                 }
40323                 
40324                 return true;
40325             },
40326
40327             scope : this,
40328
40329             doRelay : function(foo, bar, hname){
40330                 if(hname == 'down' || this.scope.isExpanded()){
40331                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40332                 }
40333                 return true;
40334             },
40335
40336             forceKeyDown: true
40337         });
40338         
40339         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40340         
40341     },
40342     
40343     initNumberEvent : function(e)
40344     {
40345         this.inputEl().on("keydown" , this.fireKey,  this);
40346         this.inputEl().on("focus", this.onFocus,  this);
40347         this.inputEl().on("blur", this.onBlur,  this);
40348         
40349         this.inputEl().relayEvent('keyup', this);
40350         
40351         if(this.indicator){
40352             this.indicator.addClass('invisible');
40353         }
40354  
40355         this.originalValue = this.getValue();
40356         
40357         if(this.validationEvent == 'keyup'){
40358             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40359             this.inputEl().on('keyup', this.filterValidation, this);
40360         }
40361         else if(this.validationEvent !== false){
40362             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40363         }
40364         
40365         if(this.selectOnFocus){
40366             this.on("focus", this.preFocus, this);
40367             
40368         }
40369         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40370             this.inputEl().on("keypress", this.filterKeys, this);
40371         } else {
40372             this.inputEl().relayEvent('keypress', this);
40373         }
40374         
40375         var allowed = "0123456789";
40376         
40377         if(this.allowDecimals){
40378             allowed += this.decimalSeparator;
40379         }
40380         
40381         if(this.allowNegative){
40382             allowed += "-";
40383         }
40384         
40385         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40386         
40387         var keyPress = function(e){
40388             
40389             var k = e.getKey();
40390             
40391             var c = e.getCharCode();
40392             
40393             if(
40394                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40395                     allowed.indexOf(String.fromCharCode(c)) === -1
40396             ){
40397                 e.stopEvent();
40398                 return;
40399             }
40400             
40401             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40402                 return;
40403             }
40404             
40405             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40406                 e.stopEvent();
40407             }
40408         };
40409         
40410         this.inputEl().on("keypress", keyPress, this);
40411         
40412     },
40413     
40414     onTriggerClick : function(e)
40415     {   
40416         if(this.disabled){
40417             return;
40418         }
40419         
40420         this.page = 0;
40421         this.loadNext = false;
40422         
40423         if(this.isExpanded()){
40424             this.collapse();
40425             return;
40426         }
40427         
40428         this.hasFocus = true;
40429         
40430         if(this.triggerAction == 'all') {
40431             this.doQuery(this.allQuery, true);
40432             return;
40433         }
40434         
40435         this.doQuery(this.getRawValue());
40436     },
40437     
40438     getCurrency : function()
40439     {   
40440         var v = this.currencyEl().getValue();
40441         
40442         return v;
40443     },
40444     
40445     restrictHeight : function()
40446     {
40447         this.list.alignTo(this.currencyEl(), this.listAlign);
40448         this.list.alignTo(this.currencyEl(), this.listAlign);
40449     },
40450     
40451     onViewClick : function(view, doFocus, el, e)
40452     {
40453         var index = this.view.getSelectedIndexes()[0];
40454         
40455         var r = this.store.getAt(index);
40456         
40457         if(r){
40458             this.onSelect(r, index);
40459         }
40460     },
40461     
40462     onSelect : function(record, index){
40463         
40464         if(this.fireEvent('beforeselect', this, record, index) !== false){
40465         
40466             this.setFromCurrencyData(index > -1 ? record.data : false);
40467             
40468             this.collapse();
40469             
40470             this.fireEvent('select', this, record, index);
40471         }
40472     },
40473     
40474     setFromCurrencyData : function(o)
40475     {
40476         var currency = '';
40477         
40478         this.lastCurrency = o;
40479         
40480         if (this.currencyField) {
40481             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40482         } else {
40483             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40484         }
40485         
40486         this.lastSelectionText = currency;
40487         
40488         //setting default currency
40489         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40490             this.setCurrency(this.defaultCurrency);
40491             return;
40492         }
40493         
40494         this.setCurrency(currency);
40495     },
40496     
40497     setFromData : function(o)
40498     {
40499         var c = {};
40500         
40501         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40502         
40503         this.setFromCurrencyData(c);
40504         
40505         var value = '';
40506         
40507         if (this.name) {
40508             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40509         } else {
40510             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40511         }
40512         
40513         this.setValue(value);
40514         
40515     },
40516     
40517     setCurrency : function(v)
40518     {   
40519         this.currencyValue = v;
40520         
40521         if(this.rendered){
40522             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40523             this.validate();
40524         }
40525     },
40526     
40527     setValue : function(v)
40528     {
40529         v = this.fixPrecision(v);
40530         
40531         v = String(v).replace(".", this.decimalSeparator);
40532         
40533         this.value = v;
40534         
40535         if(this.rendered){
40536             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40537             this.validate();
40538         }
40539     },
40540     
40541     getRawValue : function()
40542     {
40543         var v = this.inputEl().getValue();
40544         
40545         return v;
40546     },
40547     
40548     getValue : function()
40549     {
40550         return this.fixPrecision(this.parseValue(this.getRawValue()));
40551     },
40552     
40553     parseValue : function(value)
40554     {
40555         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40556         return isNaN(value) ? '' : value;
40557     },
40558     
40559     fixPrecision : function(value)
40560     {
40561         var nan = isNaN(value);
40562         
40563         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40564             return nan ? '' : value;
40565         }
40566         
40567         return parseFloat(value).toFixed(this.decimalPrecision);
40568     },
40569     
40570     decimalPrecisionFcn : function(v)
40571     {
40572         return Math.floor(v);
40573     },
40574     
40575     validateValue : function(value)
40576     {
40577         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40578             return false;
40579         }
40580         
40581         var num = this.parseValue(value);
40582         
40583         if(isNaN(num)){
40584             this.markInvalid(String.format(this.nanText, value));
40585             return false;
40586         }
40587         
40588         if(num < this.minValue){
40589             this.markInvalid(String.format(this.minText, this.minValue));
40590             return false;
40591         }
40592         
40593         if(num > this.maxValue){
40594             this.markInvalid(String.format(this.maxText, this.maxValue));
40595             return false;
40596         }
40597         
40598         return true;
40599     },
40600     
40601     validate : function()
40602     {
40603         if(this.disabled || this.allowBlank){
40604             this.markValid();
40605             return true;
40606         }
40607         
40608         var currency = this.getCurrency();
40609         
40610         if(this.validateValue(this.getRawValue()) && currency.length){
40611             this.markValid();
40612             return true;
40613         }
40614         
40615         this.markInvalid();
40616         return false;
40617     },
40618     
40619     getName: function()
40620     {
40621         return this.name;
40622     },
40623     
40624     beforeBlur : function()
40625     {
40626         if(!this.castInt){
40627             return;
40628         }
40629         
40630         var v = this.parseValue(this.getRawValue());
40631         
40632         if(v){
40633             this.setValue(v);
40634         }
40635     },
40636     
40637     onBlur : function()
40638     {
40639         this.beforeBlur();
40640         
40641         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40642             //this.el.removeClass(this.focusClass);
40643         }
40644         
40645         this.hasFocus = false;
40646         
40647         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40648             this.validate();
40649         }
40650         
40651         var v = this.getValue();
40652         
40653         if(String(v) !== String(this.startValue)){
40654             this.fireEvent('change', this, v, this.startValue);
40655         }
40656         
40657         this.fireEvent("blur", this);
40658     },
40659     
40660     inputEl : function()
40661     {
40662         return this.el.select('.roo-money-amount-input', true).first();
40663     },
40664     
40665     currencyEl : function()
40666     {
40667         return this.el.select('.roo-money-currency-input', true).first();
40668     }
40669     
40670 });