ad8ffcfa5f44e297de6b34924378b4942db90460
[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.start ? o.params.start : 0;
24443         
24444         var d = this.getPageData(),
24445             ap = d.activePage,
24446             ps = d.pages;
24447         
24448         
24449         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24450         this.field.dom.value = ap;
24451         this.first.setDisabled(ap == 1);
24452         this.prev.setDisabled(ap == 1);
24453         this.next.setDisabled(ap == ps);
24454         this.last.setDisabled(ap == ps);
24455         this.loading.enable();
24456         this.updateInfo();
24457     },
24458
24459     // private
24460     getPageData : function(){
24461         var total = this.ds.getTotalCount();
24462         return {
24463             total : total,
24464             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24465             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24466         };
24467     },
24468
24469     // private
24470     onLoadError : function(){
24471         this.loading.enable();
24472     },
24473
24474     // private
24475     onPagingKeydown : function(e){
24476         var k = e.getKey();
24477         var d = this.getPageData();
24478         if(k == e.RETURN){
24479             var v = this.field.dom.value, pageNum;
24480             if(!v || isNaN(pageNum = parseInt(v, 10))){
24481                 this.field.dom.value = d.activePage;
24482                 return;
24483             }
24484             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24485             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24486             e.stopEvent();
24487         }
24488         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))
24489         {
24490           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24491           this.field.dom.value = pageNum;
24492           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24493           e.stopEvent();
24494         }
24495         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24496         {
24497           var v = this.field.dom.value, pageNum; 
24498           var increment = (e.shiftKey) ? 10 : 1;
24499           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24500                 increment *= -1;
24501           }
24502           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24503             this.field.dom.value = d.activePage;
24504             return;
24505           }
24506           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24507           {
24508             this.field.dom.value = parseInt(v, 10) + increment;
24509             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24510             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24511           }
24512           e.stopEvent();
24513         }
24514     },
24515
24516     // private
24517     beforeLoad : function(){
24518         if(this.loading){
24519             this.loading.disable();
24520         }
24521     },
24522
24523     // private
24524     onClick : function(which){
24525         
24526         var ds = this.ds;
24527         if (!ds) {
24528             return;
24529         }
24530         
24531         switch(which){
24532             case "first":
24533                 ds.load({params:{start: 0, limit: this.pageSize}});
24534             break;
24535             case "prev":
24536                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24537             break;
24538             case "next":
24539                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24540             break;
24541             case "last":
24542                 var total = ds.getTotalCount();
24543                 var extra = total % this.pageSize;
24544                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24545                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24546             break;
24547             case "refresh":
24548                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24549             break;
24550         }
24551     },
24552
24553     /**
24554      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24555      * @param {Roo.data.Store} store The data store to unbind
24556      */
24557     unbind : function(ds){
24558         ds.un("beforeload", this.beforeLoad, this);
24559         ds.un("load", this.onLoad, this);
24560         ds.un("loadexception", this.onLoadError, this);
24561         ds.un("remove", this.updateInfo, this);
24562         ds.un("add", this.updateInfo, this);
24563         this.ds = undefined;
24564     },
24565
24566     /**
24567      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24568      * @param {Roo.data.Store} store The data store to bind
24569      */
24570     bind : function(ds){
24571         ds.on("beforeload", this.beforeLoad, this);
24572         ds.on("load", this.onLoad, this);
24573         ds.on("loadexception", this.onLoadError, this);
24574         ds.on("remove", this.updateInfo, this);
24575         ds.on("add", this.updateInfo, this);
24576         this.ds = ds;
24577     }
24578 });/*
24579  * - LGPL
24580  *
24581  * element
24582  * 
24583  */
24584
24585 /**
24586  * @class Roo.bootstrap.MessageBar
24587  * @extends Roo.bootstrap.Component
24588  * Bootstrap MessageBar class
24589  * @cfg {String} html contents of the MessageBar
24590  * @cfg {String} weight (info | success | warning | danger) default info
24591  * @cfg {String} beforeClass insert the bar before the given class
24592  * @cfg {Boolean} closable (true | false) default false
24593  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24594  * 
24595  * @constructor
24596  * Create a new Element
24597  * @param {Object} config The config object
24598  */
24599
24600 Roo.bootstrap.MessageBar = function(config){
24601     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24602 };
24603
24604 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24605     
24606     html: '',
24607     weight: 'info',
24608     closable: false,
24609     fixed: false,
24610     beforeClass: 'bootstrap-sticky-wrap',
24611     
24612     getAutoCreate : function(){
24613         
24614         var cfg = {
24615             tag: 'div',
24616             cls: 'alert alert-dismissable alert-' + this.weight,
24617             cn: [
24618                 {
24619                     tag: 'span',
24620                     cls: 'message',
24621                     html: this.html || ''
24622                 }
24623             ]
24624         };
24625         
24626         if(this.fixed){
24627             cfg.cls += ' alert-messages-fixed';
24628         }
24629         
24630         if(this.closable){
24631             cfg.cn.push({
24632                 tag: 'button',
24633                 cls: 'close',
24634                 html: 'x'
24635             });
24636         }
24637         
24638         return cfg;
24639     },
24640     
24641     onRender : function(ct, position)
24642     {
24643         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24644         
24645         if(!this.el){
24646             var cfg = Roo.apply({},  this.getAutoCreate());
24647             cfg.id = Roo.id();
24648             
24649             if (this.cls) {
24650                 cfg.cls += ' ' + this.cls;
24651             }
24652             if (this.style) {
24653                 cfg.style = this.style;
24654             }
24655             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24656             
24657             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24658         }
24659         
24660         this.el.select('>button.close').on('click', this.hide, this);
24661         
24662     },
24663     
24664     show : function()
24665     {
24666         if (!this.rendered) {
24667             this.render();
24668         }
24669         
24670         this.el.show();
24671         
24672         this.fireEvent('show', this);
24673         
24674     },
24675     
24676     hide : function()
24677     {
24678         if (!this.rendered) {
24679             this.render();
24680         }
24681         
24682         this.el.hide();
24683         
24684         this.fireEvent('hide', this);
24685     },
24686     
24687     update : function()
24688     {
24689 //        var e = this.el.dom.firstChild;
24690 //        
24691 //        if(this.closable){
24692 //            e = e.nextSibling;
24693 //        }
24694 //        
24695 //        e.data = this.html || '';
24696
24697         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24698     }
24699    
24700 });
24701
24702  
24703
24704      /*
24705  * - LGPL
24706  *
24707  * Graph
24708  * 
24709  */
24710
24711
24712 /**
24713  * @class Roo.bootstrap.Graph
24714  * @extends Roo.bootstrap.Component
24715  * Bootstrap Graph class
24716 > Prameters
24717  -sm {number} sm 4
24718  -md {number} md 5
24719  @cfg {String} graphtype  bar | vbar | pie
24720  @cfg {number} g_x coodinator | centre x (pie)
24721  @cfg {number} g_y coodinator | centre y (pie)
24722  @cfg {number} g_r radius (pie)
24723  @cfg {number} g_height height of the chart (respected by all elements in the set)
24724  @cfg {number} g_width width of the chart (respected by all elements in the set)
24725  @cfg {Object} title The title of the chart
24726     
24727  -{Array}  values
24728  -opts (object) options for the chart 
24729      o {
24730      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24731      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24732      o vgutter (number)
24733      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.
24734      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24735      o to
24736      o stretch (boolean)
24737      o }
24738  -opts (object) options for the pie
24739      o{
24740      o cut
24741      o startAngle (number)
24742      o endAngle (number)
24743      } 
24744  *
24745  * @constructor
24746  * Create a new Input
24747  * @param {Object} config The config object
24748  */
24749
24750 Roo.bootstrap.Graph = function(config){
24751     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24752     
24753     this.addEvents({
24754         // img events
24755         /**
24756          * @event click
24757          * The img click event for the img.
24758          * @param {Roo.EventObject} e
24759          */
24760         "click" : true
24761     });
24762 };
24763
24764 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
24765     
24766     sm: 4,
24767     md: 5,
24768     graphtype: 'bar',
24769     g_height: 250,
24770     g_width: 400,
24771     g_x: 50,
24772     g_y: 50,
24773     g_r: 30,
24774     opts:{
24775         //g_colors: this.colors,
24776         g_type: 'soft',
24777         g_gutter: '20%'
24778
24779     },
24780     title : false,
24781
24782     getAutoCreate : function(){
24783         
24784         var cfg = {
24785             tag: 'div',
24786             html : null
24787         };
24788         
24789         
24790         return  cfg;
24791     },
24792
24793     onRender : function(ct,position){
24794         
24795         
24796         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24797         
24798         if (typeof(Raphael) == 'undefined') {
24799             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24800             return;
24801         }
24802         
24803         this.raphael = Raphael(this.el.dom);
24804         
24805                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24806                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24807                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24808                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24809                 /*
24810                 r.text(160, 10, "Single Series Chart").attr(txtattr);
24811                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24812                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24813                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
24814                 
24815                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
24816                 r.barchart(330, 10, 300, 220, data1);
24817                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
24818                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
24819                 */
24820                 
24821                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24822                 // r.barchart(30, 30, 560, 250,  xdata, {
24823                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
24824                 //     axis : "0 0 1 1",
24825                 //     axisxlabels :  xdata
24826                 //     //yvalues : cols,
24827                    
24828                 // });
24829 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24830 //        
24831 //        this.load(null,xdata,{
24832 //                axis : "0 0 1 1",
24833 //                axisxlabels :  xdata
24834 //                });
24835
24836     },
24837
24838     load : function(graphtype,xdata,opts)
24839     {
24840         this.raphael.clear();
24841         if(!graphtype) {
24842             graphtype = this.graphtype;
24843         }
24844         if(!opts){
24845             opts = this.opts;
24846         }
24847         var r = this.raphael,
24848             fin = function () {
24849                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
24850             },
24851             fout = function () {
24852                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
24853             },
24854             pfin = function() {
24855                 this.sector.stop();
24856                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
24857
24858                 if (this.label) {
24859                     this.label[0].stop();
24860                     this.label[0].attr({ r: 7.5 });
24861                     this.label[1].attr({ "font-weight": 800 });
24862                 }
24863             },
24864             pfout = function() {
24865                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
24866
24867                 if (this.label) {
24868                     this.label[0].animate({ r: 5 }, 500, "bounce");
24869                     this.label[1].attr({ "font-weight": 400 });
24870                 }
24871             };
24872
24873         switch(graphtype){
24874             case 'bar':
24875                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24876                 break;
24877             case 'hbar':
24878                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24879                 break;
24880             case 'pie':
24881 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
24882 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
24883 //            
24884                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
24885                 
24886                 break;
24887
24888         }
24889         
24890         if(this.title){
24891             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
24892         }
24893         
24894     },
24895     
24896     setTitle: function(o)
24897     {
24898         this.title = o;
24899     },
24900     
24901     initEvents: function() {
24902         
24903         if(!this.href){
24904             this.el.on('click', this.onClick, this);
24905         }
24906     },
24907     
24908     onClick : function(e)
24909     {
24910         Roo.log('img onclick');
24911         this.fireEvent('click', this, e);
24912     }
24913    
24914 });
24915
24916  
24917 /*
24918  * - LGPL
24919  *
24920  * numberBox
24921  * 
24922  */
24923 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24924
24925 /**
24926  * @class Roo.bootstrap.dash.NumberBox
24927  * @extends Roo.bootstrap.Component
24928  * Bootstrap NumberBox class
24929  * @cfg {String} headline Box headline
24930  * @cfg {String} content Box content
24931  * @cfg {String} icon Box icon
24932  * @cfg {String} footer Footer text
24933  * @cfg {String} fhref Footer href
24934  * 
24935  * @constructor
24936  * Create a new NumberBox
24937  * @param {Object} config The config object
24938  */
24939
24940
24941 Roo.bootstrap.dash.NumberBox = function(config){
24942     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
24943     
24944 };
24945
24946 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
24947     
24948     headline : '',
24949     content : '',
24950     icon : '',
24951     footer : '',
24952     fhref : '',
24953     ficon : '',
24954     
24955     getAutoCreate : function(){
24956         
24957         var cfg = {
24958             tag : 'div',
24959             cls : 'small-box ',
24960             cn : [
24961                 {
24962                     tag : 'div',
24963                     cls : 'inner',
24964                     cn :[
24965                         {
24966                             tag : 'h3',
24967                             cls : 'roo-headline',
24968                             html : this.headline
24969                         },
24970                         {
24971                             tag : 'p',
24972                             cls : 'roo-content',
24973                             html : this.content
24974                         }
24975                     ]
24976                 }
24977             ]
24978         };
24979         
24980         if(this.icon){
24981             cfg.cn.push({
24982                 tag : 'div',
24983                 cls : 'icon',
24984                 cn :[
24985                     {
24986                         tag : 'i',
24987                         cls : 'ion ' + this.icon
24988                     }
24989                 ]
24990             });
24991         }
24992         
24993         if(this.footer){
24994             var footer = {
24995                 tag : 'a',
24996                 cls : 'small-box-footer',
24997                 href : this.fhref || '#',
24998                 html : this.footer
24999             };
25000             
25001             cfg.cn.push(footer);
25002             
25003         }
25004         
25005         return  cfg;
25006     },
25007
25008     onRender : function(ct,position){
25009         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25010
25011
25012        
25013                 
25014     },
25015
25016     setHeadline: function (value)
25017     {
25018         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25019     },
25020     
25021     setFooter: function (value, href)
25022     {
25023         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25024         
25025         if(href){
25026             this.el.select('a.small-box-footer',true).first().attr('href', href);
25027         }
25028         
25029     },
25030
25031     setContent: function (value)
25032     {
25033         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25034     },
25035
25036     initEvents: function() 
25037     {   
25038         
25039     }
25040     
25041 });
25042
25043  
25044 /*
25045  * - LGPL
25046  *
25047  * TabBox
25048  * 
25049  */
25050 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25051
25052 /**
25053  * @class Roo.bootstrap.dash.TabBox
25054  * @extends Roo.bootstrap.Component
25055  * Bootstrap TabBox class
25056  * @cfg {String} title Title of the TabBox
25057  * @cfg {String} icon Icon of the TabBox
25058  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25059  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25060  * 
25061  * @constructor
25062  * Create a new TabBox
25063  * @param {Object} config The config object
25064  */
25065
25066
25067 Roo.bootstrap.dash.TabBox = function(config){
25068     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25069     this.addEvents({
25070         // raw events
25071         /**
25072          * @event addpane
25073          * When a pane is added
25074          * @param {Roo.bootstrap.dash.TabPane} pane
25075          */
25076         "addpane" : true,
25077         /**
25078          * @event activatepane
25079          * When a pane is activated
25080          * @param {Roo.bootstrap.dash.TabPane} pane
25081          */
25082         "activatepane" : true
25083         
25084          
25085     });
25086     
25087     this.panes = [];
25088 };
25089
25090 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25091
25092     title : '',
25093     icon : false,
25094     showtabs : true,
25095     tabScrollable : false,
25096     
25097     getChildContainer : function()
25098     {
25099         return this.el.select('.tab-content', true).first();
25100     },
25101     
25102     getAutoCreate : function(){
25103         
25104         var header = {
25105             tag: 'li',
25106             cls: 'pull-left header',
25107             html: this.title,
25108             cn : []
25109         };
25110         
25111         if(this.icon){
25112             header.cn.push({
25113                 tag: 'i',
25114                 cls: 'fa ' + this.icon
25115             });
25116         }
25117         
25118         var h = {
25119             tag: 'ul',
25120             cls: 'nav nav-tabs pull-right',
25121             cn: [
25122                 header
25123             ]
25124         };
25125         
25126         if(this.tabScrollable){
25127             h = {
25128                 tag: 'div',
25129                 cls: 'tab-header',
25130                 cn: [
25131                     {
25132                         tag: 'ul',
25133                         cls: 'nav nav-tabs pull-right',
25134                         cn: [
25135                             header
25136                         ]
25137                     }
25138                 ]
25139             };
25140         }
25141         
25142         var cfg = {
25143             tag: 'div',
25144             cls: 'nav-tabs-custom',
25145             cn: [
25146                 h,
25147                 {
25148                     tag: 'div',
25149                     cls: 'tab-content no-padding',
25150                     cn: []
25151                 }
25152             ]
25153         };
25154
25155         return  cfg;
25156     },
25157     initEvents : function()
25158     {
25159         //Roo.log('add add pane handler');
25160         this.on('addpane', this.onAddPane, this);
25161     },
25162      /**
25163      * Updates the box title
25164      * @param {String} html to set the title to.
25165      */
25166     setTitle : function(value)
25167     {
25168         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25169     },
25170     onAddPane : function(pane)
25171     {
25172         this.panes.push(pane);
25173         //Roo.log('addpane');
25174         //Roo.log(pane);
25175         // tabs are rendere left to right..
25176         if(!this.showtabs){
25177             return;
25178         }
25179         
25180         var ctr = this.el.select('.nav-tabs', true).first();
25181          
25182          
25183         var existing = ctr.select('.nav-tab',true);
25184         var qty = existing.getCount();;
25185         
25186         
25187         var tab = ctr.createChild({
25188             tag : 'li',
25189             cls : 'nav-tab' + (qty ? '' : ' active'),
25190             cn : [
25191                 {
25192                     tag : 'a',
25193                     href:'#',
25194                     html : pane.title
25195                 }
25196             ]
25197         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25198         pane.tab = tab;
25199         
25200         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25201         if (!qty) {
25202             pane.el.addClass('active');
25203         }
25204         
25205                 
25206     },
25207     onTabClick : function(ev,un,ob,pane)
25208     {
25209         //Roo.log('tab - prev default');
25210         ev.preventDefault();
25211         
25212         
25213         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25214         pane.tab.addClass('active');
25215         //Roo.log(pane.title);
25216         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25217         // technically we should have a deactivate event.. but maybe add later.
25218         // and it should not de-activate the selected tab...
25219         this.fireEvent('activatepane', pane);
25220         pane.el.addClass('active');
25221         pane.fireEvent('activate');
25222         
25223         
25224     },
25225     
25226     getActivePane : function()
25227     {
25228         var r = false;
25229         Roo.each(this.panes, function(p) {
25230             if(p.el.hasClass('active')){
25231                 r = p;
25232                 return false;
25233             }
25234             
25235             return;
25236         });
25237         
25238         return r;
25239     }
25240     
25241     
25242 });
25243
25244  
25245 /*
25246  * - LGPL
25247  *
25248  * Tab pane
25249  * 
25250  */
25251 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25252 /**
25253  * @class Roo.bootstrap.TabPane
25254  * @extends Roo.bootstrap.Component
25255  * Bootstrap TabPane class
25256  * @cfg {Boolean} active (false | true) Default false
25257  * @cfg {String} title title of panel
25258
25259  * 
25260  * @constructor
25261  * Create a new TabPane
25262  * @param {Object} config The config object
25263  */
25264
25265 Roo.bootstrap.dash.TabPane = function(config){
25266     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25267     
25268     this.addEvents({
25269         // raw events
25270         /**
25271          * @event activate
25272          * When a pane is activated
25273          * @param {Roo.bootstrap.dash.TabPane} pane
25274          */
25275         "activate" : true
25276          
25277     });
25278 };
25279
25280 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25281     
25282     active : false,
25283     title : '',
25284     
25285     // the tabBox that this is attached to.
25286     tab : false,
25287      
25288     getAutoCreate : function() 
25289     {
25290         var cfg = {
25291             tag: 'div',
25292             cls: 'tab-pane'
25293         };
25294         
25295         if(this.active){
25296             cfg.cls += ' active';
25297         }
25298         
25299         return cfg;
25300     },
25301     initEvents  : function()
25302     {
25303         //Roo.log('trigger add pane handler');
25304         this.parent().fireEvent('addpane', this)
25305     },
25306     
25307      /**
25308      * Updates the tab title 
25309      * @param {String} html to set the title to.
25310      */
25311     setTitle: function(str)
25312     {
25313         if (!this.tab) {
25314             return;
25315         }
25316         this.title = str;
25317         this.tab.select('a', true).first().dom.innerHTML = str;
25318         
25319     }
25320     
25321     
25322     
25323 });
25324
25325  
25326
25327
25328  /*
25329  * - LGPL
25330  *
25331  * menu
25332  * 
25333  */
25334 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25335
25336 /**
25337  * @class Roo.bootstrap.menu.Menu
25338  * @extends Roo.bootstrap.Component
25339  * Bootstrap Menu class - container for Menu
25340  * @cfg {String} html Text of the menu
25341  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25342  * @cfg {String} icon Font awesome icon
25343  * @cfg {String} pos Menu align to (top | bottom) default bottom
25344  * 
25345  * 
25346  * @constructor
25347  * Create a new Menu
25348  * @param {Object} config The config object
25349  */
25350
25351
25352 Roo.bootstrap.menu.Menu = function(config){
25353     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25354     
25355     this.addEvents({
25356         /**
25357          * @event beforeshow
25358          * Fires before this menu is displayed
25359          * @param {Roo.bootstrap.menu.Menu} this
25360          */
25361         beforeshow : true,
25362         /**
25363          * @event beforehide
25364          * Fires before this menu is hidden
25365          * @param {Roo.bootstrap.menu.Menu} this
25366          */
25367         beforehide : true,
25368         /**
25369          * @event show
25370          * Fires after this menu is displayed
25371          * @param {Roo.bootstrap.menu.Menu} this
25372          */
25373         show : true,
25374         /**
25375          * @event hide
25376          * Fires after this menu is hidden
25377          * @param {Roo.bootstrap.menu.Menu} this
25378          */
25379         hide : true,
25380         /**
25381          * @event click
25382          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25383          * @param {Roo.bootstrap.menu.Menu} this
25384          * @param {Roo.EventObject} e
25385          */
25386         click : true
25387     });
25388     
25389 };
25390
25391 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25392     
25393     submenu : false,
25394     html : '',
25395     weight : 'default',
25396     icon : false,
25397     pos : 'bottom',
25398     
25399     
25400     getChildContainer : function() {
25401         if(this.isSubMenu){
25402             return this.el;
25403         }
25404         
25405         return this.el.select('ul.dropdown-menu', true).first();  
25406     },
25407     
25408     getAutoCreate : function()
25409     {
25410         var text = [
25411             {
25412                 tag : 'span',
25413                 cls : 'roo-menu-text',
25414                 html : this.html
25415             }
25416         ];
25417         
25418         if(this.icon){
25419             text.unshift({
25420                 tag : 'i',
25421                 cls : 'fa ' + this.icon
25422             })
25423         }
25424         
25425         
25426         var cfg = {
25427             tag : 'div',
25428             cls : 'btn-group',
25429             cn : [
25430                 {
25431                     tag : 'button',
25432                     cls : 'dropdown-button btn btn-' + this.weight,
25433                     cn : text
25434                 },
25435                 {
25436                     tag : 'button',
25437                     cls : 'dropdown-toggle btn btn-' + this.weight,
25438                     cn : [
25439                         {
25440                             tag : 'span',
25441                             cls : 'caret'
25442                         }
25443                     ]
25444                 },
25445                 {
25446                     tag : 'ul',
25447                     cls : 'dropdown-menu'
25448                 }
25449             ]
25450             
25451         };
25452         
25453         if(this.pos == 'top'){
25454             cfg.cls += ' dropup';
25455         }
25456         
25457         if(this.isSubMenu){
25458             cfg = {
25459                 tag : 'ul',
25460                 cls : 'dropdown-menu'
25461             }
25462         }
25463         
25464         return cfg;
25465     },
25466     
25467     onRender : function(ct, position)
25468     {
25469         this.isSubMenu = ct.hasClass('dropdown-submenu');
25470         
25471         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25472     },
25473     
25474     initEvents : function() 
25475     {
25476         if(this.isSubMenu){
25477             return;
25478         }
25479         
25480         this.hidden = true;
25481         
25482         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25483         this.triggerEl.on('click', this.onTriggerPress, this);
25484         
25485         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25486         this.buttonEl.on('click', this.onClick, this);
25487         
25488     },
25489     
25490     list : function()
25491     {
25492         if(this.isSubMenu){
25493             return this.el;
25494         }
25495         
25496         return this.el.select('ul.dropdown-menu', true).first();
25497     },
25498     
25499     onClick : function(e)
25500     {
25501         this.fireEvent("click", this, e);
25502     },
25503     
25504     onTriggerPress  : function(e)
25505     {   
25506         if (this.isVisible()) {
25507             this.hide();
25508         } else {
25509             this.show();
25510         }
25511     },
25512     
25513     isVisible : function(){
25514         return !this.hidden;
25515     },
25516     
25517     show : function()
25518     {
25519         this.fireEvent("beforeshow", this);
25520         
25521         this.hidden = false;
25522         this.el.addClass('open');
25523         
25524         Roo.get(document).on("mouseup", this.onMouseUp, this);
25525         
25526         this.fireEvent("show", this);
25527         
25528         
25529     },
25530     
25531     hide : function()
25532     {
25533         this.fireEvent("beforehide", this);
25534         
25535         this.hidden = true;
25536         this.el.removeClass('open');
25537         
25538         Roo.get(document).un("mouseup", this.onMouseUp);
25539         
25540         this.fireEvent("hide", this);
25541     },
25542     
25543     onMouseUp : function()
25544     {
25545         this.hide();
25546     }
25547     
25548 });
25549
25550  
25551  /*
25552  * - LGPL
25553  *
25554  * menu item
25555  * 
25556  */
25557 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25558
25559 /**
25560  * @class Roo.bootstrap.menu.Item
25561  * @extends Roo.bootstrap.Component
25562  * Bootstrap MenuItem class
25563  * @cfg {Boolean} submenu (true | false) default false
25564  * @cfg {String} html text of the item
25565  * @cfg {String} href the link
25566  * @cfg {Boolean} disable (true | false) default false
25567  * @cfg {Boolean} preventDefault (true | false) default true
25568  * @cfg {String} icon Font awesome icon
25569  * @cfg {String} pos Submenu align to (left | right) default right 
25570  * 
25571  * 
25572  * @constructor
25573  * Create a new Item
25574  * @param {Object} config The config object
25575  */
25576
25577
25578 Roo.bootstrap.menu.Item = function(config){
25579     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25580     this.addEvents({
25581         /**
25582          * @event mouseover
25583          * Fires when the mouse is hovering over this menu
25584          * @param {Roo.bootstrap.menu.Item} this
25585          * @param {Roo.EventObject} e
25586          */
25587         mouseover : true,
25588         /**
25589          * @event mouseout
25590          * Fires when the mouse exits this menu
25591          * @param {Roo.bootstrap.menu.Item} this
25592          * @param {Roo.EventObject} e
25593          */
25594         mouseout : true,
25595         // raw events
25596         /**
25597          * @event click
25598          * The raw click event for the entire grid.
25599          * @param {Roo.EventObject} e
25600          */
25601         click : true
25602     });
25603 };
25604
25605 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25606     
25607     submenu : false,
25608     href : '',
25609     html : '',
25610     preventDefault: true,
25611     disable : false,
25612     icon : false,
25613     pos : 'right',
25614     
25615     getAutoCreate : function()
25616     {
25617         var text = [
25618             {
25619                 tag : 'span',
25620                 cls : 'roo-menu-item-text',
25621                 html : this.html
25622             }
25623         ];
25624         
25625         if(this.icon){
25626             text.unshift({
25627                 tag : 'i',
25628                 cls : 'fa ' + this.icon
25629             })
25630         }
25631         
25632         var cfg = {
25633             tag : 'li',
25634             cn : [
25635                 {
25636                     tag : 'a',
25637                     href : this.href || '#',
25638                     cn : text
25639                 }
25640             ]
25641         };
25642         
25643         if(this.disable){
25644             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25645         }
25646         
25647         if(this.submenu){
25648             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25649             
25650             if(this.pos == 'left'){
25651                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25652             }
25653         }
25654         
25655         return cfg;
25656     },
25657     
25658     initEvents : function() 
25659     {
25660         this.el.on('mouseover', this.onMouseOver, this);
25661         this.el.on('mouseout', this.onMouseOut, this);
25662         
25663         this.el.select('a', true).first().on('click', this.onClick, this);
25664         
25665     },
25666     
25667     onClick : function(e)
25668     {
25669         if(this.preventDefault){
25670             e.preventDefault();
25671         }
25672         
25673         this.fireEvent("click", this, e);
25674     },
25675     
25676     onMouseOver : function(e)
25677     {
25678         if(this.submenu && this.pos == 'left'){
25679             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25680         }
25681         
25682         this.fireEvent("mouseover", this, e);
25683     },
25684     
25685     onMouseOut : function(e)
25686     {
25687         this.fireEvent("mouseout", this, e);
25688     }
25689 });
25690
25691  
25692
25693  /*
25694  * - LGPL
25695  *
25696  * menu separator
25697  * 
25698  */
25699 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25700
25701 /**
25702  * @class Roo.bootstrap.menu.Separator
25703  * @extends Roo.bootstrap.Component
25704  * Bootstrap Separator class
25705  * 
25706  * @constructor
25707  * Create a new Separator
25708  * @param {Object} config The config object
25709  */
25710
25711
25712 Roo.bootstrap.menu.Separator = function(config){
25713     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25714 };
25715
25716 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
25717     
25718     getAutoCreate : function(){
25719         var cfg = {
25720             tag : 'li',
25721             cls: 'divider'
25722         };
25723         
25724         return cfg;
25725     }
25726    
25727 });
25728
25729  
25730
25731  /*
25732  * - LGPL
25733  *
25734  * Tooltip
25735  * 
25736  */
25737
25738 /**
25739  * @class Roo.bootstrap.Tooltip
25740  * Bootstrap Tooltip class
25741  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25742  * to determine which dom element triggers the tooltip.
25743  * 
25744  * It needs to add support for additional attributes like tooltip-position
25745  * 
25746  * @constructor
25747  * Create a new Toolti
25748  * @param {Object} config The config object
25749  */
25750
25751 Roo.bootstrap.Tooltip = function(config){
25752     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25753     
25754     this.alignment = Roo.bootstrap.Tooltip.alignment;
25755     
25756     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25757         this.alignment = config.alignment;
25758     }
25759     
25760 };
25761
25762 Roo.apply(Roo.bootstrap.Tooltip, {
25763     /**
25764      * @function init initialize tooltip monitoring.
25765      * @static
25766      */
25767     currentEl : false,
25768     currentTip : false,
25769     currentRegion : false,
25770     
25771     //  init : delay?
25772     
25773     init : function()
25774     {
25775         Roo.get(document).on('mouseover', this.enter ,this);
25776         Roo.get(document).on('mouseout', this.leave, this);
25777          
25778         
25779         this.currentTip = new Roo.bootstrap.Tooltip();
25780     },
25781     
25782     enter : function(ev)
25783     {
25784         var dom = ev.getTarget();
25785         
25786         //Roo.log(['enter',dom]);
25787         var el = Roo.fly(dom);
25788         if (this.currentEl) {
25789             //Roo.log(dom);
25790             //Roo.log(this.currentEl);
25791             //Roo.log(this.currentEl.contains(dom));
25792             if (this.currentEl == el) {
25793                 return;
25794             }
25795             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25796                 return;
25797             }
25798
25799         }
25800         
25801         if (this.currentTip.el) {
25802             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25803         }    
25804         //Roo.log(ev);
25805         
25806         if(!el || el.dom == document){
25807             return;
25808         }
25809         
25810         var bindEl = el;
25811         
25812         // you can not look for children, as if el is the body.. then everythign is the child..
25813         if (!el.attr('tooltip')) { //
25814             if (!el.select("[tooltip]").elements.length) {
25815                 return;
25816             }
25817             // is the mouse over this child...?
25818             bindEl = el.select("[tooltip]").first();
25819             var xy = ev.getXY();
25820             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
25821                 //Roo.log("not in region.");
25822                 return;
25823             }
25824             //Roo.log("child element over..");
25825             
25826         }
25827         this.currentEl = bindEl;
25828         this.currentTip.bind(bindEl);
25829         this.currentRegion = Roo.lib.Region.getRegion(dom);
25830         this.currentTip.enter();
25831         
25832     },
25833     leave : function(ev)
25834     {
25835         var dom = ev.getTarget();
25836         //Roo.log(['leave',dom]);
25837         if (!this.currentEl) {
25838             return;
25839         }
25840         
25841         
25842         if (dom != this.currentEl.dom) {
25843             return;
25844         }
25845         var xy = ev.getXY();
25846         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
25847             return;
25848         }
25849         // only activate leave if mouse cursor is outside... bounding box..
25850         
25851         
25852         
25853         
25854         if (this.currentTip) {
25855             this.currentTip.leave();
25856         }
25857         //Roo.log('clear currentEl');
25858         this.currentEl = false;
25859         
25860         
25861     },
25862     alignment : {
25863         'left' : ['r-l', [-2,0], 'right'],
25864         'right' : ['l-r', [2,0], 'left'],
25865         'bottom' : ['t-b', [0,2], 'top'],
25866         'top' : [ 'b-t', [0,-2], 'bottom']
25867     }
25868     
25869 });
25870
25871
25872 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
25873     
25874     
25875     bindEl : false,
25876     
25877     delay : null, // can be { show : 300 , hide: 500}
25878     
25879     timeout : null,
25880     
25881     hoverState : null, //???
25882     
25883     placement : 'bottom', 
25884     
25885     alignment : false,
25886     
25887     getAutoCreate : function(){
25888     
25889         var cfg = {
25890            cls : 'tooltip',
25891            role : 'tooltip',
25892            cn : [
25893                 {
25894                     cls : 'tooltip-arrow'
25895                 },
25896                 {
25897                     cls : 'tooltip-inner'
25898                 }
25899            ]
25900         };
25901         
25902         return cfg;
25903     },
25904     bind : function(el)
25905     {
25906         this.bindEl = el;
25907     },
25908       
25909     
25910     enter : function () {
25911        
25912         if (this.timeout != null) {
25913             clearTimeout(this.timeout);
25914         }
25915         
25916         this.hoverState = 'in';
25917          //Roo.log("enter - show");
25918         if (!this.delay || !this.delay.show) {
25919             this.show();
25920             return;
25921         }
25922         var _t = this;
25923         this.timeout = setTimeout(function () {
25924             if (_t.hoverState == 'in') {
25925                 _t.show();
25926             }
25927         }, this.delay.show);
25928     },
25929     leave : function()
25930     {
25931         clearTimeout(this.timeout);
25932     
25933         this.hoverState = 'out';
25934          if (!this.delay || !this.delay.hide) {
25935             this.hide();
25936             return;
25937         }
25938        
25939         var _t = this;
25940         this.timeout = setTimeout(function () {
25941             //Roo.log("leave - timeout");
25942             
25943             if (_t.hoverState == 'out') {
25944                 _t.hide();
25945                 Roo.bootstrap.Tooltip.currentEl = false;
25946             }
25947         }, delay);
25948     },
25949     
25950     show : function (msg)
25951     {
25952         if (!this.el) {
25953             this.render(document.body);
25954         }
25955         // set content.
25956         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
25957         
25958         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
25959         
25960         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
25961         
25962         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
25963         
25964         var placement = typeof this.placement == 'function' ?
25965             this.placement.call(this, this.el, on_el) :
25966             this.placement;
25967             
25968         var autoToken = /\s?auto?\s?/i;
25969         var autoPlace = autoToken.test(placement);
25970         if (autoPlace) {
25971             placement = placement.replace(autoToken, '') || 'top';
25972         }
25973         
25974         //this.el.detach()
25975         //this.el.setXY([0,0]);
25976         this.el.show();
25977         //this.el.dom.style.display='block';
25978         
25979         //this.el.appendTo(on_el);
25980         
25981         var p = this.getPosition();
25982         var box = this.el.getBox();
25983         
25984         if (autoPlace) {
25985             // fixme..
25986         }
25987         
25988         var align = this.alignment[placement];
25989         
25990         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
25991         
25992         if(placement == 'top' || placement == 'bottom'){
25993             if(xy[0] < 0){
25994                 placement = 'right';
25995             }
25996             
25997             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
25998                 placement = 'left';
25999             }
26000             
26001             var scroll = Roo.select('body', true).first().getScroll();
26002             
26003             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26004                 placement = 'top';
26005             }
26006             
26007         }
26008         
26009         this.el.alignTo(this.bindEl, align[0],align[1]);
26010         //var arrow = this.el.select('.arrow',true).first();
26011         //arrow.set(align[2], 
26012         
26013         this.el.addClass(placement);
26014         
26015         this.el.addClass('in fade');
26016         
26017         this.hoverState = null;
26018         
26019         if (this.el.hasClass('fade')) {
26020             // fade it?
26021         }
26022         
26023     },
26024     hide : function()
26025     {
26026          
26027         if (!this.el) {
26028             return;
26029         }
26030         //this.el.setXY([0,0]);
26031         this.el.removeClass('in');
26032         //this.el.hide();
26033         
26034     }
26035     
26036 });
26037  
26038
26039  /*
26040  * - LGPL
26041  *
26042  * Location Picker
26043  * 
26044  */
26045
26046 /**
26047  * @class Roo.bootstrap.LocationPicker
26048  * @extends Roo.bootstrap.Component
26049  * Bootstrap LocationPicker class
26050  * @cfg {Number} latitude Position when init default 0
26051  * @cfg {Number} longitude Position when init default 0
26052  * @cfg {Number} zoom default 15
26053  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26054  * @cfg {Boolean} mapTypeControl default false
26055  * @cfg {Boolean} disableDoubleClickZoom default false
26056  * @cfg {Boolean} scrollwheel default true
26057  * @cfg {Boolean} streetViewControl default false
26058  * @cfg {Number} radius default 0
26059  * @cfg {String} locationName
26060  * @cfg {Boolean} draggable default true
26061  * @cfg {Boolean} enableAutocomplete default false
26062  * @cfg {Boolean} enableReverseGeocode default true
26063  * @cfg {String} markerTitle
26064  * 
26065  * @constructor
26066  * Create a new LocationPicker
26067  * @param {Object} config The config object
26068  */
26069
26070
26071 Roo.bootstrap.LocationPicker = function(config){
26072     
26073     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26074     
26075     this.addEvents({
26076         /**
26077          * @event initial
26078          * Fires when the picker initialized.
26079          * @param {Roo.bootstrap.LocationPicker} this
26080          * @param {Google Location} location
26081          */
26082         initial : true,
26083         /**
26084          * @event positionchanged
26085          * Fires when the picker position changed.
26086          * @param {Roo.bootstrap.LocationPicker} this
26087          * @param {Google Location} location
26088          */
26089         positionchanged : true,
26090         /**
26091          * @event resize
26092          * Fires when the map resize.
26093          * @param {Roo.bootstrap.LocationPicker} this
26094          */
26095         resize : true,
26096         /**
26097          * @event show
26098          * Fires when the map show.
26099          * @param {Roo.bootstrap.LocationPicker} this
26100          */
26101         show : true,
26102         /**
26103          * @event hide
26104          * Fires when the map hide.
26105          * @param {Roo.bootstrap.LocationPicker} this
26106          */
26107         hide : true,
26108         /**
26109          * @event mapClick
26110          * Fires when click the map.
26111          * @param {Roo.bootstrap.LocationPicker} this
26112          * @param {Map event} e
26113          */
26114         mapClick : true,
26115         /**
26116          * @event mapRightClick
26117          * Fires when right click the map.
26118          * @param {Roo.bootstrap.LocationPicker} this
26119          * @param {Map event} e
26120          */
26121         mapRightClick : true,
26122         /**
26123          * @event markerClick
26124          * Fires when click the marker.
26125          * @param {Roo.bootstrap.LocationPicker} this
26126          * @param {Map event} e
26127          */
26128         markerClick : true,
26129         /**
26130          * @event markerRightClick
26131          * Fires when right click the marker.
26132          * @param {Roo.bootstrap.LocationPicker} this
26133          * @param {Map event} e
26134          */
26135         markerRightClick : true,
26136         /**
26137          * @event OverlayViewDraw
26138          * Fires when OverlayView Draw
26139          * @param {Roo.bootstrap.LocationPicker} this
26140          */
26141         OverlayViewDraw : true,
26142         /**
26143          * @event OverlayViewOnAdd
26144          * Fires when OverlayView Draw
26145          * @param {Roo.bootstrap.LocationPicker} this
26146          */
26147         OverlayViewOnAdd : true,
26148         /**
26149          * @event OverlayViewOnRemove
26150          * Fires when OverlayView Draw
26151          * @param {Roo.bootstrap.LocationPicker} this
26152          */
26153         OverlayViewOnRemove : true,
26154         /**
26155          * @event OverlayViewShow
26156          * Fires when OverlayView Draw
26157          * @param {Roo.bootstrap.LocationPicker} this
26158          * @param {Pixel} cpx
26159          */
26160         OverlayViewShow : true,
26161         /**
26162          * @event OverlayViewHide
26163          * Fires when OverlayView Draw
26164          * @param {Roo.bootstrap.LocationPicker} this
26165          */
26166         OverlayViewHide : true,
26167         /**
26168          * @event loadexception
26169          * Fires when load google lib failed.
26170          * @param {Roo.bootstrap.LocationPicker} this
26171          */
26172         loadexception : true
26173     });
26174         
26175 };
26176
26177 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26178     
26179     gMapContext: false,
26180     
26181     latitude: 0,
26182     longitude: 0,
26183     zoom: 15,
26184     mapTypeId: false,
26185     mapTypeControl: false,
26186     disableDoubleClickZoom: false,
26187     scrollwheel: true,
26188     streetViewControl: false,
26189     radius: 0,
26190     locationName: '',
26191     draggable: true,
26192     enableAutocomplete: false,
26193     enableReverseGeocode: true,
26194     markerTitle: '',
26195     
26196     getAutoCreate: function()
26197     {
26198
26199         var cfg = {
26200             tag: 'div',
26201             cls: 'roo-location-picker'
26202         };
26203         
26204         return cfg
26205     },
26206     
26207     initEvents: function(ct, position)
26208     {       
26209         if(!this.el.getWidth() || this.isApplied()){
26210             return;
26211         }
26212         
26213         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26214         
26215         this.initial();
26216     },
26217     
26218     initial: function()
26219     {
26220         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26221             this.fireEvent('loadexception', this);
26222             return;
26223         }
26224         
26225         if(!this.mapTypeId){
26226             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26227         }
26228         
26229         this.gMapContext = this.GMapContext();
26230         
26231         this.initOverlayView();
26232         
26233         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26234         
26235         var _this = this;
26236                 
26237         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26238             _this.setPosition(_this.gMapContext.marker.position);
26239         });
26240         
26241         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26242             _this.fireEvent('mapClick', this, event);
26243             
26244         });
26245
26246         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26247             _this.fireEvent('mapRightClick', this, event);
26248             
26249         });
26250         
26251         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26252             _this.fireEvent('markerClick', this, event);
26253             
26254         });
26255
26256         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26257             _this.fireEvent('markerRightClick', this, event);
26258             
26259         });
26260         
26261         this.setPosition(this.gMapContext.location);
26262         
26263         this.fireEvent('initial', this, this.gMapContext.location);
26264     },
26265     
26266     initOverlayView: function()
26267     {
26268         var _this = this;
26269         
26270         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26271             
26272             draw: function()
26273             {
26274                 _this.fireEvent('OverlayViewDraw', _this);
26275             },
26276             
26277             onAdd: function()
26278             {
26279                 _this.fireEvent('OverlayViewOnAdd', _this);
26280             },
26281             
26282             onRemove: function()
26283             {
26284                 _this.fireEvent('OverlayViewOnRemove', _this);
26285             },
26286             
26287             show: function(cpx)
26288             {
26289                 _this.fireEvent('OverlayViewShow', _this, cpx);
26290             },
26291             
26292             hide: function()
26293             {
26294                 _this.fireEvent('OverlayViewHide', _this);
26295             }
26296             
26297         });
26298     },
26299     
26300     fromLatLngToContainerPixel: function(event)
26301     {
26302         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26303     },
26304     
26305     isApplied: function() 
26306     {
26307         return this.getGmapContext() == false ? false : true;
26308     },
26309     
26310     getGmapContext: function() 
26311     {
26312         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26313     },
26314     
26315     GMapContext: function() 
26316     {
26317         var position = new google.maps.LatLng(this.latitude, this.longitude);
26318         
26319         var _map = new google.maps.Map(this.el.dom, {
26320             center: position,
26321             zoom: this.zoom,
26322             mapTypeId: this.mapTypeId,
26323             mapTypeControl: this.mapTypeControl,
26324             disableDoubleClickZoom: this.disableDoubleClickZoom,
26325             scrollwheel: this.scrollwheel,
26326             streetViewControl: this.streetViewControl,
26327             locationName: this.locationName,
26328             draggable: this.draggable,
26329             enableAutocomplete: this.enableAutocomplete,
26330             enableReverseGeocode: this.enableReverseGeocode
26331         });
26332         
26333         var _marker = new google.maps.Marker({
26334             position: position,
26335             map: _map,
26336             title: this.markerTitle,
26337             draggable: this.draggable
26338         });
26339         
26340         return {
26341             map: _map,
26342             marker: _marker,
26343             circle: null,
26344             location: position,
26345             radius: this.radius,
26346             locationName: this.locationName,
26347             addressComponents: {
26348                 formatted_address: null,
26349                 addressLine1: null,
26350                 addressLine2: null,
26351                 streetName: null,
26352                 streetNumber: null,
26353                 city: null,
26354                 district: null,
26355                 state: null,
26356                 stateOrProvince: null
26357             },
26358             settings: this,
26359             domContainer: this.el.dom,
26360             geodecoder: new google.maps.Geocoder()
26361         };
26362     },
26363     
26364     drawCircle: function(center, radius, options) 
26365     {
26366         if (this.gMapContext.circle != null) {
26367             this.gMapContext.circle.setMap(null);
26368         }
26369         if (radius > 0) {
26370             radius *= 1;
26371             options = Roo.apply({}, options, {
26372                 strokeColor: "#0000FF",
26373                 strokeOpacity: .35,
26374                 strokeWeight: 2,
26375                 fillColor: "#0000FF",
26376                 fillOpacity: .2
26377             });
26378             
26379             options.map = this.gMapContext.map;
26380             options.radius = radius;
26381             options.center = center;
26382             this.gMapContext.circle = new google.maps.Circle(options);
26383             return this.gMapContext.circle;
26384         }
26385         
26386         return null;
26387     },
26388     
26389     setPosition: function(location) 
26390     {
26391         this.gMapContext.location = location;
26392         this.gMapContext.marker.setPosition(location);
26393         this.gMapContext.map.panTo(location);
26394         this.drawCircle(location, this.gMapContext.radius, {});
26395         
26396         var _this = this;
26397         
26398         if (this.gMapContext.settings.enableReverseGeocode) {
26399             this.gMapContext.geodecoder.geocode({
26400                 latLng: this.gMapContext.location
26401             }, function(results, status) {
26402                 
26403                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26404                     _this.gMapContext.locationName = results[0].formatted_address;
26405                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26406                     
26407                     _this.fireEvent('positionchanged', this, location);
26408                 }
26409             });
26410             
26411             return;
26412         }
26413         
26414         this.fireEvent('positionchanged', this, location);
26415     },
26416     
26417     resize: function()
26418     {
26419         google.maps.event.trigger(this.gMapContext.map, "resize");
26420         
26421         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26422         
26423         this.fireEvent('resize', this);
26424     },
26425     
26426     setPositionByLatLng: function(latitude, longitude)
26427     {
26428         this.setPosition(new google.maps.LatLng(latitude, longitude));
26429     },
26430     
26431     getCurrentPosition: function() 
26432     {
26433         return {
26434             latitude: this.gMapContext.location.lat(),
26435             longitude: this.gMapContext.location.lng()
26436         };
26437     },
26438     
26439     getAddressName: function() 
26440     {
26441         return this.gMapContext.locationName;
26442     },
26443     
26444     getAddressComponents: function() 
26445     {
26446         return this.gMapContext.addressComponents;
26447     },
26448     
26449     address_component_from_google_geocode: function(address_components) 
26450     {
26451         var result = {};
26452         
26453         for (var i = 0; i < address_components.length; i++) {
26454             var component = address_components[i];
26455             if (component.types.indexOf("postal_code") >= 0) {
26456                 result.postalCode = component.short_name;
26457             } else if (component.types.indexOf("street_number") >= 0) {
26458                 result.streetNumber = component.short_name;
26459             } else if (component.types.indexOf("route") >= 0) {
26460                 result.streetName = component.short_name;
26461             } else if (component.types.indexOf("neighborhood") >= 0) {
26462                 result.city = component.short_name;
26463             } else if (component.types.indexOf("locality") >= 0) {
26464                 result.city = component.short_name;
26465             } else if (component.types.indexOf("sublocality") >= 0) {
26466                 result.district = component.short_name;
26467             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26468                 result.stateOrProvince = component.short_name;
26469             } else if (component.types.indexOf("country") >= 0) {
26470                 result.country = component.short_name;
26471             }
26472         }
26473         
26474         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26475         result.addressLine2 = "";
26476         return result;
26477     },
26478     
26479     setZoomLevel: function(zoom)
26480     {
26481         this.gMapContext.map.setZoom(zoom);
26482     },
26483     
26484     show: function()
26485     {
26486         if(!this.el){
26487             return;
26488         }
26489         
26490         this.el.show();
26491         
26492         this.resize();
26493         
26494         this.fireEvent('show', this);
26495     },
26496     
26497     hide: function()
26498     {
26499         if(!this.el){
26500             return;
26501         }
26502         
26503         this.el.hide();
26504         
26505         this.fireEvent('hide', this);
26506     }
26507     
26508 });
26509
26510 Roo.apply(Roo.bootstrap.LocationPicker, {
26511     
26512     OverlayView : function(map, options)
26513     {
26514         options = options || {};
26515         
26516         this.setMap(map);
26517     }
26518     
26519     
26520 });/*
26521  * - LGPL
26522  *
26523  * Alert
26524  * 
26525  */
26526
26527 /**
26528  * @class Roo.bootstrap.Alert
26529  * @extends Roo.bootstrap.Component
26530  * Bootstrap Alert class
26531  * @cfg {String} title The title of alert
26532  * @cfg {String} html The content of alert
26533  * @cfg {String} weight (  success | info | warning | danger )
26534  * @cfg {String} faicon font-awesomeicon
26535  * 
26536  * @constructor
26537  * Create a new alert
26538  * @param {Object} config The config object
26539  */
26540
26541
26542 Roo.bootstrap.Alert = function(config){
26543     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26544     
26545 };
26546
26547 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26548     
26549     title: '',
26550     html: '',
26551     weight: false,
26552     faicon: false,
26553     
26554     getAutoCreate : function()
26555     {
26556         
26557         var cfg = {
26558             tag : 'div',
26559             cls : 'alert',
26560             cn : [
26561                 {
26562                     tag : 'i',
26563                     cls : 'roo-alert-icon'
26564                     
26565                 },
26566                 {
26567                     tag : 'b',
26568                     cls : 'roo-alert-title',
26569                     html : this.title
26570                 },
26571                 {
26572                     tag : 'span',
26573                     cls : 'roo-alert-text',
26574                     html : this.html
26575                 }
26576             ]
26577         };
26578         
26579         if(this.faicon){
26580             cfg.cn[0].cls += ' fa ' + this.faicon;
26581         }
26582         
26583         if(this.weight){
26584             cfg.cls += ' alert-' + this.weight;
26585         }
26586         
26587         return cfg;
26588     },
26589     
26590     initEvents: function() 
26591     {
26592         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26593     },
26594     
26595     setTitle : function(str)
26596     {
26597         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26598     },
26599     
26600     setText : function(str)
26601     {
26602         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26603     },
26604     
26605     setWeight : function(weight)
26606     {
26607         if(this.weight){
26608             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26609         }
26610         
26611         this.weight = weight;
26612         
26613         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26614     },
26615     
26616     setIcon : function(icon)
26617     {
26618         if(this.faicon){
26619             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26620         }
26621         
26622         this.faicon = icon;
26623         
26624         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26625     },
26626     
26627     hide: function() 
26628     {
26629         this.el.hide();   
26630     },
26631     
26632     show: function() 
26633     {  
26634         this.el.show();   
26635     }
26636     
26637 });
26638
26639  
26640 /*
26641 * Licence: LGPL
26642 */
26643
26644 /**
26645  * @class Roo.bootstrap.UploadCropbox
26646  * @extends Roo.bootstrap.Component
26647  * Bootstrap UploadCropbox class
26648  * @cfg {String} emptyText show when image has been loaded
26649  * @cfg {String} rotateNotify show when image too small to rotate
26650  * @cfg {Number} errorTimeout default 3000
26651  * @cfg {Number} minWidth default 300
26652  * @cfg {Number} minHeight default 300
26653  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26654  * @cfg {Boolean} isDocument (true|false) default false
26655  * @cfg {String} url action url
26656  * @cfg {String} paramName default 'imageUpload'
26657  * @cfg {String} method default POST
26658  * @cfg {Boolean} loadMask (true|false) default true
26659  * @cfg {Boolean} loadingText default 'Loading...'
26660  * 
26661  * @constructor
26662  * Create a new UploadCropbox
26663  * @param {Object} config The config object
26664  */
26665
26666 Roo.bootstrap.UploadCropbox = function(config){
26667     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26668     
26669     this.addEvents({
26670         /**
26671          * @event beforeselectfile
26672          * Fire before select file
26673          * @param {Roo.bootstrap.UploadCropbox} this
26674          */
26675         "beforeselectfile" : true,
26676         /**
26677          * @event initial
26678          * Fire after initEvent
26679          * @param {Roo.bootstrap.UploadCropbox} this
26680          */
26681         "initial" : true,
26682         /**
26683          * @event crop
26684          * Fire after initEvent
26685          * @param {Roo.bootstrap.UploadCropbox} this
26686          * @param {String} data
26687          */
26688         "crop" : true,
26689         /**
26690          * @event prepare
26691          * Fire when preparing the file data
26692          * @param {Roo.bootstrap.UploadCropbox} this
26693          * @param {Object} file
26694          */
26695         "prepare" : true,
26696         /**
26697          * @event exception
26698          * Fire when get exception
26699          * @param {Roo.bootstrap.UploadCropbox} this
26700          * @param {XMLHttpRequest} xhr
26701          */
26702         "exception" : true,
26703         /**
26704          * @event beforeloadcanvas
26705          * Fire before load the canvas
26706          * @param {Roo.bootstrap.UploadCropbox} this
26707          * @param {String} src
26708          */
26709         "beforeloadcanvas" : true,
26710         /**
26711          * @event trash
26712          * Fire when trash image
26713          * @param {Roo.bootstrap.UploadCropbox} this
26714          */
26715         "trash" : true,
26716         /**
26717          * @event download
26718          * Fire when download the image
26719          * @param {Roo.bootstrap.UploadCropbox} this
26720          */
26721         "download" : true,
26722         /**
26723          * @event footerbuttonclick
26724          * Fire when footerbuttonclick
26725          * @param {Roo.bootstrap.UploadCropbox} this
26726          * @param {String} type
26727          */
26728         "footerbuttonclick" : true,
26729         /**
26730          * @event resize
26731          * Fire when resize
26732          * @param {Roo.bootstrap.UploadCropbox} this
26733          */
26734         "resize" : true,
26735         /**
26736          * @event rotate
26737          * Fire when rotate the image
26738          * @param {Roo.bootstrap.UploadCropbox} this
26739          * @param {String} pos
26740          */
26741         "rotate" : true,
26742         /**
26743          * @event inspect
26744          * Fire when inspect the file
26745          * @param {Roo.bootstrap.UploadCropbox} this
26746          * @param {Object} file
26747          */
26748         "inspect" : true,
26749         /**
26750          * @event upload
26751          * Fire when xhr upload the file
26752          * @param {Roo.bootstrap.UploadCropbox} this
26753          * @param {Object} data
26754          */
26755         "upload" : true,
26756         /**
26757          * @event arrange
26758          * Fire when arrange the file data
26759          * @param {Roo.bootstrap.UploadCropbox} this
26760          * @param {Object} formData
26761          */
26762         "arrange" : true
26763     });
26764     
26765     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26766 };
26767
26768 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
26769     
26770     emptyText : 'Click to upload image',
26771     rotateNotify : 'Image is too small to rotate',
26772     errorTimeout : 3000,
26773     scale : 0,
26774     baseScale : 1,
26775     rotate : 0,
26776     dragable : false,
26777     pinching : false,
26778     mouseX : 0,
26779     mouseY : 0,
26780     cropData : false,
26781     minWidth : 300,
26782     minHeight : 300,
26783     file : false,
26784     exif : {},
26785     baseRotate : 1,
26786     cropType : 'image/jpeg',
26787     buttons : false,
26788     canvasLoaded : false,
26789     isDocument : false,
26790     method : 'POST',
26791     paramName : 'imageUpload',
26792     loadMask : true,
26793     loadingText : 'Loading...',
26794     maskEl : false,
26795     
26796     getAutoCreate : function()
26797     {
26798         var cfg = {
26799             tag : 'div',
26800             cls : 'roo-upload-cropbox',
26801             cn : [
26802                 {
26803                     tag : 'input',
26804                     cls : 'roo-upload-cropbox-selector',
26805                     type : 'file'
26806                 },
26807                 {
26808                     tag : 'div',
26809                     cls : 'roo-upload-cropbox-body',
26810                     style : 'cursor:pointer',
26811                     cn : [
26812                         {
26813                             tag : 'div',
26814                             cls : 'roo-upload-cropbox-preview'
26815                         },
26816                         {
26817                             tag : 'div',
26818                             cls : 'roo-upload-cropbox-thumb'
26819                         },
26820                         {
26821                             tag : 'div',
26822                             cls : 'roo-upload-cropbox-empty-notify',
26823                             html : this.emptyText
26824                         },
26825                         {
26826                             tag : 'div',
26827                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
26828                             html : this.rotateNotify
26829                         }
26830                     ]
26831                 },
26832                 {
26833                     tag : 'div',
26834                     cls : 'roo-upload-cropbox-footer',
26835                     cn : {
26836                         tag : 'div',
26837                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
26838                         cn : []
26839                     }
26840                 }
26841             ]
26842         };
26843         
26844         return cfg;
26845     },
26846     
26847     onRender : function(ct, position)
26848     {
26849         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
26850         
26851         if (this.buttons.length) {
26852             
26853             Roo.each(this.buttons, function(bb) {
26854                 
26855                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
26856                 
26857                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
26858                 
26859             }, this);
26860         }
26861         
26862         if(this.loadMask){
26863             this.maskEl = this.el;
26864         }
26865     },
26866     
26867     initEvents : function()
26868     {
26869         this.urlAPI = (window.createObjectURL && window) || 
26870                                 (window.URL && URL.revokeObjectURL && URL) || 
26871                                 (window.webkitURL && webkitURL);
26872                         
26873         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
26874         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26875         
26876         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
26877         this.selectorEl.hide();
26878         
26879         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
26880         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26881         
26882         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
26883         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26884         this.thumbEl.hide();
26885         
26886         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
26887         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26888         
26889         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
26890         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26891         this.errorEl.hide();
26892         
26893         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
26894         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26895         this.footerEl.hide();
26896         
26897         this.setThumbBoxSize();
26898         
26899         this.bind();
26900         
26901         this.resize();
26902         
26903         this.fireEvent('initial', this);
26904     },
26905
26906     bind : function()
26907     {
26908         var _this = this;
26909         
26910         window.addEventListener("resize", function() { _this.resize(); } );
26911         
26912         this.bodyEl.on('click', this.beforeSelectFile, this);
26913         
26914         if(Roo.isTouch){
26915             this.bodyEl.on('touchstart', this.onTouchStart, this);
26916             this.bodyEl.on('touchmove', this.onTouchMove, this);
26917             this.bodyEl.on('touchend', this.onTouchEnd, this);
26918         }
26919         
26920         if(!Roo.isTouch){
26921             this.bodyEl.on('mousedown', this.onMouseDown, this);
26922             this.bodyEl.on('mousemove', this.onMouseMove, this);
26923             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
26924             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
26925             Roo.get(document).on('mouseup', this.onMouseUp, this);
26926         }
26927         
26928         this.selectorEl.on('change', this.onFileSelected, this);
26929     },
26930     
26931     reset : function()
26932     {    
26933         this.scale = 0;
26934         this.baseScale = 1;
26935         this.rotate = 0;
26936         this.baseRotate = 1;
26937         this.dragable = false;
26938         this.pinching = false;
26939         this.mouseX = 0;
26940         this.mouseY = 0;
26941         this.cropData = false;
26942         this.notifyEl.dom.innerHTML = this.emptyText;
26943         
26944         this.selectorEl.dom.value = '';
26945         
26946     },
26947     
26948     resize : function()
26949     {
26950         if(this.fireEvent('resize', this) != false){
26951             this.setThumbBoxPosition();
26952             this.setCanvasPosition();
26953         }
26954     },
26955     
26956     onFooterButtonClick : function(e, el, o, type)
26957     {
26958         switch (type) {
26959             case 'rotate-left' :
26960                 this.onRotateLeft(e);
26961                 break;
26962             case 'rotate-right' :
26963                 this.onRotateRight(e);
26964                 break;
26965             case 'picture' :
26966                 this.beforeSelectFile(e);
26967                 break;
26968             case 'trash' :
26969                 this.trash(e);
26970                 break;
26971             case 'crop' :
26972                 this.crop(e);
26973                 break;
26974             case 'download' :
26975                 this.download(e);
26976                 break;
26977             default :
26978                 break;
26979         }
26980         
26981         this.fireEvent('footerbuttonclick', this, type);
26982     },
26983     
26984     beforeSelectFile : function(e)
26985     {
26986         e.preventDefault();
26987         
26988         if(this.fireEvent('beforeselectfile', this) != false){
26989             this.selectorEl.dom.click();
26990         }
26991     },
26992     
26993     onFileSelected : function(e)
26994     {
26995         e.preventDefault();
26996         
26997         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26998             return;
26999         }
27000         
27001         var file = this.selectorEl.dom.files[0];
27002         
27003         if(this.fireEvent('inspect', this, file) != false){
27004             this.prepare(file);
27005         }
27006         
27007     },
27008     
27009     trash : function(e)
27010     {
27011         this.fireEvent('trash', this);
27012     },
27013     
27014     download : function(e)
27015     {
27016         this.fireEvent('download', this);
27017     },
27018     
27019     loadCanvas : function(src)
27020     {   
27021         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27022             
27023             this.reset();
27024             
27025             this.imageEl = document.createElement('img');
27026             
27027             var _this = this;
27028             
27029             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27030             
27031             this.imageEl.src = src;
27032         }
27033     },
27034     
27035     onLoadCanvas : function()
27036     {   
27037         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27038         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27039         
27040         this.bodyEl.un('click', this.beforeSelectFile, this);
27041         
27042         this.notifyEl.hide();
27043         this.thumbEl.show();
27044         this.footerEl.show();
27045         
27046         this.baseRotateLevel();
27047         
27048         if(this.isDocument){
27049             this.setThumbBoxSize();
27050         }
27051         
27052         this.setThumbBoxPosition();
27053         
27054         this.baseScaleLevel();
27055         
27056         this.draw();
27057         
27058         this.resize();
27059         
27060         this.canvasLoaded = true;
27061         
27062         if(this.loadMask){
27063             this.maskEl.unmask();
27064         }
27065         
27066     },
27067     
27068     setCanvasPosition : function()
27069     {   
27070         if(!this.canvasEl){
27071             return;
27072         }
27073         
27074         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27075         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27076         
27077         this.previewEl.setLeft(pw);
27078         this.previewEl.setTop(ph);
27079         
27080     },
27081     
27082     onMouseDown : function(e)
27083     {   
27084         e.stopEvent();
27085         
27086         this.dragable = true;
27087         this.pinching = false;
27088         
27089         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27090             this.dragable = false;
27091             return;
27092         }
27093         
27094         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27095         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27096         
27097     },
27098     
27099     onMouseMove : function(e)
27100     {   
27101         e.stopEvent();
27102         
27103         if(!this.canvasLoaded){
27104             return;
27105         }
27106         
27107         if (!this.dragable){
27108             return;
27109         }
27110         
27111         var minX = Math.ceil(this.thumbEl.getLeft(true));
27112         var minY = Math.ceil(this.thumbEl.getTop(true));
27113         
27114         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27115         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27116         
27117         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27118         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27119         
27120         x = x - this.mouseX;
27121         y = y - this.mouseY;
27122         
27123         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27124         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27125         
27126         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27127         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27128         
27129         this.previewEl.setLeft(bgX);
27130         this.previewEl.setTop(bgY);
27131         
27132         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27133         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27134     },
27135     
27136     onMouseUp : function(e)
27137     {   
27138         e.stopEvent();
27139         
27140         this.dragable = false;
27141     },
27142     
27143     onMouseWheel : function(e)
27144     {   
27145         e.stopEvent();
27146         
27147         this.startScale = this.scale;
27148         
27149         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27150         
27151         if(!this.zoomable()){
27152             this.scale = this.startScale;
27153             return;
27154         }
27155         
27156         this.draw();
27157         
27158         return;
27159     },
27160     
27161     zoomable : function()
27162     {
27163         var minScale = this.thumbEl.getWidth() / this.minWidth;
27164         
27165         if(this.minWidth < this.minHeight){
27166             minScale = this.thumbEl.getHeight() / this.minHeight;
27167         }
27168         
27169         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27170         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27171         
27172         if(
27173                 this.isDocument &&
27174                 (this.rotate == 0 || this.rotate == 180) && 
27175                 (
27176                     width > this.imageEl.OriginWidth || 
27177                     height > this.imageEl.OriginHeight ||
27178                     (width < this.minWidth && height < this.minHeight)
27179                 )
27180         ){
27181             return false;
27182         }
27183         
27184         if(
27185                 this.isDocument &&
27186                 (this.rotate == 90 || this.rotate == 270) && 
27187                 (
27188                     width > this.imageEl.OriginWidth || 
27189                     height > this.imageEl.OriginHeight ||
27190                     (width < this.minHeight && height < this.minWidth)
27191                 )
27192         ){
27193             return false;
27194         }
27195         
27196         if(
27197                 !this.isDocument &&
27198                 (this.rotate == 0 || this.rotate == 180) && 
27199                 (
27200                     width < this.minWidth || 
27201                     width > this.imageEl.OriginWidth || 
27202                     height < this.minHeight || 
27203                     height > this.imageEl.OriginHeight
27204                 )
27205         ){
27206             return false;
27207         }
27208         
27209         if(
27210                 !this.isDocument &&
27211                 (this.rotate == 90 || this.rotate == 270) && 
27212                 (
27213                     width < this.minHeight || 
27214                     width > this.imageEl.OriginWidth || 
27215                     height < this.minWidth || 
27216                     height > this.imageEl.OriginHeight
27217                 )
27218         ){
27219             return false;
27220         }
27221         
27222         return true;
27223         
27224     },
27225     
27226     onRotateLeft : function(e)
27227     {   
27228         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27229             
27230             var minScale = this.thumbEl.getWidth() / this.minWidth;
27231             
27232             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27233             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27234             
27235             this.startScale = this.scale;
27236             
27237             while (this.getScaleLevel() < minScale){
27238             
27239                 this.scale = this.scale + 1;
27240                 
27241                 if(!this.zoomable()){
27242                     break;
27243                 }
27244                 
27245                 if(
27246                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27247                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27248                 ){
27249                     continue;
27250                 }
27251                 
27252                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27253
27254                 this.draw();
27255                 
27256                 return;
27257             }
27258             
27259             this.scale = this.startScale;
27260             
27261             this.onRotateFail();
27262             
27263             return false;
27264         }
27265         
27266         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27267
27268         if(this.isDocument){
27269             this.setThumbBoxSize();
27270             this.setThumbBoxPosition();
27271             this.setCanvasPosition();
27272         }
27273         
27274         this.draw();
27275         
27276         this.fireEvent('rotate', this, 'left');
27277         
27278     },
27279     
27280     onRotateRight : function(e)
27281     {
27282         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27283             
27284             var minScale = this.thumbEl.getWidth() / this.minWidth;
27285         
27286             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27287             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27288             
27289             this.startScale = this.scale;
27290             
27291             while (this.getScaleLevel() < minScale){
27292             
27293                 this.scale = this.scale + 1;
27294                 
27295                 if(!this.zoomable()){
27296                     break;
27297                 }
27298                 
27299                 if(
27300                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27301                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27302                 ){
27303                     continue;
27304                 }
27305                 
27306                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27307
27308                 this.draw();
27309                 
27310                 return;
27311             }
27312             
27313             this.scale = this.startScale;
27314             
27315             this.onRotateFail();
27316             
27317             return false;
27318         }
27319         
27320         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27321
27322         if(this.isDocument){
27323             this.setThumbBoxSize();
27324             this.setThumbBoxPosition();
27325             this.setCanvasPosition();
27326         }
27327         
27328         this.draw();
27329         
27330         this.fireEvent('rotate', this, 'right');
27331     },
27332     
27333     onRotateFail : function()
27334     {
27335         this.errorEl.show(true);
27336         
27337         var _this = this;
27338         
27339         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27340     },
27341     
27342     draw : function()
27343     {
27344         this.previewEl.dom.innerHTML = '';
27345         
27346         var canvasEl = document.createElement("canvas");
27347         
27348         var contextEl = canvasEl.getContext("2d");
27349         
27350         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27351         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27352         var center = this.imageEl.OriginWidth / 2;
27353         
27354         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27355             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27356             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27357             center = this.imageEl.OriginHeight / 2;
27358         }
27359         
27360         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27361         
27362         contextEl.translate(center, center);
27363         contextEl.rotate(this.rotate * Math.PI / 180);
27364
27365         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27366         
27367         this.canvasEl = document.createElement("canvas");
27368         
27369         this.contextEl = this.canvasEl.getContext("2d");
27370         
27371         switch (this.rotate) {
27372             case 0 :
27373                 
27374                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27375                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27376                 
27377                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27378                 
27379                 break;
27380             case 90 : 
27381                 
27382                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27383                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27384                 
27385                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27386                     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);
27387                     break;
27388                 }
27389                 
27390                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27391                 
27392                 break;
27393             case 180 :
27394                 
27395                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27396                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27397                 
27398                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27399                     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);
27400                     break;
27401                 }
27402                 
27403                 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);
27404                 
27405                 break;
27406             case 270 :
27407                 
27408                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27409                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27410         
27411                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27412                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27413                     break;
27414                 }
27415                 
27416                 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);
27417                 
27418                 break;
27419             default : 
27420                 break;
27421         }
27422         
27423         this.previewEl.appendChild(this.canvasEl);
27424         
27425         this.setCanvasPosition();
27426     },
27427     
27428     crop : function()
27429     {
27430         if(!this.canvasLoaded){
27431             return;
27432         }
27433         
27434         var imageCanvas = document.createElement("canvas");
27435         
27436         var imageContext = imageCanvas.getContext("2d");
27437         
27438         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27439         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27440         
27441         var center = imageCanvas.width / 2;
27442         
27443         imageContext.translate(center, center);
27444         
27445         imageContext.rotate(this.rotate * Math.PI / 180);
27446         
27447         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27448         
27449         var canvas = document.createElement("canvas");
27450         
27451         var context = canvas.getContext("2d");
27452                 
27453         canvas.width = this.minWidth;
27454         canvas.height = this.minHeight;
27455
27456         switch (this.rotate) {
27457             case 0 :
27458                 
27459                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27460                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27461                 
27462                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27463                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27464                 
27465                 var targetWidth = this.minWidth - 2 * x;
27466                 var targetHeight = this.minHeight - 2 * y;
27467                 
27468                 var scale = 1;
27469                 
27470                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27471                     scale = targetWidth / width;
27472                 }
27473                 
27474                 if(x > 0 && y == 0){
27475                     scale = targetHeight / height;
27476                 }
27477                 
27478                 if(x > 0 && y > 0){
27479                     scale = targetWidth / width;
27480                     
27481                     if(width < height){
27482                         scale = targetHeight / height;
27483                     }
27484                 }
27485                 
27486                 context.scale(scale, scale);
27487                 
27488                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27489                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27490
27491                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27492                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27493
27494                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27495                 
27496                 break;
27497             case 90 : 
27498                 
27499                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27500                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27501                 
27502                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27503                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27504                 
27505                 var targetWidth = this.minWidth - 2 * x;
27506                 var targetHeight = this.minHeight - 2 * y;
27507                 
27508                 var scale = 1;
27509                 
27510                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27511                     scale = targetWidth / width;
27512                 }
27513                 
27514                 if(x > 0 && y == 0){
27515                     scale = targetHeight / height;
27516                 }
27517                 
27518                 if(x > 0 && y > 0){
27519                     scale = targetWidth / width;
27520                     
27521                     if(width < height){
27522                         scale = targetHeight / height;
27523                     }
27524                 }
27525                 
27526                 context.scale(scale, scale);
27527                 
27528                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27529                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27530
27531                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27532                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27533                 
27534                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27535                 
27536                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27537                 
27538                 break;
27539             case 180 :
27540                 
27541                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27542                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27543                 
27544                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27545                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27546                 
27547                 var targetWidth = this.minWidth - 2 * x;
27548                 var targetHeight = this.minHeight - 2 * y;
27549                 
27550                 var scale = 1;
27551                 
27552                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27553                     scale = targetWidth / width;
27554                 }
27555                 
27556                 if(x > 0 && y == 0){
27557                     scale = targetHeight / height;
27558                 }
27559                 
27560                 if(x > 0 && y > 0){
27561                     scale = targetWidth / width;
27562                     
27563                     if(width < height){
27564                         scale = targetHeight / height;
27565                     }
27566                 }
27567                 
27568                 context.scale(scale, scale);
27569                 
27570                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27571                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27572
27573                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27574                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27575
27576                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27577                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27578                 
27579                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27580                 
27581                 break;
27582             case 270 :
27583                 
27584                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27585                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27586                 
27587                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27588                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27589                 
27590                 var targetWidth = this.minWidth - 2 * x;
27591                 var targetHeight = this.minHeight - 2 * y;
27592                 
27593                 var scale = 1;
27594                 
27595                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27596                     scale = targetWidth / width;
27597                 }
27598                 
27599                 if(x > 0 && y == 0){
27600                     scale = targetHeight / height;
27601                 }
27602                 
27603                 if(x > 0 && y > 0){
27604                     scale = targetWidth / width;
27605                     
27606                     if(width < height){
27607                         scale = targetHeight / height;
27608                     }
27609                 }
27610                 
27611                 context.scale(scale, scale);
27612                 
27613                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27614                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27615
27616                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27617                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27618                 
27619                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27620                 
27621                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27622                 
27623                 break;
27624             default : 
27625                 break;
27626         }
27627         
27628         this.cropData = canvas.toDataURL(this.cropType);
27629         
27630         if(this.fireEvent('crop', this, this.cropData) !== false){
27631             this.process(this.file, this.cropData);
27632         }
27633         
27634         return;
27635         
27636     },
27637     
27638     setThumbBoxSize : function()
27639     {
27640         var width, height;
27641         
27642         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27643             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27644             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27645             
27646             this.minWidth = width;
27647             this.minHeight = height;
27648             
27649             if(this.rotate == 90 || this.rotate == 270){
27650                 this.minWidth = height;
27651                 this.minHeight = width;
27652             }
27653         }
27654         
27655         height = 300;
27656         width = Math.ceil(this.minWidth * height / this.minHeight);
27657         
27658         if(this.minWidth > this.minHeight){
27659             width = 300;
27660             height = Math.ceil(this.minHeight * width / this.minWidth);
27661         }
27662         
27663         this.thumbEl.setStyle({
27664             width : width + 'px',
27665             height : height + 'px'
27666         });
27667
27668         return;
27669             
27670     },
27671     
27672     setThumbBoxPosition : function()
27673     {
27674         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27675         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27676         
27677         this.thumbEl.setLeft(x);
27678         this.thumbEl.setTop(y);
27679         
27680     },
27681     
27682     baseRotateLevel : function()
27683     {
27684         this.baseRotate = 1;
27685         
27686         if(
27687                 typeof(this.exif) != 'undefined' &&
27688                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27689                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27690         ){
27691             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27692         }
27693         
27694         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27695         
27696     },
27697     
27698     baseScaleLevel : function()
27699     {
27700         var width, height;
27701         
27702         if(this.isDocument){
27703             
27704             if(this.baseRotate == 6 || this.baseRotate == 8){
27705             
27706                 height = this.thumbEl.getHeight();
27707                 this.baseScale = height / this.imageEl.OriginWidth;
27708
27709                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27710                     width = this.thumbEl.getWidth();
27711                     this.baseScale = width / this.imageEl.OriginHeight;
27712                 }
27713
27714                 return;
27715             }
27716
27717             height = this.thumbEl.getHeight();
27718             this.baseScale = height / this.imageEl.OriginHeight;
27719
27720             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27721                 width = this.thumbEl.getWidth();
27722                 this.baseScale = width / this.imageEl.OriginWidth;
27723             }
27724
27725             return;
27726         }
27727         
27728         if(this.baseRotate == 6 || this.baseRotate == 8){
27729             
27730             width = this.thumbEl.getHeight();
27731             this.baseScale = width / this.imageEl.OriginHeight;
27732             
27733             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27734                 height = this.thumbEl.getWidth();
27735                 this.baseScale = height / this.imageEl.OriginHeight;
27736             }
27737             
27738             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27739                 height = this.thumbEl.getWidth();
27740                 this.baseScale = height / this.imageEl.OriginHeight;
27741                 
27742                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27743                     width = this.thumbEl.getHeight();
27744                     this.baseScale = width / this.imageEl.OriginWidth;
27745                 }
27746             }
27747             
27748             return;
27749         }
27750         
27751         width = this.thumbEl.getWidth();
27752         this.baseScale = width / this.imageEl.OriginWidth;
27753         
27754         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27755             height = this.thumbEl.getHeight();
27756             this.baseScale = height / this.imageEl.OriginHeight;
27757         }
27758         
27759         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27760             
27761             height = this.thumbEl.getHeight();
27762             this.baseScale = height / this.imageEl.OriginHeight;
27763             
27764             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27765                 width = this.thumbEl.getWidth();
27766                 this.baseScale = width / this.imageEl.OriginWidth;
27767             }
27768             
27769         }
27770         
27771         return;
27772     },
27773     
27774     getScaleLevel : function()
27775     {
27776         return this.baseScale * Math.pow(1.1, this.scale);
27777     },
27778     
27779     onTouchStart : function(e)
27780     {
27781         if(!this.canvasLoaded){
27782             this.beforeSelectFile(e);
27783             return;
27784         }
27785         
27786         var touches = e.browserEvent.touches;
27787         
27788         if(!touches){
27789             return;
27790         }
27791         
27792         if(touches.length == 1){
27793             this.onMouseDown(e);
27794             return;
27795         }
27796         
27797         if(touches.length != 2){
27798             return;
27799         }
27800         
27801         var coords = [];
27802         
27803         for(var i = 0, finger; finger = touches[i]; i++){
27804             coords.push(finger.pageX, finger.pageY);
27805         }
27806         
27807         var x = Math.pow(coords[0] - coords[2], 2);
27808         var y = Math.pow(coords[1] - coords[3], 2);
27809         
27810         this.startDistance = Math.sqrt(x + y);
27811         
27812         this.startScale = this.scale;
27813         
27814         this.pinching = true;
27815         this.dragable = false;
27816         
27817     },
27818     
27819     onTouchMove : function(e)
27820     {
27821         if(!this.pinching && !this.dragable){
27822             return;
27823         }
27824         
27825         var touches = e.browserEvent.touches;
27826         
27827         if(!touches){
27828             return;
27829         }
27830         
27831         if(this.dragable){
27832             this.onMouseMove(e);
27833             return;
27834         }
27835         
27836         var coords = [];
27837         
27838         for(var i = 0, finger; finger = touches[i]; i++){
27839             coords.push(finger.pageX, finger.pageY);
27840         }
27841         
27842         var x = Math.pow(coords[0] - coords[2], 2);
27843         var y = Math.pow(coords[1] - coords[3], 2);
27844         
27845         this.endDistance = Math.sqrt(x + y);
27846         
27847         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
27848         
27849         if(!this.zoomable()){
27850             this.scale = this.startScale;
27851             return;
27852         }
27853         
27854         this.draw();
27855         
27856     },
27857     
27858     onTouchEnd : function(e)
27859     {
27860         this.pinching = false;
27861         this.dragable = false;
27862         
27863     },
27864     
27865     process : function(file, crop)
27866     {
27867         if(this.loadMask){
27868             this.maskEl.mask(this.loadingText);
27869         }
27870         
27871         this.xhr = new XMLHttpRequest();
27872         
27873         file.xhr = this.xhr;
27874
27875         this.xhr.open(this.method, this.url, true);
27876         
27877         var headers = {
27878             "Accept": "application/json",
27879             "Cache-Control": "no-cache",
27880             "X-Requested-With": "XMLHttpRequest"
27881         };
27882         
27883         for (var headerName in headers) {
27884             var headerValue = headers[headerName];
27885             if (headerValue) {
27886                 this.xhr.setRequestHeader(headerName, headerValue);
27887             }
27888         }
27889         
27890         var _this = this;
27891         
27892         this.xhr.onload = function()
27893         {
27894             _this.xhrOnLoad(_this.xhr);
27895         }
27896         
27897         this.xhr.onerror = function()
27898         {
27899             _this.xhrOnError(_this.xhr);
27900         }
27901         
27902         var formData = new FormData();
27903
27904         formData.append('returnHTML', 'NO');
27905         
27906         if(crop){
27907             formData.append('crop', crop);
27908         }
27909         
27910         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
27911             formData.append(this.paramName, file, file.name);
27912         }
27913         
27914         if(typeof(file.filename) != 'undefined'){
27915             formData.append('filename', file.filename);
27916         }
27917         
27918         if(typeof(file.mimetype) != 'undefined'){
27919             formData.append('mimetype', file.mimetype);
27920         }
27921         
27922         if(this.fireEvent('arrange', this, formData) != false){
27923             this.xhr.send(formData);
27924         };
27925     },
27926     
27927     xhrOnLoad : function(xhr)
27928     {
27929         if(this.loadMask){
27930             this.maskEl.unmask();
27931         }
27932         
27933         if (xhr.readyState !== 4) {
27934             this.fireEvent('exception', this, xhr);
27935             return;
27936         }
27937
27938         var response = Roo.decode(xhr.responseText);
27939         
27940         if(!response.success){
27941             this.fireEvent('exception', this, xhr);
27942             return;
27943         }
27944         
27945         var response = Roo.decode(xhr.responseText);
27946         
27947         this.fireEvent('upload', this, response);
27948         
27949     },
27950     
27951     xhrOnError : function()
27952     {
27953         if(this.loadMask){
27954             this.maskEl.unmask();
27955         }
27956         
27957         Roo.log('xhr on error');
27958         
27959         var response = Roo.decode(xhr.responseText);
27960           
27961         Roo.log(response);
27962         
27963     },
27964     
27965     prepare : function(file)
27966     {   
27967         if(this.loadMask){
27968             this.maskEl.mask(this.loadingText);
27969         }
27970         
27971         this.file = false;
27972         this.exif = {};
27973         
27974         if(typeof(file) === 'string'){
27975             this.loadCanvas(file);
27976             return;
27977         }
27978         
27979         if(!file || !this.urlAPI){
27980             return;
27981         }
27982         
27983         this.file = file;
27984         this.cropType = file.type;
27985         
27986         var _this = this;
27987         
27988         if(this.fireEvent('prepare', this, this.file) != false){
27989             
27990             var reader = new FileReader();
27991             
27992             reader.onload = function (e) {
27993                 if (e.target.error) {
27994                     Roo.log(e.target.error);
27995                     return;
27996                 }
27997                 
27998                 var buffer = e.target.result,
27999                     dataView = new DataView(buffer),
28000                     offset = 2,
28001                     maxOffset = dataView.byteLength - 4,
28002                     markerBytes,
28003                     markerLength;
28004                 
28005                 if (dataView.getUint16(0) === 0xffd8) {
28006                     while (offset < maxOffset) {
28007                         markerBytes = dataView.getUint16(offset);
28008                         
28009                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28010                             markerLength = dataView.getUint16(offset + 2) + 2;
28011                             if (offset + markerLength > dataView.byteLength) {
28012                                 Roo.log('Invalid meta data: Invalid segment size.');
28013                                 break;
28014                             }
28015                             
28016                             if(markerBytes == 0xffe1){
28017                                 _this.parseExifData(
28018                                     dataView,
28019                                     offset,
28020                                     markerLength
28021                                 );
28022                             }
28023                             
28024                             offset += markerLength;
28025                             
28026                             continue;
28027                         }
28028                         
28029                         break;
28030                     }
28031                     
28032                 }
28033                 
28034                 var url = _this.urlAPI.createObjectURL(_this.file);
28035                 
28036                 _this.loadCanvas(url);
28037                 
28038                 return;
28039             }
28040             
28041             reader.readAsArrayBuffer(this.file);
28042             
28043         }
28044         
28045     },
28046     
28047     parseExifData : function(dataView, offset, length)
28048     {
28049         var tiffOffset = offset + 10,
28050             littleEndian,
28051             dirOffset;
28052     
28053         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28054             // No Exif data, might be XMP data instead
28055             return;
28056         }
28057         
28058         // Check for the ASCII code for "Exif" (0x45786966):
28059         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28060             // No Exif data, might be XMP data instead
28061             return;
28062         }
28063         if (tiffOffset + 8 > dataView.byteLength) {
28064             Roo.log('Invalid Exif data: Invalid segment size.');
28065             return;
28066         }
28067         // Check for the two null bytes:
28068         if (dataView.getUint16(offset + 8) !== 0x0000) {
28069             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28070             return;
28071         }
28072         // Check the byte alignment:
28073         switch (dataView.getUint16(tiffOffset)) {
28074         case 0x4949:
28075             littleEndian = true;
28076             break;
28077         case 0x4D4D:
28078             littleEndian = false;
28079             break;
28080         default:
28081             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28082             return;
28083         }
28084         // Check for the TIFF tag marker (0x002A):
28085         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28086             Roo.log('Invalid Exif data: Missing TIFF marker.');
28087             return;
28088         }
28089         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28090         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28091         
28092         this.parseExifTags(
28093             dataView,
28094             tiffOffset,
28095             tiffOffset + dirOffset,
28096             littleEndian
28097         );
28098     },
28099     
28100     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28101     {
28102         var tagsNumber,
28103             dirEndOffset,
28104             i;
28105         if (dirOffset + 6 > dataView.byteLength) {
28106             Roo.log('Invalid Exif data: Invalid directory offset.');
28107             return;
28108         }
28109         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28110         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28111         if (dirEndOffset + 4 > dataView.byteLength) {
28112             Roo.log('Invalid Exif data: Invalid directory size.');
28113             return;
28114         }
28115         for (i = 0; i < tagsNumber; i += 1) {
28116             this.parseExifTag(
28117                 dataView,
28118                 tiffOffset,
28119                 dirOffset + 2 + 12 * i, // tag offset
28120                 littleEndian
28121             );
28122         }
28123         // Return the offset to the next directory:
28124         return dataView.getUint32(dirEndOffset, littleEndian);
28125     },
28126     
28127     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28128     {
28129         var tag = dataView.getUint16(offset, littleEndian);
28130         
28131         this.exif[tag] = this.getExifValue(
28132             dataView,
28133             tiffOffset,
28134             offset,
28135             dataView.getUint16(offset + 2, littleEndian), // tag type
28136             dataView.getUint32(offset + 4, littleEndian), // tag length
28137             littleEndian
28138         );
28139     },
28140     
28141     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28142     {
28143         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28144             tagSize,
28145             dataOffset,
28146             values,
28147             i,
28148             str,
28149             c;
28150     
28151         if (!tagType) {
28152             Roo.log('Invalid Exif data: Invalid tag type.');
28153             return;
28154         }
28155         
28156         tagSize = tagType.size * length;
28157         // Determine if the value is contained in the dataOffset bytes,
28158         // or if the value at the dataOffset is a pointer to the actual data:
28159         dataOffset = tagSize > 4 ?
28160                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28161         if (dataOffset + tagSize > dataView.byteLength) {
28162             Roo.log('Invalid Exif data: Invalid data offset.');
28163             return;
28164         }
28165         if (length === 1) {
28166             return tagType.getValue(dataView, dataOffset, littleEndian);
28167         }
28168         values = [];
28169         for (i = 0; i < length; i += 1) {
28170             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28171         }
28172         
28173         if (tagType.ascii) {
28174             str = '';
28175             // Concatenate the chars:
28176             for (i = 0; i < values.length; i += 1) {
28177                 c = values[i];
28178                 // Ignore the terminating NULL byte(s):
28179                 if (c === '\u0000') {
28180                     break;
28181                 }
28182                 str += c;
28183             }
28184             return str;
28185         }
28186         return values;
28187     }
28188     
28189 });
28190
28191 Roo.apply(Roo.bootstrap.UploadCropbox, {
28192     tags : {
28193         'Orientation': 0x0112
28194     },
28195     
28196     Orientation: {
28197             1: 0, //'top-left',
28198 //            2: 'top-right',
28199             3: 180, //'bottom-right',
28200 //            4: 'bottom-left',
28201 //            5: 'left-top',
28202             6: 90, //'right-top',
28203 //            7: 'right-bottom',
28204             8: 270 //'left-bottom'
28205     },
28206     
28207     exifTagTypes : {
28208         // byte, 8-bit unsigned int:
28209         1: {
28210             getValue: function (dataView, dataOffset) {
28211                 return dataView.getUint8(dataOffset);
28212             },
28213             size: 1
28214         },
28215         // ascii, 8-bit byte:
28216         2: {
28217             getValue: function (dataView, dataOffset) {
28218                 return String.fromCharCode(dataView.getUint8(dataOffset));
28219             },
28220             size: 1,
28221             ascii: true
28222         },
28223         // short, 16 bit int:
28224         3: {
28225             getValue: function (dataView, dataOffset, littleEndian) {
28226                 return dataView.getUint16(dataOffset, littleEndian);
28227             },
28228             size: 2
28229         },
28230         // long, 32 bit int:
28231         4: {
28232             getValue: function (dataView, dataOffset, littleEndian) {
28233                 return dataView.getUint32(dataOffset, littleEndian);
28234             },
28235             size: 4
28236         },
28237         // rational = two long values, first is numerator, second is denominator:
28238         5: {
28239             getValue: function (dataView, dataOffset, littleEndian) {
28240                 return dataView.getUint32(dataOffset, littleEndian) /
28241                     dataView.getUint32(dataOffset + 4, littleEndian);
28242             },
28243             size: 8
28244         },
28245         // slong, 32 bit signed int:
28246         9: {
28247             getValue: function (dataView, dataOffset, littleEndian) {
28248                 return dataView.getInt32(dataOffset, littleEndian);
28249             },
28250             size: 4
28251         },
28252         // srational, two slongs, first is numerator, second is denominator:
28253         10: {
28254             getValue: function (dataView, dataOffset, littleEndian) {
28255                 return dataView.getInt32(dataOffset, littleEndian) /
28256                     dataView.getInt32(dataOffset + 4, littleEndian);
28257             },
28258             size: 8
28259         }
28260     },
28261     
28262     footer : {
28263         STANDARD : [
28264             {
28265                 tag : 'div',
28266                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28267                 action : 'rotate-left',
28268                 cn : [
28269                     {
28270                         tag : 'button',
28271                         cls : 'btn btn-default',
28272                         html : '<i class="fa fa-undo"></i>'
28273                     }
28274                 ]
28275             },
28276             {
28277                 tag : 'div',
28278                 cls : 'btn-group roo-upload-cropbox-picture',
28279                 action : 'picture',
28280                 cn : [
28281                     {
28282                         tag : 'button',
28283                         cls : 'btn btn-default',
28284                         html : '<i class="fa fa-picture-o"></i>'
28285                     }
28286                 ]
28287             },
28288             {
28289                 tag : 'div',
28290                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28291                 action : 'rotate-right',
28292                 cn : [
28293                     {
28294                         tag : 'button',
28295                         cls : 'btn btn-default',
28296                         html : '<i class="fa fa-repeat"></i>'
28297                     }
28298                 ]
28299             }
28300         ],
28301         DOCUMENT : [
28302             {
28303                 tag : 'div',
28304                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28305                 action : 'rotate-left',
28306                 cn : [
28307                     {
28308                         tag : 'button',
28309                         cls : 'btn btn-default',
28310                         html : '<i class="fa fa-undo"></i>'
28311                     }
28312                 ]
28313             },
28314             {
28315                 tag : 'div',
28316                 cls : 'btn-group roo-upload-cropbox-download',
28317                 action : 'download',
28318                 cn : [
28319                     {
28320                         tag : 'button',
28321                         cls : 'btn btn-default',
28322                         html : '<i class="fa fa-download"></i>'
28323                     }
28324                 ]
28325             },
28326             {
28327                 tag : 'div',
28328                 cls : 'btn-group roo-upload-cropbox-crop',
28329                 action : 'crop',
28330                 cn : [
28331                     {
28332                         tag : 'button',
28333                         cls : 'btn btn-default',
28334                         html : '<i class="fa fa-crop"></i>'
28335                     }
28336                 ]
28337             },
28338             {
28339                 tag : 'div',
28340                 cls : 'btn-group roo-upload-cropbox-trash',
28341                 action : 'trash',
28342                 cn : [
28343                     {
28344                         tag : 'button',
28345                         cls : 'btn btn-default',
28346                         html : '<i class="fa fa-trash"></i>'
28347                     }
28348                 ]
28349             },
28350             {
28351                 tag : 'div',
28352                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28353                 action : 'rotate-right',
28354                 cn : [
28355                     {
28356                         tag : 'button',
28357                         cls : 'btn btn-default',
28358                         html : '<i class="fa fa-repeat"></i>'
28359                     }
28360                 ]
28361             }
28362         ],
28363         ROTATOR : [
28364             {
28365                 tag : 'div',
28366                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28367                 action : 'rotate-left',
28368                 cn : [
28369                     {
28370                         tag : 'button',
28371                         cls : 'btn btn-default',
28372                         html : '<i class="fa fa-undo"></i>'
28373                     }
28374                 ]
28375             },
28376             {
28377                 tag : 'div',
28378                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28379                 action : 'rotate-right',
28380                 cn : [
28381                     {
28382                         tag : 'button',
28383                         cls : 'btn btn-default',
28384                         html : '<i class="fa fa-repeat"></i>'
28385                     }
28386                 ]
28387             }
28388         ]
28389     }
28390 });
28391
28392 /*
28393 * Licence: LGPL
28394 */
28395
28396 /**
28397  * @class Roo.bootstrap.DocumentManager
28398  * @extends Roo.bootstrap.Component
28399  * Bootstrap DocumentManager class
28400  * @cfg {String} paramName default 'imageUpload'
28401  * @cfg {String} toolTipName default 'filename'
28402  * @cfg {String} method default POST
28403  * @cfg {String} url action url
28404  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28405  * @cfg {Boolean} multiple multiple upload default true
28406  * @cfg {Number} thumbSize default 300
28407  * @cfg {String} fieldLabel
28408  * @cfg {Number} labelWidth default 4
28409  * @cfg {String} labelAlign (left|top) default left
28410  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28411 * @cfg {Number} labellg set the width of label (1-12)
28412  * @cfg {Number} labelmd set the width of label (1-12)
28413  * @cfg {Number} labelsm set the width of label (1-12)
28414  * @cfg {Number} labelxs set the width of label (1-12)
28415  * 
28416  * @constructor
28417  * Create a new DocumentManager
28418  * @param {Object} config The config object
28419  */
28420
28421 Roo.bootstrap.DocumentManager = function(config){
28422     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28423     
28424     this.files = [];
28425     this.delegates = [];
28426     
28427     this.addEvents({
28428         /**
28429          * @event initial
28430          * Fire when initial the DocumentManager
28431          * @param {Roo.bootstrap.DocumentManager} this
28432          */
28433         "initial" : true,
28434         /**
28435          * @event inspect
28436          * inspect selected file
28437          * @param {Roo.bootstrap.DocumentManager} this
28438          * @param {File} file
28439          */
28440         "inspect" : true,
28441         /**
28442          * @event exception
28443          * Fire when xhr load exception
28444          * @param {Roo.bootstrap.DocumentManager} this
28445          * @param {XMLHttpRequest} xhr
28446          */
28447         "exception" : true,
28448         /**
28449          * @event afterupload
28450          * Fire when xhr load exception
28451          * @param {Roo.bootstrap.DocumentManager} this
28452          * @param {XMLHttpRequest} xhr
28453          */
28454         "afterupload" : true,
28455         /**
28456          * @event prepare
28457          * prepare the form data
28458          * @param {Roo.bootstrap.DocumentManager} this
28459          * @param {Object} formData
28460          */
28461         "prepare" : true,
28462         /**
28463          * @event remove
28464          * Fire when remove the file
28465          * @param {Roo.bootstrap.DocumentManager} this
28466          * @param {Object} file
28467          */
28468         "remove" : true,
28469         /**
28470          * @event refresh
28471          * Fire after refresh the file
28472          * @param {Roo.bootstrap.DocumentManager} this
28473          */
28474         "refresh" : true,
28475         /**
28476          * @event click
28477          * Fire after click the image
28478          * @param {Roo.bootstrap.DocumentManager} this
28479          * @param {Object} file
28480          */
28481         "click" : true,
28482         /**
28483          * @event edit
28484          * Fire when upload a image and editable set to true
28485          * @param {Roo.bootstrap.DocumentManager} this
28486          * @param {Object} file
28487          */
28488         "edit" : true,
28489         /**
28490          * @event beforeselectfile
28491          * Fire before select file
28492          * @param {Roo.bootstrap.DocumentManager} this
28493          */
28494         "beforeselectfile" : true,
28495         /**
28496          * @event process
28497          * Fire before process file
28498          * @param {Roo.bootstrap.DocumentManager} this
28499          * @param {Object} file
28500          */
28501         "process" : true,
28502         /**
28503          * @event previewrendered
28504          * Fire when preview rendered
28505          * @param {Roo.bootstrap.DocumentManager} this
28506          * @param {Object} file
28507          */
28508         "previewrendered" : true
28509         
28510     });
28511 };
28512
28513 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28514     
28515     boxes : 0,
28516     inputName : '',
28517     thumbSize : 300,
28518     multiple : true,
28519     files : false,
28520     method : 'POST',
28521     url : '',
28522     paramName : 'imageUpload',
28523     toolTipName : 'filename',
28524     fieldLabel : '',
28525     labelWidth : 4,
28526     labelAlign : 'left',
28527     editable : true,
28528     delegates : false,
28529     xhr : false, 
28530     
28531     labellg : 0,
28532     labelmd : 0,
28533     labelsm : 0,
28534     labelxs : 0,
28535     
28536     getAutoCreate : function()
28537     {   
28538         var managerWidget = {
28539             tag : 'div',
28540             cls : 'roo-document-manager',
28541             cn : [
28542                 {
28543                     tag : 'input',
28544                     cls : 'roo-document-manager-selector',
28545                     type : 'file'
28546                 },
28547                 {
28548                     tag : 'div',
28549                     cls : 'roo-document-manager-uploader',
28550                     cn : [
28551                         {
28552                             tag : 'div',
28553                             cls : 'roo-document-manager-upload-btn',
28554                             html : '<i class="fa fa-plus"></i>'
28555                         }
28556                     ]
28557                     
28558                 }
28559             ]
28560         };
28561         
28562         var content = [
28563             {
28564                 tag : 'div',
28565                 cls : 'column col-md-12',
28566                 cn : managerWidget
28567             }
28568         ];
28569         
28570         if(this.fieldLabel.length){
28571             
28572             content = [
28573                 {
28574                     tag : 'div',
28575                     cls : 'column col-md-12',
28576                     html : this.fieldLabel
28577                 },
28578                 {
28579                     tag : 'div',
28580                     cls : 'column col-md-12',
28581                     cn : managerWidget
28582                 }
28583             ];
28584
28585             if(this.labelAlign == 'left'){
28586                 content = [
28587                     {
28588                         tag : 'div',
28589                         cls : 'column',
28590                         html : this.fieldLabel
28591                     },
28592                     {
28593                         tag : 'div',
28594                         cls : 'column',
28595                         cn : managerWidget
28596                     }
28597                 ];
28598                 
28599                 if(this.labelWidth > 12){
28600                     content[0].style = "width: " + this.labelWidth + 'px';
28601                 }
28602
28603                 if(this.labelWidth < 13 && this.labelmd == 0){
28604                     this.labelmd = this.labelWidth;
28605                 }
28606
28607                 if(this.labellg > 0){
28608                     content[0].cls += ' col-lg-' + this.labellg;
28609                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28610                 }
28611
28612                 if(this.labelmd > 0){
28613                     content[0].cls += ' col-md-' + this.labelmd;
28614                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28615                 }
28616
28617                 if(this.labelsm > 0){
28618                     content[0].cls += ' col-sm-' + this.labelsm;
28619                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28620                 }
28621
28622                 if(this.labelxs > 0){
28623                     content[0].cls += ' col-xs-' + this.labelxs;
28624                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28625                 }
28626                 
28627             }
28628         }
28629         
28630         var cfg = {
28631             tag : 'div',
28632             cls : 'row clearfix',
28633             cn : content
28634         };
28635         
28636         return cfg;
28637         
28638     },
28639     
28640     initEvents : function()
28641     {
28642         this.managerEl = this.el.select('.roo-document-manager', true).first();
28643         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28644         
28645         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28646         this.selectorEl.hide();
28647         
28648         if(this.multiple){
28649             this.selectorEl.attr('multiple', 'multiple');
28650         }
28651         
28652         this.selectorEl.on('change', this.onFileSelected, this);
28653         
28654         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28655         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28656         
28657         this.uploader.on('click', this.onUploaderClick, this);
28658         
28659         this.renderProgressDialog();
28660         
28661         var _this = this;
28662         
28663         window.addEventListener("resize", function() { _this.refresh(); } );
28664         
28665         this.fireEvent('initial', this);
28666     },
28667     
28668     renderProgressDialog : function()
28669     {
28670         var _this = this;
28671         
28672         this.progressDialog = new Roo.bootstrap.Modal({
28673             cls : 'roo-document-manager-progress-dialog',
28674             allow_close : false,
28675             title : '',
28676             buttons : [
28677                 {
28678                     name  :'cancel',
28679                     weight : 'danger',
28680                     html : 'Cancel'
28681                 }
28682             ], 
28683             listeners : { 
28684                 btnclick : function() {
28685                     _this.uploadCancel();
28686                     this.hide();
28687                 }
28688             }
28689         });
28690          
28691         this.progressDialog.render(Roo.get(document.body));
28692          
28693         this.progress = new Roo.bootstrap.Progress({
28694             cls : 'roo-document-manager-progress',
28695             active : true,
28696             striped : true
28697         });
28698         
28699         this.progress.render(this.progressDialog.getChildContainer());
28700         
28701         this.progressBar = new Roo.bootstrap.ProgressBar({
28702             cls : 'roo-document-manager-progress-bar',
28703             aria_valuenow : 0,
28704             aria_valuemin : 0,
28705             aria_valuemax : 12,
28706             panel : 'success'
28707         });
28708         
28709         this.progressBar.render(this.progress.getChildContainer());
28710     },
28711     
28712     onUploaderClick : function(e)
28713     {
28714         e.preventDefault();
28715      
28716         if(this.fireEvent('beforeselectfile', this) != false){
28717             this.selectorEl.dom.click();
28718         }
28719         
28720     },
28721     
28722     onFileSelected : function(e)
28723     {
28724         e.preventDefault();
28725         
28726         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28727             return;
28728         }
28729         
28730         Roo.each(this.selectorEl.dom.files, function(file){
28731             if(this.fireEvent('inspect', this, file) != false){
28732                 this.files.push(file);
28733             }
28734         }, this);
28735         
28736         this.queue();
28737         
28738     },
28739     
28740     queue : function()
28741     {
28742         this.selectorEl.dom.value = '';
28743         
28744         if(!this.files || !this.files.length){
28745             return;
28746         }
28747         
28748         if(this.boxes > 0 && this.files.length > this.boxes){
28749             this.files = this.files.slice(0, this.boxes);
28750         }
28751         
28752         this.uploader.show();
28753         
28754         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28755             this.uploader.hide();
28756         }
28757         
28758         var _this = this;
28759         
28760         var files = [];
28761         
28762         var docs = [];
28763         
28764         Roo.each(this.files, function(file){
28765             
28766             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28767                 var f = this.renderPreview(file);
28768                 files.push(f);
28769                 return;
28770             }
28771             
28772             if(file.type.indexOf('image') != -1){
28773                 this.delegates.push(
28774                     (function(){
28775                         _this.process(file);
28776                     }).createDelegate(this)
28777                 );
28778         
28779                 return;
28780             }
28781             
28782             docs.push(
28783                 (function(){
28784                     _this.process(file);
28785                 }).createDelegate(this)
28786             );
28787             
28788         }, this);
28789         
28790         this.files = files;
28791         
28792         this.delegates = this.delegates.concat(docs);
28793         
28794         if(!this.delegates.length){
28795             this.refresh();
28796             return;
28797         }
28798         
28799         this.progressBar.aria_valuemax = this.delegates.length;
28800         
28801         this.arrange();
28802         
28803         return;
28804     },
28805     
28806     arrange : function()
28807     {
28808         if(!this.delegates.length){
28809             this.progressDialog.hide();
28810             this.refresh();
28811             return;
28812         }
28813         
28814         var delegate = this.delegates.shift();
28815         
28816         this.progressDialog.show();
28817         
28818         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
28819         
28820         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
28821         
28822         delegate();
28823     },
28824     
28825     refresh : function()
28826     {
28827         this.uploader.show();
28828         
28829         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28830             this.uploader.hide();
28831         }
28832         
28833         Roo.isTouch ? this.closable(false) : this.closable(true);
28834         
28835         this.fireEvent('refresh', this);
28836     },
28837     
28838     onRemove : function(e, el, o)
28839     {
28840         e.preventDefault();
28841         
28842         this.fireEvent('remove', this, o);
28843         
28844     },
28845     
28846     remove : function(o)
28847     {
28848         var files = [];
28849         
28850         Roo.each(this.files, function(file){
28851             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
28852                 files.push(file);
28853                 return;
28854             }
28855
28856             o.target.remove();
28857
28858         }, this);
28859         
28860         this.files = files;
28861         
28862         this.refresh();
28863     },
28864     
28865     clear : function()
28866     {
28867         Roo.each(this.files, function(file){
28868             if(!file.target){
28869                 return;
28870             }
28871             
28872             file.target.remove();
28873
28874         }, this);
28875         
28876         this.files = [];
28877         
28878         this.refresh();
28879     },
28880     
28881     onClick : function(e, el, o)
28882     {
28883         e.preventDefault();
28884         
28885         this.fireEvent('click', this, o);
28886         
28887     },
28888     
28889     closable : function(closable)
28890     {
28891         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
28892             
28893             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28894             
28895             if(closable){
28896                 el.show();
28897                 return;
28898             }
28899             
28900             el.hide();
28901             
28902         }, this);
28903     },
28904     
28905     xhrOnLoad : function(xhr)
28906     {
28907         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28908             el.remove();
28909         }, this);
28910         
28911         if (xhr.readyState !== 4) {
28912             this.arrange();
28913             this.fireEvent('exception', this, xhr);
28914             return;
28915         }
28916
28917         var response = Roo.decode(xhr.responseText);
28918         
28919         if(!response.success){
28920             this.arrange();
28921             this.fireEvent('exception', this, xhr);
28922             return;
28923         }
28924         
28925         var file = this.renderPreview(response.data);
28926         
28927         this.files.push(file);
28928         
28929         this.arrange();
28930         
28931         this.fireEvent('afterupload', this, xhr);
28932         
28933     },
28934     
28935     xhrOnError : function(xhr)
28936     {
28937         Roo.log('xhr on error');
28938         
28939         var response = Roo.decode(xhr.responseText);
28940           
28941         Roo.log(response);
28942         
28943         this.arrange();
28944     },
28945     
28946     process : function(file)
28947     {
28948         if(this.fireEvent('process', this, file) !== false){
28949             if(this.editable && file.type.indexOf('image') != -1){
28950                 this.fireEvent('edit', this, file);
28951                 return;
28952             }
28953
28954             this.uploadStart(file, false);
28955
28956             return;
28957         }
28958         
28959     },
28960     
28961     uploadStart : function(file, crop)
28962     {
28963         this.xhr = new XMLHttpRequest();
28964         
28965         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28966             this.arrange();
28967             return;
28968         }
28969         
28970         file.xhr = this.xhr;
28971             
28972         this.managerEl.createChild({
28973             tag : 'div',
28974             cls : 'roo-document-manager-loading',
28975             cn : [
28976                 {
28977                     tag : 'div',
28978                     tooltip : file.name,
28979                     cls : 'roo-document-manager-thumb',
28980                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28981                 }
28982             ]
28983
28984         });
28985
28986         this.xhr.open(this.method, this.url, true);
28987         
28988         var headers = {
28989             "Accept": "application/json",
28990             "Cache-Control": "no-cache",
28991             "X-Requested-With": "XMLHttpRequest"
28992         };
28993         
28994         for (var headerName in headers) {
28995             var headerValue = headers[headerName];
28996             if (headerValue) {
28997                 this.xhr.setRequestHeader(headerName, headerValue);
28998             }
28999         }
29000         
29001         var _this = this;
29002         
29003         this.xhr.onload = function()
29004         {
29005             _this.xhrOnLoad(_this.xhr);
29006         }
29007         
29008         this.xhr.onerror = function()
29009         {
29010             _this.xhrOnError(_this.xhr);
29011         }
29012         
29013         var formData = new FormData();
29014
29015         formData.append('returnHTML', 'NO');
29016         
29017         if(crop){
29018             formData.append('crop', crop);
29019         }
29020         
29021         formData.append(this.paramName, file, file.name);
29022         
29023         var options = {
29024             file : file, 
29025             manually : false
29026         };
29027         
29028         if(this.fireEvent('prepare', this, formData, options) != false){
29029             
29030             if(options.manually){
29031                 return;
29032             }
29033             
29034             this.xhr.send(formData);
29035             return;
29036         };
29037         
29038         this.uploadCancel();
29039     },
29040     
29041     uploadCancel : function()
29042     {
29043         if (this.xhr) {
29044             this.xhr.abort();
29045         }
29046         
29047         this.delegates = [];
29048         
29049         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29050             el.remove();
29051         }, this);
29052         
29053         this.arrange();
29054     },
29055     
29056     renderPreview : function(file)
29057     {
29058         if(typeof(file.target) != 'undefined' && file.target){
29059             return file;
29060         }
29061         
29062         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29063         
29064         var previewEl = this.managerEl.createChild({
29065             tag : 'div',
29066             cls : 'roo-document-manager-preview',
29067             cn : [
29068                 {
29069                     tag : 'div',
29070                     tooltip : file[this.toolTipName],
29071                     cls : 'roo-document-manager-thumb',
29072                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29073                 },
29074                 {
29075                     tag : 'button',
29076                     cls : 'close',
29077                     html : '<i class="fa fa-times-circle"></i>'
29078                 }
29079             ]
29080         });
29081
29082         var close = previewEl.select('button.close', true).first();
29083
29084         close.on('click', this.onRemove, this, file);
29085
29086         file.target = previewEl;
29087
29088         var image = previewEl.select('img', true).first();
29089         
29090         var _this = this;
29091         
29092         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29093         
29094         image.on('click', this.onClick, this, file);
29095         
29096         this.fireEvent('previewrendered', this, file);
29097         
29098         return file;
29099         
29100     },
29101     
29102     onPreviewLoad : function(file, image)
29103     {
29104         if(typeof(file.target) == 'undefined' || !file.target){
29105             return;
29106         }
29107         
29108         var width = image.dom.naturalWidth || image.dom.width;
29109         var height = image.dom.naturalHeight || image.dom.height;
29110         
29111         if(width > height){
29112             file.target.addClass('wide');
29113             return;
29114         }
29115         
29116         file.target.addClass('tall');
29117         return;
29118         
29119     },
29120     
29121     uploadFromSource : function(file, crop)
29122     {
29123         this.xhr = new XMLHttpRequest();
29124         
29125         this.managerEl.createChild({
29126             tag : 'div',
29127             cls : 'roo-document-manager-loading',
29128             cn : [
29129                 {
29130                     tag : 'div',
29131                     tooltip : file.name,
29132                     cls : 'roo-document-manager-thumb',
29133                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29134                 }
29135             ]
29136
29137         });
29138
29139         this.xhr.open(this.method, this.url, true);
29140         
29141         var headers = {
29142             "Accept": "application/json",
29143             "Cache-Control": "no-cache",
29144             "X-Requested-With": "XMLHttpRequest"
29145         };
29146         
29147         for (var headerName in headers) {
29148             var headerValue = headers[headerName];
29149             if (headerValue) {
29150                 this.xhr.setRequestHeader(headerName, headerValue);
29151             }
29152         }
29153         
29154         var _this = this;
29155         
29156         this.xhr.onload = function()
29157         {
29158             _this.xhrOnLoad(_this.xhr);
29159         }
29160         
29161         this.xhr.onerror = function()
29162         {
29163             _this.xhrOnError(_this.xhr);
29164         }
29165         
29166         var formData = new FormData();
29167
29168         formData.append('returnHTML', 'NO');
29169         
29170         formData.append('crop', crop);
29171         
29172         if(typeof(file.filename) != 'undefined'){
29173             formData.append('filename', file.filename);
29174         }
29175         
29176         if(typeof(file.mimetype) != 'undefined'){
29177             formData.append('mimetype', file.mimetype);
29178         }
29179         
29180         Roo.log(formData);
29181         
29182         if(this.fireEvent('prepare', this, formData) != false){
29183             this.xhr.send(formData);
29184         };
29185     }
29186 });
29187
29188 /*
29189 * Licence: LGPL
29190 */
29191
29192 /**
29193  * @class Roo.bootstrap.DocumentViewer
29194  * @extends Roo.bootstrap.Component
29195  * Bootstrap DocumentViewer class
29196  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29197  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29198  * 
29199  * @constructor
29200  * Create a new DocumentViewer
29201  * @param {Object} config The config object
29202  */
29203
29204 Roo.bootstrap.DocumentViewer = function(config){
29205     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29206     
29207     this.addEvents({
29208         /**
29209          * @event initial
29210          * Fire after initEvent
29211          * @param {Roo.bootstrap.DocumentViewer} this
29212          */
29213         "initial" : true,
29214         /**
29215          * @event click
29216          * Fire after click
29217          * @param {Roo.bootstrap.DocumentViewer} this
29218          */
29219         "click" : true,
29220         /**
29221          * @event download
29222          * Fire after download button
29223          * @param {Roo.bootstrap.DocumentViewer} this
29224          */
29225         "download" : true,
29226         /**
29227          * @event trash
29228          * Fire after trash button
29229          * @param {Roo.bootstrap.DocumentViewer} this
29230          */
29231         "trash" : true
29232         
29233     });
29234 };
29235
29236 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29237     
29238     showDownload : true,
29239     
29240     showTrash : true,
29241     
29242     getAutoCreate : function()
29243     {
29244         var cfg = {
29245             tag : 'div',
29246             cls : 'roo-document-viewer',
29247             cn : [
29248                 {
29249                     tag : 'div',
29250                     cls : 'roo-document-viewer-body',
29251                     cn : [
29252                         {
29253                             tag : 'div',
29254                             cls : 'roo-document-viewer-thumb',
29255                             cn : [
29256                                 {
29257                                     tag : 'img',
29258                                     cls : 'roo-document-viewer-image'
29259                                 }
29260                             ]
29261                         }
29262                     ]
29263                 },
29264                 {
29265                     tag : 'div',
29266                     cls : 'roo-document-viewer-footer',
29267                     cn : {
29268                         tag : 'div',
29269                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29270                         cn : [
29271                             {
29272                                 tag : 'div',
29273                                 cls : 'btn-group roo-document-viewer-download',
29274                                 cn : [
29275                                     {
29276                                         tag : 'button',
29277                                         cls : 'btn btn-default',
29278                                         html : '<i class="fa fa-download"></i>'
29279                                     }
29280                                 ]
29281                             },
29282                             {
29283                                 tag : 'div',
29284                                 cls : 'btn-group roo-document-viewer-trash',
29285                                 cn : [
29286                                     {
29287                                         tag : 'button',
29288                                         cls : 'btn btn-default',
29289                                         html : '<i class="fa fa-trash"></i>'
29290                                     }
29291                                 ]
29292                             }
29293                         ]
29294                     }
29295                 }
29296             ]
29297         };
29298         
29299         return cfg;
29300     },
29301     
29302     initEvents : function()
29303     {
29304         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29305         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29306         
29307         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29308         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29309         
29310         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29311         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29312         
29313         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29314         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29315         
29316         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29317         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29318         
29319         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29320         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29321         
29322         this.bodyEl.on('click', this.onClick, this);
29323         this.downloadBtn.on('click', this.onDownload, this);
29324         this.trashBtn.on('click', this.onTrash, this);
29325         
29326         this.downloadBtn.hide();
29327         this.trashBtn.hide();
29328         
29329         if(this.showDownload){
29330             this.downloadBtn.show();
29331         }
29332         
29333         if(this.showTrash){
29334             this.trashBtn.show();
29335         }
29336         
29337         if(!this.showDownload && !this.showTrash) {
29338             this.footerEl.hide();
29339         }
29340         
29341     },
29342     
29343     initial : function()
29344     {
29345         this.fireEvent('initial', this);
29346         
29347     },
29348     
29349     onClick : function(e)
29350     {
29351         e.preventDefault();
29352         
29353         this.fireEvent('click', this);
29354     },
29355     
29356     onDownload : function(e)
29357     {
29358         e.preventDefault();
29359         
29360         this.fireEvent('download', this);
29361     },
29362     
29363     onTrash : function(e)
29364     {
29365         e.preventDefault();
29366         
29367         this.fireEvent('trash', this);
29368     }
29369     
29370 });
29371 /*
29372  * - LGPL
29373  *
29374  * nav progress bar
29375  * 
29376  */
29377
29378 /**
29379  * @class Roo.bootstrap.NavProgressBar
29380  * @extends Roo.bootstrap.Component
29381  * Bootstrap NavProgressBar class
29382  * 
29383  * @constructor
29384  * Create a new nav progress bar
29385  * @param {Object} config The config object
29386  */
29387
29388 Roo.bootstrap.NavProgressBar = function(config){
29389     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29390
29391     this.bullets = this.bullets || [];
29392    
29393 //    Roo.bootstrap.NavProgressBar.register(this);
29394      this.addEvents({
29395         /**
29396              * @event changed
29397              * Fires when the active item changes
29398              * @param {Roo.bootstrap.NavProgressBar} this
29399              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29400              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29401          */
29402         'changed': true
29403      });
29404     
29405 };
29406
29407 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29408     
29409     bullets : [],
29410     barItems : [],
29411     
29412     getAutoCreate : function()
29413     {
29414         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29415         
29416         cfg = {
29417             tag : 'div',
29418             cls : 'roo-navigation-bar-group',
29419             cn : [
29420                 {
29421                     tag : 'div',
29422                     cls : 'roo-navigation-top-bar'
29423                 },
29424                 {
29425                     tag : 'div',
29426                     cls : 'roo-navigation-bullets-bar',
29427                     cn : [
29428                         {
29429                             tag : 'ul',
29430                             cls : 'roo-navigation-bar'
29431                         }
29432                     ]
29433                 },
29434                 
29435                 {
29436                     tag : 'div',
29437                     cls : 'roo-navigation-bottom-bar'
29438                 }
29439             ]
29440             
29441         };
29442         
29443         return cfg;
29444         
29445     },
29446     
29447     initEvents: function() 
29448     {
29449         
29450     },
29451     
29452     onRender : function(ct, position) 
29453     {
29454         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29455         
29456         if(this.bullets.length){
29457             Roo.each(this.bullets, function(b){
29458                this.addItem(b);
29459             }, this);
29460         }
29461         
29462         this.format();
29463         
29464     },
29465     
29466     addItem : function(cfg)
29467     {
29468         var item = new Roo.bootstrap.NavProgressItem(cfg);
29469         
29470         item.parentId = this.id;
29471         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29472         
29473         if(cfg.html){
29474             var top = new Roo.bootstrap.Element({
29475                 tag : 'div',
29476                 cls : 'roo-navigation-bar-text'
29477             });
29478             
29479             var bottom = new Roo.bootstrap.Element({
29480                 tag : 'div',
29481                 cls : 'roo-navigation-bar-text'
29482             });
29483             
29484             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29485             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29486             
29487             var topText = new Roo.bootstrap.Element({
29488                 tag : 'span',
29489                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29490             });
29491             
29492             var bottomText = new Roo.bootstrap.Element({
29493                 tag : 'span',
29494                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29495             });
29496             
29497             topText.onRender(top.el, null);
29498             bottomText.onRender(bottom.el, null);
29499             
29500             item.topEl = top;
29501             item.bottomEl = bottom;
29502         }
29503         
29504         this.barItems.push(item);
29505         
29506         return item;
29507     },
29508     
29509     getActive : function()
29510     {
29511         var active = false;
29512         
29513         Roo.each(this.barItems, function(v){
29514             
29515             if (!v.isActive()) {
29516                 return;
29517             }
29518             
29519             active = v;
29520             return false;
29521             
29522         });
29523         
29524         return active;
29525     },
29526     
29527     setActiveItem : function(item)
29528     {
29529         var prev = false;
29530         
29531         Roo.each(this.barItems, function(v){
29532             if (v.rid == item.rid) {
29533                 return ;
29534             }
29535             
29536             if (v.isActive()) {
29537                 v.setActive(false);
29538                 prev = v;
29539             }
29540         });
29541
29542         item.setActive(true);
29543         
29544         this.fireEvent('changed', this, item, prev);
29545     },
29546     
29547     getBarItem: function(rid)
29548     {
29549         var ret = false;
29550         
29551         Roo.each(this.barItems, function(e) {
29552             if (e.rid != rid) {
29553                 return;
29554             }
29555             
29556             ret =  e;
29557             return false;
29558         });
29559         
29560         return ret;
29561     },
29562     
29563     indexOfItem : function(item)
29564     {
29565         var index = false;
29566         
29567         Roo.each(this.barItems, function(v, i){
29568             
29569             if (v.rid != item.rid) {
29570                 return;
29571             }
29572             
29573             index = i;
29574             return false
29575         });
29576         
29577         return index;
29578     },
29579     
29580     setActiveNext : function()
29581     {
29582         var i = this.indexOfItem(this.getActive());
29583         
29584         if (i > this.barItems.length) {
29585             return;
29586         }
29587         
29588         this.setActiveItem(this.barItems[i+1]);
29589     },
29590     
29591     setActivePrev : function()
29592     {
29593         var i = this.indexOfItem(this.getActive());
29594         
29595         if (i  < 1) {
29596             return;
29597         }
29598         
29599         this.setActiveItem(this.barItems[i-1]);
29600     },
29601     
29602     format : function()
29603     {
29604         if(!this.barItems.length){
29605             return;
29606         }
29607      
29608         var width = 100 / this.barItems.length;
29609         
29610         Roo.each(this.barItems, function(i){
29611             i.el.setStyle('width', width + '%');
29612             i.topEl.el.setStyle('width', width + '%');
29613             i.bottomEl.el.setStyle('width', width + '%');
29614         }, this);
29615         
29616     }
29617     
29618 });
29619 /*
29620  * - LGPL
29621  *
29622  * Nav Progress Item
29623  * 
29624  */
29625
29626 /**
29627  * @class Roo.bootstrap.NavProgressItem
29628  * @extends Roo.bootstrap.Component
29629  * Bootstrap NavProgressItem class
29630  * @cfg {String} rid the reference id
29631  * @cfg {Boolean} active (true|false) Is item active default false
29632  * @cfg {Boolean} disabled (true|false) Is item active default false
29633  * @cfg {String} html
29634  * @cfg {String} position (top|bottom) text position default bottom
29635  * @cfg {String} icon show icon instead of number
29636  * 
29637  * @constructor
29638  * Create a new NavProgressItem
29639  * @param {Object} config The config object
29640  */
29641 Roo.bootstrap.NavProgressItem = function(config){
29642     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29643     this.addEvents({
29644         // raw events
29645         /**
29646          * @event click
29647          * The raw click event for the entire grid.
29648          * @param {Roo.bootstrap.NavProgressItem} this
29649          * @param {Roo.EventObject} e
29650          */
29651         "click" : true
29652     });
29653    
29654 };
29655
29656 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29657     
29658     rid : '',
29659     active : false,
29660     disabled : false,
29661     html : '',
29662     position : 'bottom',
29663     icon : false,
29664     
29665     getAutoCreate : function()
29666     {
29667         var iconCls = 'roo-navigation-bar-item-icon';
29668         
29669         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29670         
29671         var cfg = {
29672             tag: 'li',
29673             cls: 'roo-navigation-bar-item',
29674             cn : [
29675                 {
29676                     tag : 'i',
29677                     cls : iconCls
29678                 }
29679             ]
29680         };
29681         
29682         if(this.active){
29683             cfg.cls += ' active';
29684         }
29685         if(this.disabled){
29686             cfg.cls += ' disabled';
29687         }
29688         
29689         return cfg;
29690     },
29691     
29692     disable : function()
29693     {
29694         this.setDisabled(true);
29695     },
29696     
29697     enable : function()
29698     {
29699         this.setDisabled(false);
29700     },
29701     
29702     initEvents: function() 
29703     {
29704         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29705         
29706         this.iconEl.on('click', this.onClick, this);
29707     },
29708     
29709     onClick : function(e)
29710     {
29711         e.preventDefault();
29712         
29713         if(this.disabled){
29714             return;
29715         }
29716         
29717         if(this.fireEvent('click', this, e) === false){
29718             return;
29719         };
29720         
29721         this.parent().setActiveItem(this);
29722     },
29723     
29724     isActive: function () 
29725     {
29726         return this.active;
29727     },
29728     
29729     setActive : function(state)
29730     {
29731         if(this.active == state){
29732             return;
29733         }
29734         
29735         this.active = state;
29736         
29737         if (state) {
29738             this.el.addClass('active');
29739             return;
29740         }
29741         
29742         this.el.removeClass('active');
29743         
29744         return;
29745     },
29746     
29747     setDisabled : function(state)
29748     {
29749         if(this.disabled == state){
29750             return;
29751         }
29752         
29753         this.disabled = state;
29754         
29755         if (state) {
29756             this.el.addClass('disabled');
29757             return;
29758         }
29759         
29760         this.el.removeClass('disabled');
29761     },
29762     
29763     tooltipEl : function()
29764     {
29765         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29766     }
29767 });
29768  
29769
29770  /*
29771  * - LGPL
29772  *
29773  * FieldLabel
29774  * 
29775  */
29776
29777 /**
29778  * @class Roo.bootstrap.FieldLabel
29779  * @extends Roo.bootstrap.Component
29780  * Bootstrap FieldLabel class
29781  * @cfg {String} html contents of the element
29782  * @cfg {String} tag tag of the element default label
29783  * @cfg {String} cls class of the element
29784  * @cfg {String} target label target 
29785  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29786  * @cfg {String} invalidClass default "text-warning"
29787  * @cfg {String} validClass default "text-success"
29788  * @cfg {String} iconTooltip default "This field is required"
29789  * @cfg {String} indicatorpos (left|right) default left
29790  * 
29791  * @constructor
29792  * Create a new FieldLabel
29793  * @param {Object} config The config object
29794  */
29795
29796 Roo.bootstrap.FieldLabel = function(config){
29797     Roo.bootstrap.Element.superclass.constructor.call(this, config);
29798     
29799     this.addEvents({
29800             /**
29801              * @event invalid
29802              * Fires after the field has been marked as invalid.
29803              * @param {Roo.form.FieldLabel} this
29804              * @param {String} msg The validation message
29805              */
29806             invalid : true,
29807             /**
29808              * @event valid
29809              * Fires after the field has been validated with no errors.
29810              * @param {Roo.form.FieldLabel} this
29811              */
29812             valid : true
29813         });
29814 };
29815
29816 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
29817     
29818     tag: 'label',
29819     cls: '',
29820     html: '',
29821     target: '',
29822     allowBlank : true,
29823     invalidClass : 'has-warning',
29824     validClass : 'has-success',
29825     iconTooltip : 'This field is required',
29826     indicatorpos : 'left',
29827     
29828     getAutoCreate : function(){
29829         
29830         var cfg = {
29831             tag : this.tag,
29832             cls : 'roo-bootstrap-field-label ' + this.cls,
29833             for : this.target,
29834             cn : [
29835                 {
29836                     tag : 'i',
29837                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
29838                     tooltip : this.iconTooltip
29839                 },
29840                 {
29841                     tag : 'span',
29842                     html : this.html
29843                 }
29844             ] 
29845         };
29846         
29847         if(this.indicatorpos == 'right'){
29848             var cfg = {
29849                 tag : this.tag,
29850                 cls : 'roo-bootstrap-field-label ' + this.cls,
29851                 for : this.target,
29852                 cn : [
29853                     {
29854                         tag : 'span',
29855                         html : this.html
29856                     },
29857                     {
29858                         tag : 'i',
29859                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
29860                         tooltip : this.iconTooltip
29861                     }
29862                 ] 
29863             };
29864         }
29865         
29866         return cfg;
29867     },
29868     
29869     initEvents: function() 
29870     {
29871         Roo.bootstrap.Element.superclass.initEvents.call(this);
29872         
29873         this.indicator = this.indicatorEl();
29874         
29875         if(this.indicator){
29876             this.indicator.removeClass('visible');
29877             this.indicator.addClass('invisible');
29878         }
29879         
29880         Roo.bootstrap.FieldLabel.register(this);
29881     },
29882     
29883     indicatorEl : function()
29884     {
29885         var indicator = this.el.select('i.roo-required-indicator',true).first();
29886         
29887         if(!indicator){
29888             return false;
29889         }
29890         
29891         return indicator;
29892         
29893     },
29894     
29895     /**
29896      * Mark this field as valid
29897      */
29898     markValid : function()
29899     {
29900         if(this.indicator){
29901             this.indicator.removeClass('visible');
29902             this.indicator.addClass('invisible');
29903         }
29904         
29905         this.el.removeClass(this.invalidClass);
29906         
29907         this.el.addClass(this.validClass);
29908         
29909         this.fireEvent('valid', this);
29910     },
29911     
29912     /**
29913      * Mark this field as invalid
29914      * @param {String} msg The validation message
29915      */
29916     markInvalid : function(msg)
29917     {
29918         if(this.indicator){
29919             this.indicator.removeClass('invisible');
29920             this.indicator.addClass('visible');
29921         }
29922         
29923         this.el.removeClass(this.validClass);
29924         
29925         this.el.addClass(this.invalidClass);
29926         
29927         this.fireEvent('invalid', this, msg);
29928     }
29929     
29930    
29931 });
29932
29933 Roo.apply(Roo.bootstrap.FieldLabel, {
29934     
29935     groups: {},
29936     
29937      /**
29938     * register a FieldLabel Group
29939     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
29940     */
29941     register : function(label)
29942     {
29943         if(this.groups.hasOwnProperty(label.target)){
29944             return;
29945         }
29946      
29947         this.groups[label.target] = label;
29948         
29949     },
29950     /**
29951     * fetch a FieldLabel Group based on the target
29952     * @param {string} target
29953     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
29954     */
29955     get: function(target) {
29956         if (typeof(this.groups[target]) == 'undefined') {
29957             return false;
29958         }
29959         
29960         return this.groups[target] ;
29961     }
29962 });
29963
29964  
29965
29966  /*
29967  * - LGPL
29968  *
29969  * page DateSplitField.
29970  * 
29971  */
29972
29973
29974 /**
29975  * @class Roo.bootstrap.DateSplitField
29976  * @extends Roo.bootstrap.Component
29977  * Bootstrap DateSplitField class
29978  * @cfg {string} fieldLabel - the label associated
29979  * @cfg {Number} labelWidth set the width of label (0-12)
29980  * @cfg {String} labelAlign (top|left)
29981  * @cfg {Boolean} dayAllowBlank (true|false) default false
29982  * @cfg {Boolean} monthAllowBlank (true|false) default false
29983  * @cfg {Boolean} yearAllowBlank (true|false) default false
29984  * @cfg {string} dayPlaceholder 
29985  * @cfg {string} monthPlaceholder
29986  * @cfg {string} yearPlaceholder
29987  * @cfg {string} dayFormat default 'd'
29988  * @cfg {string} monthFormat default 'm'
29989  * @cfg {string} yearFormat default 'Y'
29990  * @cfg {Number} labellg set the width of label (1-12)
29991  * @cfg {Number} labelmd set the width of label (1-12)
29992  * @cfg {Number} labelsm set the width of label (1-12)
29993  * @cfg {Number} labelxs set the width of label (1-12)
29994
29995  *     
29996  * @constructor
29997  * Create a new DateSplitField
29998  * @param {Object} config The config object
29999  */
30000
30001 Roo.bootstrap.DateSplitField = function(config){
30002     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30003     
30004     this.addEvents({
30005         // raw events
30006          /**
30007          * @event years
30008          * getting the data of years
30009          * @param {Roo.bootstrap.DateSplitField} this
30010          * @param {Object} years
30011          */
30012         "years" : true,
30013         /**
30014          * @event days
30015          * getting the data of days
30016          * @param {Roo.bootstrap.DateSplitField} this
30017          * @param {Object} days
30018          */
30019         "days" : true,
30020         /**
30021          * @event invalid
30022          * Fires after the field has been marked as invalid.
30023          * @param {Roo.form.Field} this
30024          * @param {String} msg The validation message
30025          */
30026         invalid : true,
30027        /**
30028          * @event valid
30029          * Fires after the field has been validated with no errors.
30030          * @param {Roo.form.Field} this
30031          */
30032         valid : true
30033     });
30034 };
30035
30036 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30037     
30038     fieldLabel : '',
30039     labelAlign : 'top',
30040     labelWidth : 3,
30041     dayAllowBlank : false,
30042     monthAllowBlank : false,
30043     yearAllowBlank : false,
30044     dayPlaceholder : '',
30045     monthPlaceholder : '',
30046     yearPlaceholder : '',
30047     dayFormat : 'd',
30048     monthFormat : 'm',
30049     yearFormat : 'Y',
30050     isFormField : true,
30051     labellg : 0,
30052     labelmd : 0,
30053     labelsm : 0,
30054     labelxs : 0,
30055     
30056     getAutoCreate : function()
30057     {
30058         var cfg = {
30059             tag : 'div',
30060             cls : 'row roo-date-split-field-group',
30061             cn : [
30062                 {
30063                     tag : 'input',
30064                     type : 'hidden',
30065                     cls : 'form-hidden-field roo-date-split-field-group-value',
30066                     name : this.name
30067                 }
30068             ]
30069         };
30070         
30071         var labelCls = 'col-md-12';
30072         var contentCls = 'col-md-4';
30073         
30074         if(this.fieldLabel){
30075             
30076             var label = {
30077                 tag : 'div',
30078                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30079                 cn : [
30080                     {
30081                         tag : 'label',
30082                         html : this.fieldLabel
30083                     }
30084                 ]
30085             };
30086             
30087             if(this.labelAlign == 'left'){
30088             
30089                 if(this.labelWidth > 12){
30090                     label.style = "width: " + this.labelWidth + 'px';
30091                 }
30092
30093                 if(this.labelWidth < 13 && this.labelmd == 0){
30094                     this.labelmd = this.labelWidth;
30095                 }
30096
30097                 if(this.labellg > 0){
30098                     labelCls = ' col-lg-' + this.labellg;
30099                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30100                 }
30101
30102                 if(this.labelmd > 0){
30103                     labelCls = ' col-md-' + this.labelmd;
30104                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30105                 }
30106
30107                 if(this.labelsm > 0){
30108                     labelCls = ' col-sm-' + this.labelsm;
30109                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30110                 }
30111
30112                 if(this.labelxs > 0){
30113                     labelCls = ' col-xs-' + this.labelxs;
30114                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30115                 }
30116             }
30117             
30118             label.cls += ' ' + labelCls;
30119             
30120             cfg.cn.push(label);
30121         }
30122         
30123         Roo.each(['day', 'month', 'year'], function(t){
30124             cfg.cn.push({
30125                 tag : 'div',
30126                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30127             });
30128         }, this);
30129         
30130         return cfg;
30131     },
30132     
30133     inputEl: function ()
30134     {
30135         return this.el.select('.roo-date-split-field-group-value', true).first();
30136     },
30137     
30138     onRender : function(ct, position) 
30139     {
30140         var _this = this;
30141         
30142         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30143         
30144         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30145         
30146         this.dayField = new Roo.bootstrap.ComboBox({
30147             allowBlank : this.dayAllowBlank,
30148             alwaysQuery : true,
30149             displayField : 'value',
30150             editable : false,
30151             fieldLabel : '',
30152             forceSelection : true,
30153             mode : 'local',
30154             placeholder : this.dayPlaceholder,
30155             selectOnFocus : true,
30156             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30157             triggerAction : 'all',
30158             typeAhead : true,
30159             valueField : 'value',
30160             store : new Roo.data.SimpleStore({
30161                 data : (function() {    
30162                     var days = [];
30163                     _this.fireEvent('days', _this, days);
30164                     return days;
30165                 })(),
30166                 fields : [ 'value' ]
30167             }),
30168             listeners : {
30169                 select : function (_self, record, index)
30170                 {
30171                     _this.setValue(_this.getValue());
30172                 }
30173             }
30174         });
30175
30176         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30177         
30178         this.monthField = new Roo.bootstrap.MonthField({
30179             after : '<i class=\"fa fa-calendar\"></i>',
30180             allowBlank : this.monthAllowBlank,
30181             placeholder : this.monthPlaceholder,
30182             readOnly : true,
30183             listeners : {
30184                 render : function (_self)
30185                 {
30186                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30187                         e.preventDefault();
30188                         _self.focus();
30189                     });
30190                 },
30191                 select : function (_self, oldvalue, newvalue)
30192                 {
30193                     _this.setValue(_this.getValue());
30194                 }
30195             }
30196         });
30197         
30198         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30199         
30200         this.yearField = new Roo.bootstrap.ComboBox({
30201             allowBlank : this.yearAllowBlank,
30202             alwaysQuery : true,
30203             displayField : 'value',
30204             editable : false,
30205             fieldLabel : '',
30206             forceSelection : true,
30207             mode : 'local',
30208             placeholder : this.yearPlaceholder,
30209             selectOnFocus : true,
30210             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30211             triggerAction : 'all',
30212             typeAhead : true,
30213             valueField : 'value',
30214             store : new Roo.data.SimpleStore({
30215                 data : (function() {
30216                     var years = [];
30217                     _this.fireEvent('years', _this, years);
30218                     return years;
30219                 })(),
30220                 fields : [ 'value' ]
30221             }),
30222             listeners : {
30223                 select : function (_self, record, index)
30224                 {
30225                     _this.setValue(_this.getValue());
30226                 }
30227             }
30228         });
30229
30230         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30231     },
30232     
30233     setValue : function(v, format)
30234     {
30235         this.inputEl.dom.value = v;
30236         
30237         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30238         
30239         var d = Date.parseDate(v, f);
30240         
30241         if(!d){
30242             this.validate();
30243             return;
30244         }
30245         
30246         this.setDay(d.format(this.dayFormat));
30247         this.setMonth(d.format(this.monthFormat));
30248         this.setYear(d.format(this.yearFormat));
30249         
30250         this.validate();
30251         
30252         return;
30253     },
30254     
30255     setDay : function(v)
30256     {
30257         this.dayField.setValue(v);
30258         this.inputEl.dom.value = this.getValue();
30259         this.validate();
30260         return;
30261     },
30262     
30263     setMonth : function(v)
30264     {
30265         this.monthField.setValue(v, true);
30266         this.inputEl.dom.value = this.getValue();
30267         this.validate();
30268         return;
30269     },
30270     
30271     setYear : function(v)
30272     {
30273         this.yearField.setValue(v);
30274         this.inputEl.dom.value = this.getValue();
30275         this.validate();
30276         return;
30277     },
30278     
30279     getDay : function()
30280     {
30281         return this.dayField.getValue();
30282     },
30283     
30284     getMonth : function()
30285     {
30286         return this.monthField.getValue();
30287     },
30288     
30289     getYear : function()
30290     {
30291         return this.yearField.getValue();
30292     },
30293     
30294     getValue : function()
30295     {
30296         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30297         
30298         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30299         
30300         return date;
30301     },
30302     
30303     reset : function()
30304     {
30305         this.setDay('');
30306         this.setMonth('');
30307         this.setYear('');
30308         this.inputEl.dom.value = '';
30309         this.validate();
30310         return;
30311     },
30312     
30313     validate : function()
30314     {
30315         var d = this.dayField.validate();
30316         var m = this.monthField.validate();
30317         var y = this.yearField.validate();
30318         
30319         var valid = true;
30320         
30321         if(
30322                 (!this.dayAllowBlank && !d) ||
30323                 (!this.monthAllowBlank && !m) ||
30324                 (!this.yearAllowBlank && !y)
30325         ){
30326             valid = false;
30327         }
30328         
30329         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30330             return valid;
30331         }
30332         
30333         if(valid){
30334             this.markValid();
30335             return valid;
30336         }
30337         
30338         this.markInvalid();
30339         
30340         return valid;
30341     },
30342     
30343     markValid : function()
30344     {
30345         
30346         var label = this.el.select('label', true).first();
30347         var icon = this.el.select('i.fa-star', true).first();
30348
30349         if(label && icon){
30350             icon.remove();
30351         }
30352         
30353         this.fireEvent('valid', this);
30354     },
30355     
30356      /**
30357      * Mark this field as invalid
30358      * @param {String} msg The validation message
30359      */
30360     markInvalid : function(msg)
30361     {
30362         
30363         var label = this.el.select('label', true).first();
30364         var icon = this.el.select('i.fa-star', true).first();
30365
30366         if(label && !icon){
30367             this.el.select('.roo-date-split-field-label', true).createChild({
30368                 tag : 'i',
30369                 cls : 'text-danger fa fa-lg fa-star',
30370                 tooltip : 'This field is required',
30371                 style : 'margin-right:5px;'
30372             }, label, true);
30373         }
30374         
30375         this.fireEvent('invalid', this, msg);
30376     },
30377     
30378     clearInvalid : function()
30379     {
30380         var label = this.el.select('label', true).first();
30381         var icon = this.el.select('i.fa-star', true).first();
30382
30383         if(label && icon){
30384             icon.remove();
30385         }
30386         
30387         this.fireEvent('valid', this);
30388     },
30389     
30390     getName: function()
30391     {
30392         return this.name;
30393     }
30394     
30395 });
30396
30397  /**
30398  *
30399  * This is based on 
30400  * http://masonry.desandro.com
30401  *
30402  * The idea is to render all the bricks based on vertical width...
30403  *
30404  * The original code extends 'outlayer' - we might need to use that....
30405  * 
30406  */
30407
30408
30409 /**
30410  * @class Roo.bootstrap.LayoutMasonry
30411  * @extends Roo.bootstrap.Component
30412  * Bootstrap Layout Masonry class
30413  * 
30414  * @constructor
30415  * Create a new Element
30416  * @param {Object} config The config object
30417  */
30418
30419 Roo.bootstrap.LayoutMasonry = function(config){
30420     
30421     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30422     
30423     this.bricks = [];
30424     
30425     Roo.bootstrap.LayoutMasonry.register(this);
30426     
30427     this.addEvents({
30428         // raw events
30429         /**
30430          * @event layout
30431          * Fire after layout the items
30432          * @param {Roo.bootstrap.LayoutMasonry} this
30433          * @param {Roo.EventObject} e
30434          */
30435         "layout" : true
30436     });
30437     
30438 };
30439
30440 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30441     
30442     /**
30443      * @cfg {Boolean} isLayoutInstant = no animation?
30444      */   
30445     isLayoutInstant : false, // needed?
30446    
30447     /**
30448      * @cfg {Number} boxWidth  width of the columns
30449      */   
30450     boxWidth : 450,
30451     
30452       /**
30453      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30454      */   
30455     boxHeight : 0,
30456     
30457     /**
30458      * @cfg {Number} padWidth padding below box..
30459      */   
30460     padWidth : 10, 
30461     
30462     /**
30463      * @cfg {Number} gutter gutter width..
30464      */   
30465     gutter : 10,
30466     
30467      /**
30468      * @cfg {Number} maxCols maximum number of columns
30469      */   
30470     
30471     maxCols: 0,
30472     
30473     /**
30474      * @cfg {Boolean} isAutoInitial defalut true
30475      */   
30476     isAutoInitial : true, 
30477     
30478     containerWidth: 0,
30479     
30480     /**
30481      * @cfg {Boolean} isHorizontal defalut false
30482      */   
30483     isHorizontal : false, 
30484
30485     currentSize : null,
30486     
30487     tag: 'div',
30488     
30489     cls: '',
30490     
30491     bricks: null, //CompositeElement
30492     
30493     cols : 1,
30494     
30495     _isLayoutInited : false,
30496     
30497 //    isAlternative : false, // only use for vertical layout...
30498     
30499     /**
30500      * @cfg {Number} alternativePadWidth padding below box..
30501      */   
30502     alternativePadWidth : 50,
30503     
30504     selectedBrick : [],
30505     
30506     getAutoCreate : function(){
30507         
30508         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30509         
30510         var cfg = {
30511             tag: this.tag,
30512             cls: 'blog-masonary-wrapper ' + this.cls,
30513             cn : {
30514                 cls : 'mas-boxes masonary'
30515             }
30516         };
30517         
30518         return cfg;
30519     },
30520     
30521     getChildContainer: function( )
30522     {
30523         if (this.boxesEl) {
30524             return this.boxesEl;
30525         }
30526         
30527         this.boxesEl = this.el.select('.mas-boxes').first();
30528         
30529         return this.boxesEl;
30530     },
30531     
30532     
30533     initEvents : function()
30534     {
30535         var _this = this;
30536         
30537         if(this.isAutoInitial){
30538             Roo.log('hook children rendered');
30539             this.on('childrenrendered', function() {
30540                 Roo.log('children rendered');
30541                 _this.initial();
30542             } ,this);
30543         }
30544     },
30545     
30546     initial : function()
30547     {
30548         this.selectedBrick = [];
30549         
30550         this.currentSize = this.el.getBox(true);
30551         
30552         Roo.EventManager.onWindowResize(this.resize, this); 
30553
30554         if(!this.isAutoInitial){
30555             this.layout();
30556             return;
30557         }
30558         
30559         this.layout();
30560         
30561         return;
30562         //this.layout.defer(500,this);
30563         
30564     },
30565     
30566     resize : function()
30567     {
30568         var cs = this.el.getBox(true);
30569         
30570         if (
30571                 this.currentSize.width == cs.width && 
30572                 this.currentSize.x == cs.x && 
30573                 this.currentSize.height == cs.height && 
30574                 this.currentSize.y == cs.y 
30575         ) {
30576             Roo.log("no change in with or X or Y");
30577             return;
30578         }
30579         
30580         this.currentSize = cs;
30581         
30582         this.layout();
30583         
30584     },
30585     
30586     layout : function()
30587     {   
30588         this._resetLayout();
30589         
30590         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30591         
30592         this.layoutItems( isInstant );
30593       
30594         this._isLayoutInited = true;
30595         
30596         this.fireEvent('layout', this);
30597         
30598     },
30599     
30600     _resetLayout : function()
30601     {
30602         if(this.isHorizontal){
30603             this.horizontalMeasureColumns();
30604             return;
30605         }
30606         
30607         this.verticalMeasureColumns();
30608         
30609     },
30610     
30611     verticalMeasureColumns : function()
30612     {
30613         this.getContainerWidth();
30614         
30615 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30616 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30617 //            return;
30618 //        }
30619         
30620         var boxWidth = this.boxWidth + this.padWidth;
30621         
30622         if(this.containerWidth < this.boxWidth){
30623             boxWidth = this.containerWidth
30624         }
30625         
30626         var containerWidth = this.containerWidth;
30627         
30628         var cols = Math.floor(containerWidth / boxWidth);
30629         
30630         this.cols = Math.max( cols, 1 );
30631         
30632         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30633         
30634         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30635         
30636         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30637         
30638         this.colWidth = boxWidth + avail - this.padWidth;
30639         
30640         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30641         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30642     },
30643     
30644     horizontalMeasureColumns : function()
30645     {
30646         this.getContainerWidth();
30647         
30648         var boxWidth = this.boxWidth;
30649         
30650         if(this.containerWidth < boxWidth){
30651             boxWidth = this.containerWidth;
30652         }
30653         
30654         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30655         
30656         this.el.setHeight(boxWidth);
30657         
30658     },
30659     
30660     getContainerWidth : function()
30661     {
30662         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30663     },
30664     
30665     layoutItems : function( isInstant )
30666     {
30667         Roo.log(this.bricks);
30668         
30669         var items = Roo.apply([], this.bricks);
30670         
30671         if(this.isHorizontal){
30672             this._horizontalLayoutItems( items , isInstant );
30673             return;
30674         }
30675         
30676 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30677 //            this._verticalAlternativeLayoutItems( items , isInstant );
30678 //            return;
30679 //        }
30680         
30681         this._verticalLayoutItems( items , isInstant );
30682         
30683     },
30684     
30685     _verticalLayoutItems : function ( items , isInstant)
30686     {
30687         if ( !items || !items.length ) {
30688             return;
30689         }
30690         
30691         var standard = [
30692             ['xs', 'xs', 'xs', 'tall'],
30693             ['xs', 'xs', 'tall'],
30694             ['xs', 'xs', 'sm'],
30695             ['xs', 'xs', 'xs'],
30696             ['xs', 'tall'],
30697             ['xs', 'sm'],
30698             ['xs', 'xs'],
30699             ['xs'],
30700             
30701             ['sm', 'xs', 'xs'],
30702             ['sm', 'xs'],
30703             ['sm'],
30704             
30705             ['tall', 'xs', 'xs', 'xs'],
30706             ['tall', 'xs', 'xs'],
30707             ['tall', 'xs'],
30708             ['tall']
30709             
30710         ];
30711         
30712         var queue = [];
30713         
30714         var boxes = [];
30715         
30716         var box = [];
30717         
30718         Roo.each(items, function(item, k){
30719             
30720             switch (item.size) {
30721                 // these layouts take up a full box,
30722                 case 'md' :
30723                 case 'md-left' :
30724                 case 'md-right' :
30725                 case 'wide' :
30726                     
30727                     if(box.length){
30728                         boxes.push(box);
30729                         box = [];
30730                     }
30731                     
30732                     boxes.push([item]);
30733                     
30734                     break;
30735                     
30736                 case 'xs' :
30737                 case 'sm' :
30738                 case 'tall' :
30739                     
30740                     box.push(item);
30741                     
30742                     break;
30743                 default :
30744                     break;
30745                     
30746             }
30747             
30748         }, this);
30749         
30750         if(box.length){
30751             boxes.push(box);
30752             box = [];
30753         }
30754         
30755         var filterPattern = function(box, length)
30756         {
30757             if(!box.length){
30758                 return;
30759             }
30760             
30761             var match = false;
30762             
30763             var pattern = box.slice(0, length);
30764             
30765             var format = [];
30766             
30767             Roo.each(pattern, function(i){
30768                 format.push(i.size);
30769             }, this);
30770             
30771             Roo.each(standard, function(s){
30772                 
30773                 if(String(s) != String(format)){
30774                     return;
30775                 }
30776                 
30777                 match = true;
30778                 return false;
30779                 
30780             }, this);
30781             
30782             if(!match && length == 1){
30783                 return;
30784             }
30785             
30786             if(!match){
30787                 filterPattern(box, length - 1);
30788                 return;
30789             }
30790                 
30791             queue.push(pattern);
30792
30793             box = box.slice(length, box.length);
30794
30795             filterPattern(box, 4);
30796
30797             return;
30798             
30799         }
30800         
30801         Roo.each(boxes, function(box, k){
30802             
30803             if(!box.length){
30804                 return;
30805             }
30806             
30807             if(box.length == 1){
30808                 queue.push(box);
30809                 return;
30810             }
30811             
30812             filterPattern(box, 4);
30813             
30814         }, this);
30815         
30816         this._processVerticalLayoutQueue( queue, isInstant );
30817         
30818     },
30819     
30820 //    _verticalAlternativeLayoutItems : function( items , isInstant )
30821 //    {
30822 //        if ( !items || !items.length ) {
30823 //            return;
30824 //        }
30825 //
30826 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
30827 //        
30828 //    },
30829     
30830     _horizontalLayoutItems : function ( items , isInstant)
30831     {
30832         if ( !items || !items.length || items.length < 3) {
30833             return;
30834         }
30835         
30836         items.reverse();
30837         
30838         var eItems = items.slice(0, 3);
30839         
30840         items = items.slice(3, items.length);
30841         
30842         var standard = [
30843             ['xs', 'xs', 'xs', 'wide'],
30844             ['xs', 'xs', 'wide'],
30845             ['xs', 'xs', 'sm'],
30846             ['xs', 'xs', 'xs'],
30847             ['xs', 'wide'],
30848             ['xs', 'sm'],
30849             ['xs', 'xs'],
30850             ['xs'],
30851             
30852             ['sm', 'xs', 'xs'],
30853             ['sm', 'xs'],
30854             ['sm'],
30855             
30856             ['wide', 'xs', 'xs', 'xs'],
30857             ['wide', 'xs', 'xs'],
30858             ['wide', 'xs'],
30859             ['wide'],
30860             
30861             ['wide-thin']
30862         ];
30863         
30864         var queue = [];
30865         
30866         var boxes = [];
30867         
30868         var box = [];
30869         
30870         Roo.each(items, function(item, k){
30871             
30872             switch (item.size) {
30873                 case 'md' :
30874                 case 'md-left' :
30875                 case 'md-right' :
30876                 case 'tall' :
30877                     
30878                     if(box.length){
30879                         boxes.push(box);
30880                         box = [];
30881                     }
30882                     
30883                     boxes.push([item]);
30884                     
30885                     break;
30886                     
30887                 case 'xs' :
30888                 case 'sm' :
30889                 case 'wide' :
30890                 case 'wide-thin' :
30891                     
30892                     box.push(item);
30893                     
30894                     break;
30895                 default :
30896                     break;
30897                     
30898             }
30899             
30900         }, this);
30901         
30902         if(box.length){
30903             boxes.push(box);
30904             box = [];
30905         }
30906         
30907         var filterPattern = function(box, length)
30908         {
30909             if(!box.length){
30910                 return;
30911             }
30912             
30913             var match = false;
30914             
30915             var pattern = box.slice(0, length);
30916             
30917             var format = [];
30918             
30919             Roo.each(pattern, function(i){
30920                 format.push(i.size);
30921             }, this);
30922             
30923             Roo.each(standard, function(s){
30924                 
30925                 if(String(s) != String(format)){
30926                     return;
30927                 }
30928                 
30929                 match = true;
30930                 return false;
30931                 
30932             }, this);
30933             
30934             if(!match && length == 1){
30935                 return;
30936             }
30937             
30938             if(!match){
30939                 filterPattern(box, length - 1);
30940                 return;
30941             }
30942                 
30943             queue.push(pattern);
30944
30945             box = box.slice(length, box.length);
30946
30947             filterPattern(box, 4);
30948
30949             return;
30950             
30951         }
30952         
30953         Roo.each(boxes, function(box, k){
30954             
30955             if(!box.length){
30956                 return;
30957             }
30958             
30959             if(box.length == 1){
30960                 queue.push(box);
30961                 return;
30962             }
30963             
30964             filterPattern(box, 4);
30965             
30966         }, this);
30967         
30968         
30969         var prune = [];
30970         
30971         var pos = this.el.getBox(true);
30972         
30973         var minX = pos.x;
30974         
30975         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30976         
30977         var hit_end = false;
30978         
30979         Roo.each(queue, function(box){
30980             
30981             if(hit_end){
30982                 
30983                 Roo.each(box, function(b){
30984                 
30985                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30986                     b.el.hide();
30987
30988                 }, this);
30989
30990                 return;
30991             }
30992             
30993             var mx = 0;
30994             
30995             Roo.each(box, function(b){
30996                 
30997                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
30998                 b.el.show();
30999
31000                 mx = Math.max(mx, b.x);
31001                 
31002             }, this);
31003             
31004             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31005             
31006             if(maxX < minX){
31007                 
31008                 Roo.each(box, function(b){
31009                 
31010                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31011                     b.el.hide();
31012                     
31013                 }, this);
31014                 
31015                 hit_end = true;
31016                 
31017                 return;
31018             }
31019             
31020             prune.push(box);
31021             
31022         }, this);
31023         
31024         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31025     },
31026     
31027     /** Sets position of item in DOM
31028     * @param {Element} item
31029     * @param {Number} x - horizontal position
31030     * @param {Number} y - vertical position
31031     * @param {Boolean} isInstant - disables transitions
31032     */
31033     _processVerticalLayoutQueue : function( queue, isInstant )
31034     {
31035         var pos = this.el.getBox(true);
31036         var x = pos.x;
31037         var y = pos.y;
31038         var maxY = [];
31039         
31040         for (var i = 0; i < this.cols; i++){
31041             maxY[i] = pos.y;
31042         }
31043         
31044         Roo.each(queue, function(box, k){
31045             
31046             var col = k % this.cols;
31047             
31048             Roo.each(box, function(b,kk){
31049                 
31050                 b.el.position('absolute');
31051                 
31052                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31053                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31054                 
31055                 if(b.size == 'md-left' || b.size == 'md-right'){
31056                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31057                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31058                 }
31059                 
31060                 b.el.setWidth(width);
31061                 b.el.setHeight(height);
31062                 // iframe?
31063                 b.el.select('iframe',true).setSize(width,height);
31064                 
31065             }, this);
31066             
31067             for (var i = 0; i < this.cols; i++){
31068                 
31069                 if(maxY[i] < maxY[col]){
31070                     col = i;
31071                     continue;
31072                 }
31073                 
31074                 col = Math.min(col, i);
31075                 
31076             }
31077             
31078             x = pos.x + col * (this.colWidth + this.padWidth);
31079             
31080             y = maxY[col];
31081             
31082             var positions = [];
31083             
31084             switch (box.length){
31085                 case 1 :
31086                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31087                     break;
31088                 case 2 :
31089                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31090                     break;
31091                 case 3 :
31092                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31093                     break;
31094                 case 4 :
31095                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31096                     break;
31097                 default :
31098                     break;
31099             }
31100             
31101             Roo.each(box, function(b,kk){
31102                 
31103                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31104                 
31105                 var sz = b.el.getSize();
31106                 
31107                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31108                 
31109             }, this);
31110             
31111         }, this);
31112         
31113         var mY = 0;
31114         
31115         for (var i = 0; i < this.cols; i++){
31116             mY = Math.max(mY, maxY[i]);
31117         }
31118         
31119         this.el.setHeight(mY - pos.y);
31120         
31121     },
31122     
31123 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31124 //    {
31125 //        var pos = this.el.getBox(true);
31126 //        var x = pos.x;
31127 //        var y = pos.y;
31128 //        var maxX = pos.right;
31129 //        
31130 //        var maxHeight = 0;
31131 //        
31132 //        Roo.each(items, function(item, k){
31133 //            
31134 //            var c = k % 2;
31135 //            
31136 //            item.el.position('absolute');
31137 //                
31138 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31139 //
31140 //            item.el.setWidth(width);
31141 //
31142 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31143 //
31144 //            item.el.setHeight(height);
31145 //            
31146 //            if(c == 0){
31147 //                item.el.setXY([x, y], isInstant ? false : true);
31148 //            } else {
31149 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31150 //            }
31151 //            
31152 //            y = y + height + this.alternativePadWidth;
31153 //            
31154 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31155 //            
31156 //        }, this);
31157 //        
31158 //        this.el.setHeight(maxHeight);
31159 //        
31160 //    },
31161     
31162     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31163     {
31164         var pos = this.el.getBox(true);
31165         
31166         var minX = pos.x;
31167         var minY = pos.y;
31168         
31169         var maxX = pos.right;
31170         
31171         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31172         
31173         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31174         
31175         Roo.each(queue, function(box, k){
31176             
31177             Roo.each(box, function(b, kk){
31178                 
31179                 b.el.position('absolute');
31180                 
31181                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31182                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31183                 
31184                 if(b.size == 'md-left' || b.size == 'md-right'){
31185                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31186                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31187                 }
31188                 
31189                 b.el.setWidth(width);
31190                 b.el.setHeight(height);
31191                 
31192             }, this);
31193             
31194             if(!box.length){
31195                 return;
31196             }
31197             
31198             var positions = [];
31199             
31200             switch (box.length){
31201                 case 1 :
31202                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31203                     break;
31204                 case 2 :
31205                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31206                     break;
31207                 case 3 :
31208                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31209                     break;
31210                 case 4 :
31211                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31212                     break;
31213                 default :
31214                     break;
31215             }
31216             
31217             Roo.each(box, function(b,kk){
31218                 
31219                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31220                 
31221                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31222                 
31223             }, this);
31224             
31225         }, this);
31226         
31227     },
31228     
31229     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31230     {
31231         Roo.each(eItems, function(b,k){
31232             
31233             b.size = (k == 0) ? 'sm' : 'xs';
31234             b.x = (k == 0) ? 2 : 1;
31235             b.y = (k == 0) ? 2 : 1;
31236             
31237             b.el.position('absolute');
31238             
31239             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31240                 
31241             b.el.setWidth(width);
31242             
31243             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31244             
31245             b.el.setHeight(height);
31246             
31247         }, this);
31248
31249         var positions = [];
31250         
31251         positions.push({
31252             x : maxX - this.unitWidth * 2 - this.gutter,
31253             y : minY
31254         });
31255         
31256         positions.push({
31257             x : maxX - this.unitWidth,
31258             y : minY + (this.unitWidth + this.gutter) * 2
31259         });
31260         
31261         positions.push({
31262             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31263             y : minY
31264         });
31265         
31266         Roo.each(eItems, function(b,k){
31267             
31268             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31269
31270         }, this);
31271         
31272     },
31273     
31274     getVerticalOneBoxColPositions : function(x, y, box)
31275     {
31276         var pos = [];
31277         
31278         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31279         
31280         if(box[0].size == 'md-left'){
31281             rand = 0;
31282         }
31283         
31284         if(box[0].size == 'md-right'){
31285             rand = 1;
31286         }
31287         
31288         pos.push({
31289             x : x + (this.unitWidth + this.gutter) * rand,
31290             y : y
31291         });
31292         
31293         return pos;
31294     },
31295     
31296     getVerticalTwoBoxColPositions : function(x, y, box)
31297     {
31298         var pos = [];
31299         
31300         if(box[0].size == 'xs'){
31301             
31302             pos.push({
31303                 x : x,
31304                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31305             });
31306
31307             pos.push({
31308                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31309                 y : y
31310             });
31311             
31312             return pos;
31313             
31314         }
31315         
31316         pos.push({
31317             x : x,
31318             y : y
31319         });
31320
31321         pos.push({
31322             x : x + (this.unitWidth + this.gutter) * 2,
31323             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31324         });
31325         
31326         return pos;
31327         
31328     },
31329     
31330     getVerticalThreeBoxColPositions : function(x, y, box)
31331     {
31332         var pos = [];
31333         
31334         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31335             
31336             pos.push({
31337                 x : x,
31338                 y : y
31339             });
31340
31341             pos.push({
31342                 x : x + (this.unitWidth + this.gutter) * 1,
31343                 y : y
31344             });
31345             
31346             pos.push({
31347                 x : x + (this.unitWidth + this.gutter) * 2,
31348                 y : y
31349             });
31350             
31351             return pos;
31352             
31353         }
31354         
31355         if(box[0].size == 'xs' && box[1].size == 'xs'){
31356             
31357             pos.push({
31358                 x : x,
31359                 y : y
31360             });
31361
31362             pos.push({
31363                 x : x,
31364                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31365             });
31366             
31367             pos.push({
31368                 x : x + (this.unitWidth + this.gutter) * 1,
31369                 y : y
31370             });
31371             
31372             return pos;
31373             
31374         }
31375         
31376         pos.push({
31377             x : x,
31378             y : y
31379         });
31380
31381         pos.push({
31382             x : x + (this.unitWidth + this.gutter) * 2,
31383             y : y
31384         });
31385
31386         pos.push({
31387             x : x + (this.unitWidth + this.gutter) * 2,
31388             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31389         });
31390             
31391         return pos;
31392         
31393     },
31394     
31395     getVerticalFourBoxColPositions : function(x, y, box)
31396     {
31397         var pos = [];
31398         
31399         if(box[0].size == 'xs'){
31400             
31401             pos.push({
31402                 x : x,
31403                 y : y
31404             });
31405
31406             pos.push({
31407                 x : x,
31408                 y : y + (this.unitHeight + this.gutter) * 1
31409             });
31410             
31411             pos.push({
31412                 x : x,
31413                 y : y + (this.unitHeight + this.gutter) * 2
31414             });
31415             
31416             pos.push({
31417                 x : x + (this.unitWidth + this.gutter) * 1,
31418                 y : y
31419             });
31420             
31421             return pos;
31422             
31423         }
31424         
31425         pos.push({
31426             x : x,
31427             y : y
31428         });
31429
31430         pos.push({
31431             x : x + (this.unitWidth + this.gutter) * 2,
31432             y : y
31433         });
31434
31435         pos.push({
31436             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31437             y : y + (this.unitHeight + this.gutter) * 1
31438         });
31439
31440         pos.push({
31441             x : x + (this.unitWidth + this.gutter) * 2,
31442             y : y + (this.unitWidth + this.gutter) * 2
31443         });
31444
31445         return pos;
31446         
31447     },
31448     
31449     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31450     {
31451         var pos = [];
31452         
31453         if(box[0].size == 'md-left'){
31454             pos.push({
31455                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31456                 y : minY
31457             });
31458             
31459             return pos;
31460         }
31461         
31462         if(box[0].size == 'md-right'){
31463             pos.push({
31464                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31465                 y : minY + (this.unitWidth + this.gutter) * 1
31466             });
31467             
31468             return pos;
31469         }
31470         
31471         var rand = Math.floor(Math.random() * (4 - box[0].y));
31472         
31473         pos.push({
31474             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31475             y : minY + (this.unitWidth + this.gutter) * rand
31476         });
31477         
31478         return pos;
31479         
31480     },
31481     
31482     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31483     {
31484         var pos = [];
31485         
31486         if(box[0].size == 'xs'){
31487             
31488             pos.push({
31489                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31490                 y : minY
31491             });
31492
31493             pos.push({
31494                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31495                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31496             });
31497             
31498             return pos;
31499             
31500         }
31501         
31502         pos.push({
31503             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31504             y : minY
31505         });
31506
31507         pos.push({
31508             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31509             y : minY + (this.unitWidth + this.gutter) * 2
31510         });
31511         
31512         return pos;
31513         
31514     },
31515     
31516     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31517     {
31518         var pos = [];
31519         
31520         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31521             
31522             pos.push({
31523                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31524                 y : minY
31525             });
31526
31527             pos.push({
31528                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31529                 y : minY + (this.unitWidth + this.gutter) * 1
31530             });
31531             
31532             pos.push({
31533                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31534                 y : minY + (this.unitWidth + this.gutter) * 2
31535             });
31536             
31537             return pos;
31538             
31539         }
31540         
31541         if(box[0].size == 'xs' && box[1].size == 'xs'){
31542             
31543             pos.push({
31544                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31545                 y : minY
31546             });
31547
31548             pos.push({
31549                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31550                 y : minY
31551             });
31552             
31553             pos.push({
31554                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31555                 y : minY + (this.unitWidth + this.gutter) * 1
31556             });
31557             
31558             return pos;
31559             
31560         }
31561         
31562         pos.push({
31563             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31564             y : minY
31565         });
31566
31567         pos.push({
31568             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31569             y : minY + (this.unitWidth + this.gutter) * 2
31570         });
31571
31572         pos.push({
31573             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31574             y : minY + (this.unitWidth + this.gutter) * 2
31575         });
31576             
31577         return pos;
31578         
31579     },
31580     
31581     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31582     {
31583         var pos = [];
31584         
31585         if(box[0].size == 'xs'){
31586             
31587             pos.push({
31588                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31589                 y : minY
31590             });
31591
31592             pos.push({
31593                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31594                 y : minY
31595             });
31596             
31597             pos.push({
31598                 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),
31599                 y : minY
31600             });
31601             
31602             pos.push({
31603                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31604                 y : minY + (this.unitWidth + this.gutter) * 1
31605             });
31606             
31607             return pos;
31608             
31609         }
31610         
31611         pos.push({
31612             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31613             y : minY
31614         });
31615         
31616         pos.push({
31617             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31618             y : minY + (this.unitWidth + this.gutter) * 2
31619         });
31620         
31621         pos.push({
31622             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31623             y : minY + (this.unitWidth + this.gutter) * 2
31624         });
31625         
31626         pos.push({
31627             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),
31628             y : minY + (this.unitWidth + this.gutter) * 2
31629         });
31630
31631         return pos;
31632         
31633     },
31634     
31635     /**
31636     * remove a Masonry Brick
31637     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31638     */
31639     removeBrick : function(brick_id)
31640     {
31641         if (!brick_id) {
31642             return;
31643         }
31644         
31645         for (var i = 0; i<this.bricks.length; i++) {
31646             if (this.bricks[i].id == brick_id) {
31647                 this.bricks.splice(i,1);
31648                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31649                 this.initial();
31650             }
31651         }
31652     },
31653     
31654     /**
31655     * adds a Masonry Brick
31656     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31657     */
31658     addBrick : function(cfg)
31659     {
31660         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31661         //this.register(cn);
31662         cn.parentId = this.id;
31663         cn.onRender(this.el, null);
31664         return cn;
31665     },
31666     
31667     /**
31668     * register a Masonry Brick
31669     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31670     */
31671     
31672     register : function(brick)
31673     {
31674         this.bricks.push(brick);
31675         brick.masonryId = this.id;
31676     },
31677     
31678     /**
31679     * clear all the Masonry Brick
31680     */
31681     clearAll : function()
31682     {
31683         this.bricks = [];
31684         //this.getChildContainer().dom.innerHTML = "";
31685         this.el.dom.innerHTML = '';
31686     },
31687     
31688     getSelected : function()
31689     {
31690         if (!this.selectedBrick) {
31691             return false;
31692         }
31693         
31694         return this.selectedBrick;
31695     }
31696 });
31697
31698 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31699     
31700     groups: {},
31701      /**
31702     * register a Masonry Layout
31703     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31704     */
31705     
31706     register : function(layout)
31707     {
31708         this.groups[layout.id] = layout;
31709     },
31710     /**
31711     * fetch a  Masonry Layout based on the masonry layout ID
31712     * @param {string} the masonry layout to add
31713     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31714     */
31715     
31716     get: function(layout_id) {
31717         if (typeof(this.groups[layout_id]) == 'undefined') {
31718             return false;
31719         }
31720         return this.groups[layout_id] ;
31721     }
31722     
31723     
31724     
31725 });
31726
31727  
31728
31729  /**
31730  *
31731  * This is based on 
31732  * http://masonry.desandro.com
31733  *
31734  * The idea is to render all the bricks based on vertical width...
31735  *
31736  * The original code extends 'outlayer' - we might need to use that....
31737  * 
31738  */
31739
31740
31741 /**
31742  * @class Roo.bootstrap.LayoutMasonryAuto
31743  * @extends Roo.bootstrap.Component
31744  * Bootstrap Layout Masonry class
31745  * 
31746  * @constructor
31747  * Create a new Element
31748  * @param {Object} config The config object
31749  */
31750
31751 Roo.bootstrap.LayoutMasonryAuto = function(config){
31752     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31753 };
31754
31755 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
31756     
31757       /**
31758      * @cfg {Boolean} isFitWidth  - resize the width..
31759      */   
31760     isFitWidth : false,  // options..
31761     /**
31762      * @cfg {Boolean} isOriginLeft = left align?
31763      */   
31764     isOriginLeft : true,
31765     /**
31766      * @cfg {Boolean} isOriginTop = top align?
31767      */   
31768     isOriginTop : false,
31769     /**
31770      * @cfg {Boolean} isLayoutInstant = no animation?
31771      */   
31772     isLayoutInstant : false, // needed?
31773     /**
31774      * @cfg {Boolean} isResizingContainer = not sure if this is used..
31775      */   
31776     isResizingContainer : true,
31777     /**
31778      * @cfg {Number} columnWidth  width of the columns 
31779      */   
31780     
31781     columnWidth : 0,
31782     
31783     /**
31784      * @cfg {Number} maxCols maximum number of columns
31785      */   
31786     
31787     maxCols: 0,
31788     /**
31789      * @cfg {Number} padHeight padding below box..
31790      */   
31791     
31792     padHeight : 10, 
31793     
31794     /**
31795      * @cfg {Boolean} isAutoInitial defalut true
31796      */   
31797     
31798     isAutoInitial : true, 
31799     
31800     // private?
31801     gutter : 0,
31802     
31803     containerWidth: 0,
31804     initialColumnWidth : 0,
31805     currentSize : null,
31806     
31807     colYs : null, // array.
31808     maxY : 0,
31809     padWidth: 10,
31810     
31811     
31812     tag: 'div',
31813     cls: '',
31814     bricks: null, //CompositeElement
31815     cols : 0, // array?
31816     // element : null, // wrapped now this.el
31817     _isLayoutInited : null, 
31818     
31819     
31820     getAutoCreate : function(){
31821         
31822         var cfg = {
31823             tag: this.tag,
31824             cls: 'blog-masonary-wrapper ' + this.cls,
31825             cn : {
31826                 cls : 'mas-boxes masonary'
31827             }
31828         };
31829         
31830         return cfg;
31831     },
31832     
31833     getChildContainer: function( )
31834     {
31835         if (this.boxesEl) {
31836             return this.boxesEl;
31837         }
31838         
31839         this.boxesEl = this.el.select('.mas-boxes').first();
31840         
31841         return this.boxesEl;
31842     },
31843     
31844     
31845     initEvents : function()
31846     {
31847         var _this = this;
31848         
31849         if(this.isAutoInitial){
31850             Roo.log('hook children rendered');
31851             this.on('childrenrendered', function() {
31852                 Roo.log('children rendered');
31853                 _this.initial();
31854             } ,this);
31855         }
31856         
31857     },
31858     
31859     initial : function()
31860     {
31861         this.reloadItems();
31862
31863         this.currentSize = this.el.getBox(true);
31864
31865         /// was window resize... - let's see if this works..
31866         Roo.EventManager.onWindowResize(this.resize, this); 
31867
31868         if(!this.isAutoInitial){
31869             this.layout();
31870             return;
31871         }
31872         
31873         this.layout.defer(500,this);
31874     },
31875     
31876     reloadItems: function()
31877     {
31878         this.bricks = this.el.select('.masonry-brick', true);
31879         
31880         this.bricks.each(function(b) {
31881             //Roo.log(b.getSize());
31882             if (!b.attr('originalwidth')) {
31883                 b.attr('originalwidth',  b.getSize().width);
31884             }
31885             
31886         });
31887         
31888         Roo.log(this.bricks.elements.length);
31889     },
31890     
31891     resize : function()
31892     {
31893         Roo.log('resize');
31894         var cs = this.el.getBox(true);
31895         
31896         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
31897             Roo.log("no change in with or X");
31898             return;
31899         }
31900         this.currentSize = cs;
31901         this.layout();
31902     },
31903     
31904     layout : function()
31905     {
31906          Roo.log('layout');
31907         this._resetLayout();
31908         //this._manageStamps();
31909       
31910         // don't animate first layout
31911         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31912         this.layoutItems( isInstant );
31913       
31914         // flag for initalized
31915         this._isLayoutInited = true;
31916     },
31917     
31918     layoutItems : function( isInstant )
31919     {
31920         //var items = this._getItemsForLayout( this.items );
31921         // original code supports filtering layout items.. we just ignore it..
31922         
31923         this._layoutItems( this.bricks , isInstant );
31924       
31925         this._postLayout();
31926     },
31927     _layoutItems : function ( items , isInstant)
31928     {
31929        //this.fireEvent( 'layout', this, items );
31930     
31931
31932         if ( !items || !items.elements.length ) {
31933           // no items, emit event with empty array
31934             return;
31935         }
31936
31937         var queue = [];
31938         items.each(function(item) {
31939             Roo.log("layout item");
31940             Roo.log(item);
31941             // get x/y object from method
31942             var position = this._getItemLayoutPosition( item );
31943             // enqueue
31944             position.item = item;
31945             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
31946             queue.push( position );
31947         }, this);
31948       
31949         this._processLayoutQueue( queue );
31950     },
31951     /** Sets position of item in DOM
31952     * @param {Element} item
31953     * @param {Number} x - horizontal position
31954     * @param {Number} y - vertical position
31955     * @param {Boolean} isInstant - disables transitions
31956     */
31957     _processLayoutQueue : function( queue )
31958     {
31959         for ( var i=0, len = queue.length; i < len; i++ ) {
31960             var obj = queue[i];
31961             obj.item.position('absolute');
31962             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
31963         }
31964     },
31965       
31966     
31967     /**
31968     * Any logic you want to do after each layout,
31969     * i.e. size the container
31970     */
31971     _postLayout : function()
31972     {
31973         this.resizeContainer();
31974     },
31975     
31976     resizeContainer : function()
31977     {
31978         if ( !this.isResizingContainer ) {
31979             return;
31980         }
31981         var size = this._getContainerSize();
31982         if ( size ) {
31983             this.el.setSize(size.width,size.height);
31984             this.boxesEl.setSize(size.width,size.height);
31985         }
31986     },
31987     
31988     
31989     
31990     _resetLayout : function()
31991     {
31992         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
31993         this.colWidth = this.el.getWidth();
31994         //this.gutter = this.el.getWidth(); 
31995         
31996         this.measureColumns();
31997
31998         // reset column Y
31999         var i = this.cols;
32000         this.colYs = [];
32001         while (i--) {
32002             this.colYs.push( 0 );
32003         }
32004     
32005         this.maxY = 0;
32006     },
32007
32008     measureColumns : function()
32009     {
32010         this.getContainerWidth();
32011       // if columnWidth is 0, default to outerWidth of first item
32012         if ( !this.columnWidth ) {
32013             var firstItem = this.bricks.first();
32014             Roo.log(firstItem);
32015             this.columnWidth  = this.containerWidth;
32016             if (firstItem && firstItem.attr('originalwidth') ) {
32017                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32018             }
32019             // columnWidth fall back to item of first element
32020             Roo.log("set column width?");
32021                         this.initialColumnWidth = this.columnWidth  ;
32022
32023             // if first elem has no width, default to size of container
32024             
32025         }
32026         
32027         
32028         if (this.initialColumnWidth) {
32029             this.columnWidth = this.initialColumnWidth;
32030         }
32031         
32032         
32033             
32034         // column width is fixed at the top - however if container width get's smaller we should
32035         // reduce it...
32036         
32037         // this bit calcs how man columns..
32038             
32039         var columnWidth = this.columnWidth += this.gutter;
32040       
32041         // calculate columns
32042         var containerWidth = this.containerWidth + this.gutter;
32043         
32044         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32045         // fix rounding errors, typically with gutters
32046         var excess = columnWidth - containerWidth % columnWidth;
32047         
32048         
32049         // if overshoot is less than a pixel, round up, otherwise floor it
32050         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32051         cols = Math[ mathMethod ]( cols );
32052         this.cols = Math.max( cols, 1 );
32053         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32054         
32055          // padding positioning..
32056         var totalColWidth = this.cols * this.columnWidth;
32057         var padavail = this.containerWidth - totalColWidth;
32058         // so for 2 columns - we need 3 'pads'
32059         
32060         var padNeeded = (1+this.cols) * this.padWidth;
32061         
32062         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32063         
32064         this.columnWidth += padExtra
32065         //this.padWidth = Math.floor(padavail /  ( this.cols));
32066         
32067         // adjust colum width so that padding is fixed??
32068         
32069         // we have 3 columns ... total = width * 3
32070         // we have X left over... that should be used by 
32071         
32072         //if (this.expandC) {
32073             
32074         //}
32075         
32076         
32077         
32078     },
32079     
32080     getContainerWidth : function()
32081     {
32082        /* // container is parent if fit width
32083         var container = this.isFitWidth ? this.element.parentNode : this.element;
32084         // check that this.size and size are there
32085         // IE8 triggers resize on body size change, so they might not be
32086         
32087         var size = getSize( container );  //FIXME
32088         this.containerWidth = size && size.innerWidth; //FIXME
32089         */
32090          
32091         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32092         
32093     },
32094     
32095     _getItemLayoutPosition : function( item )  // what is item?
32096     {
32097         // we resize the item to our columnWidth..
32098       
32099         item.setWidth(this.columnWidth);
32100         item.autoBoxAdjust  = false;
32101         
32102         var sz = item.getSize();
32103  
32104         // how many columns does this brick span
32105         var remainder = this.containerWidth % this.columnWidth;
32106         
32107         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32108         // round if off by 1 pixel, otherwise use ceil
32109         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32110         colSpan = Math.min( colSpan, this.cols );
32111         
32112         // normally this should be '1' as we dont' currently allow multi width columns..
32113         
32114         var colGroup = this._getColGroup( colSpan );
32115         // get the minimum Y value from the columns
32116         var minimumY = Math.min.apply( Math, colGroup );
32117         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32118         
32119         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32120          
32121         // position the brick
32122         var position = {
32123             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32124             y: this.currentSize.y + minimumY + this.padHeight
32125         };
32126         
32127         Roo.log(position);
32128         // apply setHeight to necessary columns
32129         var setHeight = minimumY + sz.height + this.padHeight;
32130         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32131         
32132         var setSpan = this.cols + 1 - colGroup.length;
32133         for ( var i = 0; i < setSpan; i++ ) {
32134           this.colYs[ shortColIndex + i ] = setHeight ;
32135         }
32136       
32137         return position;
32138     },
32139     
32140     /**
32141      * @param {Number} colSpan - number of columns the element spans
32142      * @returns {Array} colGroup
32143      */
32144     _getColGroup : function( colSpan )
32145     {
32146         if ( colSpan < 2 ) {
32147           // if brick spans only one column, use all the column Ys
32148           return this.colYs;
32149         }
32150       
32151         var colGroup = [];
32152         // how many different places could this brick fit horizontally
32153         var groupCount = this.cols + 1 - colSpan;
32154         // for each group potential horizontal position
32155         for ( var i = 0; i < groupCount; i++ ) {
32156           // make an array of colY values for that one group
32157           var groupColYs = this.colYs.slice( i, i + colSpan );
32158           // and get the max value of the array
32159           colGroup[i] = Math.max.apply( Math, groupColYs );
32160         }
32161         return colGroup;
32162     },
32163     /*
32164     _manageStamp : function( stamp )
32165     {
32166         var stampSize =  stamp.getSize();
32167         var offset = stamp.getBox();
32168         // get the columns that this stamp affects
32169         var firstX = this.isOriginLeft ? offset.x : offset.right;
32170         var lastX = firstX + stampSize.width;
32171         var firstCol = Math.floor( firstX / this.columnWidth );
32172         firstCol = Math.max( 0, firstCol );
32173         
32174         var lastCol = Math.floor( lastX / this.columnWidth );
32175         // lastCol should not go over if multiple of columnWidth #425
32176         lastCol -= lastX % this.columnWidth ? 0 : 1;
32177         lastCol = Math.min( this.cols - 1, lastCol );
32178         
32179         // set colYs to bottom of the stamp
32180         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32181             stampSize.height;
32182             
32183         for ( var i = firstCol; i <= lastCol; i++ ) {
32184           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32185         }
32186     },
32187     */
32188     
32189     _getContainerSize : function()
32190     {
32191         this.maxY = Math.max.apply( Math, this.colYs );
32192         var size = {
32193             height: this.maxY
32194         };
32195       
32196         if ( this.isFitWidth ) {
32197             size.width = this._getContainerFitWidth();
32198         }
32199       
32200         return size;
32201     },
32202     
32203     _getContainerFitWidth : function()
32204     {
32205         var unusedCols = 0;
32206         // count unused columns
32207         var i = this.cols;
32208         while ( --i ) {
32209           if ( this.colYs[i] !== 0 ) {
32210             break;
32211           }
32212           unusedCols++;
32213         }
32214         // fit container to columns that have been used
32215         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32216     },
32217     
32218     needsResizeLayout : function()
32219     {
32220         var previousWidth = this.containerWidth;
32221         this.getContainerWidth();
32222         return previousWidth !== this.containerWidth;
32223     }
32224  
32225 });
32226
32227  
32228
32229  /*
32230  * - LGPL
32231  *
32232  * element
32233  * 
32234  */
32235
32236 /**
32237  * @class Roo.bootstrap.MasonryBrick
32238  * @extends Roo.bootstrap.Component
32239  * Bootstrap MasonryBrick class
32240  * 
32241  * @constructor
32242  * Create a new MasonryBrick
32243  * @param {Object} config The config object
32244  */
32245
32246 Roo.bootstrap.MasonryBrick = function(config){
32247     
32248     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32249     
32250     Roo.bootstrap.MasonryBrick.register(this);
32251     
32252     this.addEvents({
32253         // raw events
32254         /**
32255          * @event click
32256          * When a MasonryBrick is clcik
32257          * @param {Roo.bootstrap.MasonryBrick} this
32258          * @param {Roo.EventObject} e
32259          */
32260         "click" : true
32261     });
32262 };
32263
32264 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32265     
32266     /**
32267      * @cfg {String} title
32268      */   
32269     title : '',
32270     /**
32271      * @cfg {String} html
32272      */   
32273     html : '',
32274     /**
32275      * @cfg {String} bgimage
32276      */   
32277     bgimage : '',
32278     /**
32279      * @cfg {String} videourl
32280      */   
32281     videourl : '',
32282     /**
32283      * @cfg {String} cls
32284      */   
32285     cls : '',
32286     /**
32287      * @cfg {String} href
32288      */   
32289     href : '',
32290     /**
32291      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32292      */   
32293     size : 'xs',
32294     
32295     /**
32296      * @cfg {String} placetitle (center|bottom)
32297      */   
32298     placetitle : '',
32299     
32300     /**
32301      * @cfg {Boolean} isFitContainer defalut true
32302      */   
32303     isFitContainer : true, 
32304     
32305     /**
32306      * @cfg {Boolean} preventDefault defalut false
32307      */   
32308     preventDefault : false, 
32309     
32310     /**
32311      * @cfg {Boolean} inverse defalut false
32312      */   
32313     maskInverse : false, 
32314     
32315     getAutoCreate : function()
32316     {
32317         if(!this.isFitContainer){
32318             return this.getSplitAutoCreate();
32319         }
32320         
32321         var cls = 'masonry-brick masonry-brick-full';
32322         
32323         if(this.href.length){
32324             cls += ' masonry-brick-link';
32325         }
32326         
32327         if(this.bgimage.length){
32328             cls += ' masonry-brick-image';
32329         }
32330         
32331         if(this.maskInverse){
32332             cls += ' mask-inverse';
32333         }
32334         
32335         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32336             cls += ' enable-mask';
32337         }
32338         
32339         if(this.size){
32340             cls += ' masonry-' + this.size + '-brick';
32341         }
32342         
32343         if(this.placetitle.length){
32344             
32345             switch (this.placetitle) {
32346                 case 'center' :
32347                     cls += ' masonry-center-title';
32348                     break;
32349                 case 'bottom' :
32350                     cls += ' masonry-bottom-title';
32351                     break;
32352                 default:
32353                     break;
32354             }
32355             
32356         } else {
32357             if(!this.html.length && !this.bgimage.length){
32358                 cls += ' masonry-center-title';
32359             }
32360
32361             if(!this.html.length && this.bgimage.length){
32362                 cls += ' masonry-bottom-title';
32363             }
32364         }
32365         
32366         if(this.cls){
32367             cls += ' ' + this.cls;
32368         }
32369         
32370         var cfg = {
32371             tag: (this.href.length) ? 'a' : 'div',
32372             cls: cls,
32373             cn: [
32374                 {
32375                     tag: 'div',
32376                     cls: 'masonry-brick-mask'
32377                 },
32378                 {
32379                     tag: 'div',
32380                     cls: 'masonry-brick-paragraph',
32381                     cn: []
32382                 }
32383             ]
32384         };
32385         
32386         if(this.href.length){
32387             cfg.href = this.href;
32388         }
32389         
32390         var cn = cfg.cn[1].cn;
32391         
32392         if(this.title.length){
32393             cn.push({
32394                 tag: 'h4',
32395                 cls: 'masonry-brick-title',
32396                 html: this.title
32397             });
32398         }
32399         
32400         if(this.html.length){
32401             cn.push({
32402                 tag: 'p',
32403                 cls: 'masonry-brick-text',
32404                 html: this.html
32405             });
32406         }
32407         
32408         if (!this.title.length && !this.html.length) {
32409             cfg.cn[1].cls += ' hide';
32410         }
32411         
32412         if(this.bgimage.length){
32413             cfg.cn.push({
32414                 tag: 'img',
32415                 cls: 'masonry-brick-image-view',
32416                 src: this.bgimage
32417             });
32418         }
32419         
32420         if(this.videourl.length){
32421             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32422             // youtube support only?
32423             cfg.cn.push({
32424                 tag: 'iframe',
32425                 cls: 'masonry-brick-image-view',
32426                 src: vurl,
32427                 frameborder : 0,
32428                 allowfullscreen : true
32429             });
32430         }
32431         
32432         return cfg;
32433         
32434     },
32435     
32436     getSplitAutoCreate : function()
32437     {
32438         var cls = 'masonry-brick masonry-brick-split';
32439         
32440         if(this.href.length){
32441             cls += ' masonry-brick-link';
32442         }
32443         
32444         if(this.bgimage.length){
32445             cls += ' masonry-brick-image';
32446         }
32447         
32448         if(this.size){
32449             cls += ' masonry-' + this.size + '-brick';
32450         }
32451         
32452         switch (this.placetitle) {
32453             case 'center' :
32454                 cls += ' masonry-center-title';
32455                 break;
32456             case 'bottom' :
32457                 cls += ' masonry-bottom-title';
32458                 break;
32459             default:
32460                 if(!this.bgimage.length){
32461                     cls += ' masonry-center-title';
32462                 }
32463
32464                 if(this.bgimage.length){
32465                     cls += ' masonry-bottom-title';
32466                 }
32467                 break;
32468         }
32469         
32470         if(this.cls){
32471             cls += ' ' + this.cls;
32472         }
32473         
32474         var cfg = {
32475             tag: (this.href.length) ? 'a' : 'div',
32476             cls: cls,
32477             cn: [
32478                 {
32479                     tag: 'div',
32480                     cls: 'masonry-brick-split-head',
32481                     cn: [
32482                         {
32483                             tag: 'div',
32484                             cls: 'masonry-brick-paragraph',
32485                             cn: []
32486                         }
32487                     ]
32488                 },
32489                 {
32490                     tag: 'div',
32491                     cls: 'masonry-brick-split-body',
32492                     cn: []
32493                 }
32494             ]
32495         };
32496         
32497         if(this.href.length){
32498             cfg.href = this.href;
32499         }
32500         
32501         if(this.title.length){
32502             cfg.cn[0].cn[0].cn.push({
32503                 tag: 'h4',
32504                 cls: 'masonry-brick-title',
32505                 html: this.title
32506             });
32507         }
32508         
32509         if(this.html.length){
32510             cfg.cn[1].cn.push({
32511                 tag: 'p',
32512                 cls: 'masonry-brick-text',
32513                 html: this.html
32514             });
32515         }
32516
32517         if(this.bgimage.length){
32518             cfg.cn[0].cn.push({
32519                 tag: 'img',
32520                 cls: 'masonry-brick-image-view',
32521                 src: this.bgimage
32522             });
32523         }
32524         
32525         if(this.videourl.length){
32526             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32527             // youtube support only?
32528             cfg.cn[0].cn.cn.push({
32529                 tag: 'iframe',
32530                 cls: 'masonry-brick-image-view',
32531                 src: vurl,
32532                 frameborder : 0,
32533                 allowfullscreen : true
32534             });
32535         }
32536         
32537         return cfg;
32538     },
32539     
32540     initEvents: function() 
32541     {
32542         switch (this.size) {
32543             case 'xs' :
32544                 this.x = 1;
32545                 this.y = 1;
32546                 break;
32547             case 'sm' :
32548                 this.x = 2;
32549                 this.y = 2;
32550                 break;
32551             case 'md' :
32552             case 'md-left' :
32553             case 'md-right' :
32554                 this.x = 3;
32555                 this.y = 3;
32556                 break;
32557             case 'tall' :
32558                 this.x = 2;
32559                 this.y = 3;
32560                 break;
32561             case 'wide' :
32562                 this.x = 3;
32563                 this.y = 2;
32564                 break;
32565             case 'wide-thin' :
32566                 this.x = 3;
32567                 this.y = 1;
32568                 break;
32569                         
32570             default :
32571                 break;
32572         }
32573         
32574         if(Roo.isTouch){
32575             this.el.on('touchstart', this.onTouchStart, this);
32576             this.el.on('touchmove', this.onTouchMove, this);
32577             this.el.on('touchend', this.onTouchEnd, this);
32578             this.el.on('contextmenu', this.onContextMenu, this);
32579         } else {
32580             this.el.on('mouseenter'  ,this.enter, this);
32581             this.el.on('mouseleave', this.leave, this);
32582             this.el.on('click', this.onClick, this);
32583         }
32584         
32585         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32586             this.parent().bricks.push(this);   
32587         }
32588         
32589     },
32590     
32591     onClick: function(e, el)
32592     {
32593         var time = this.endTimer - this.startTimer;
32594         // Roo.log(e.preventDefault());
32595         if(Roo.isTouch){
32596             if(time > 1000){
32597                 e.preventDefault();
32598                 return;
32599             }
32600         }
32601         
32602         if(!this.preventDefault){
32603             return;
32604         }
32605         
32606         e.preventDefault();
32607         
32608         if (this.activcClass != '') {
32609             this.selectBrick();
32610         }
32611         
32612         this.fireEvent('click', this);
32613     },
32614     
32615     enter: function(e, el)
32616     {
32617         e.preventDefault();
32618         
32619         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32620             return;
32621         }
32622         
32623         if(this.bgimage.length && this.html.length){
32624             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32625         }
32626     },
32627     
32628     leave: function(e, el)
32629     {
32630         e.preventDefault();
32631         
32632         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32633             return;
32634         }
32635         
32636         if(this.bgimage.length && this.html.length){
32637             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32638         }
32639     },
32640     
32641     onTouchStart: function(e, el)
32642     {
32643 //        e.preventDefault();
32644         
32645         this.touchmoved = false;
32646         
32647         if(!this.isFitContainer){
32648             return;
32649         }
32650         
32651         if(!this.bgimage.length || !this.html.length){
32652             return;
32653         }
32654         
32655         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32656         
32657         this.timer = new Date().getTime();
32658         
32659     },
32660     
32661     onTouchMove: function(e, el)
32662     {
32663         this.touchmoved = true;
32664     },
32665     
32666     onContextMenu : function(e,el)
32667     {
32668         e.preventDefault();
32669         e.stopPropagation();
32670         return false;
32671     },
32672     
32673     onTouchEnd: function(e, el)
32674     {
32675 //        e.preventDefault();
32676         
32677         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32678         
32679             this.leave(e,el);
32680             
32681             return;
32682         }
32683         
32684         if(!this.bgimage.length || !this.html.length){
32685             
32686             if(this.href.length){
32687                 window.location.href = this.href;
32688             }
32689             
32690             return;
32691         }
32692         
32693         if(!this.isFitContainer){
32694             return;
32695         }
32696         
32697         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32698         
32699         window.location.href = this.href;
32700     },
32701     
32702     //selection on single brick only
32703     selectBrick : function() {
32704         
32705         if (!this.parentId) {
32706             return;
32707         }
32708         
32709         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32710         var index = m.selectedBrick.indexOf(this.id);
32711         
32712         if ( index > -1) {
32713             m.selectedBrick.splice(index,1);
32714             this.el.removeClass(this.activeClass);
32715             return;
32716         }
32717         
32718         for(var i = 0; i < m.selectedBrick.length; i++) {
32719             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32720             b.el.removeClass(b.activeClass);
32721         }
32722         
32723         m.selectedBrick = [];
32724         
32725         m.selectedBrick.push(this.id);
32726         this.el.addClass(this.activeClass);
32727         return;
32728     }
32729     
32730 });
32731
32732 Roo.apply(Roo.bootstrap.MasonryBrick, {
32733     
32734     //groups: {},
32735     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32736      /**
32737     * register a Masonry Brick
32738     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32739     */
32740     
32741     register : function(brick)
32742     {
32743         //this.groups[brick.id] = brick;
32744         this.groups.add(brick.id, brick);
32745     },
32746     /**
32747     * fetch a  masonry brick based on the masonry brick ID
32748     * @param {string} the masonry brick to add
32749     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32750     */
32751     
32752     get: function(brick_id) 
32753     {
32754         // if (typeof(this.groups[brick_id]) == 'undefined') {
32755         //     return false;
32756         // }
32757         // return this.groups[brick_id] ;
32758         
32759         if(this.groups.key(brick_id)) {
32760             return this.groups.key(brick_id);
32761         }
32762         
32763         return false;
32764     }
32765     
32766     
32767     
32768 });
32769
32770  /*
32771  * - LGPL
32772  *
32773  * element
32774  * 
32775  */
32776
32777 /**
32778  * @class Roo.bootstrap.Brick
32779  * @extends Roo.bootstrap.Component
32780  * Bootstrap Brick class
32781  * 
32782  * @constructor
32783  * Create a new Brick
32784  * @param {Object} config The config object
32785  */
32786
32787 Roo.bootstrap.Brick = function(config){
32788     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
32789     
32790     this.addEvents({
32791         // raw events
32792         /**
32793          * @event click
32794          * When a Brick is click
32795          * @param {Roo.bootstrap.Brick} this
32796          * @param {Roo.EventObject} e
32797          */
32798         "click" : true
32799     });
32800 };
32801
32802 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
32803     
32804     /**
32805      * @cfg {String} title
32806      */   
32807     title : '',
32808     /**
32809      * @cfg {String} html
32810      */   
32811     html : '',
32812     /**
32813      * @cfg {String} bgimage
32814      */   
32815     bgimage : '',
32816     /**
32817      * @cfg {String} cls
32818      */   
32819     cls : '',
32820     /**
32821      * @cfg {String} href
32822      */   
32823     href : '',
32824     /**
32825      * @cfg {String} video
32826      */   
32827     video : '',
32828     /**
32829      * @cfg {Boolean} square
32830      */   
32831     square : true,
32832     
32833     getAutoCreate : function()
32834     {
32835         var cls = 'roo-brick';
32836         
32837         if(this.href.length){
32838             cls += ' roo-brick-link';
32839         }
32840         
32841         if(this.bgimage.length){
32842             cls += ' roo-brick-image';
32843         }
32844         
32845         if(!this.html.length && !this.bgimage.length){
32846             cls += ' roo-brick-center-title';
32847         }
32848         
32849         if(!this.html.length && this.bgimage.length){
32850             cls += ' roo-brick-bottom-title';
32851         }
32852         
32853         if(this.cls){
32854             cls += ' ' + this.cls;
32855         }
32856         
32857         var cfg = {
32858             tag: (this.href.length) ? 'a' : 'div',
32859             cls: cls,
32860             cn: [
32861                 {
32862                     tag: 'div',
32863                     cls: 'roo-brick-paragraph',
32864                     cn: []
32865                 }
32866             ]
32867         };
32868         
32869         if(this.href.length){
32870             cfg.href = this.href;
32871         }
32872         
32873         var cn = cfg.cn[0].cn;
32874         
32875         if(this.title.length){
32876             cn.push({
32877                 tag: 'h4',
32878                 cls: 'roo-brick-title',
32879                 html: this.title
32880             });
32881         }
32882         
32883         if(this.html.length){
32884             cn.push({
32885                 tag: 'p',
32886                 cls: 'roo-brick-text',
32887                 html: this.html
32888             });
32889         } else {
32890             cn.cls += ' hide';
32891         }
32892         
32893         if(this.bgimage.length){
32894             cfg.cn.push({
32895                 tag: 'img',
32896                 cls: 'roo-brick-image-view',
32897                 src: this.bgimage
32898             });
32899         }
32900         
32901         return cfg;
32902     },
32903     
32904     initEvents: function() 
32905     {
32906         if(this.title.length || this.html.length){
32907             this.el.on('mouseenter'  ,this.enter, this);
32908             this.el.on('mouseleave', this.leave, this);
32909         }
32910         
32911         Roo.EventManager.onWindowResize(this.resize, this); 
32912         
32913         if(this.bgimage.length){
32914             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
32915             this.imageEl.on('load', this.onImageLoad, this);
32916             return;
32917         }
32918         
32919         this.resize();
32920     },
32921     
32922     onImageLoad : function()
32923     {
32924         this.resize();
32925     },
32926     
32927     resize : function()
32928     {
32929         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
32930         
32931         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
32932         
32933         if(this.bgimage.length){
32934             var image = this.el.select('.roo-brick-image-view', true).first();
32935             
32936             image.setWidth(paragraph.getWidth());
32937             
32938             if(this.square){
32939                 image.setHeight(paragraph.getWidth());
32940             }
32941             
32942             this.el.setHeight(image.getHeight());
32943             paragraph.setHeight(image.getHeight());
32944             
32945         }
32946         
32947     },
32948     
32949     enter: function(e, el)
32950     {
32951         e.preventDefault();
32952         
32953         if(this.bgimage.length){
32954             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
32955             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
32956         }
32957     },
32958     
32959     leave: function(e, el)
32960     {
32961         e.preventDefault();
32962         
32963         if(this.bgimage.length){
32964             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
32965             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
32966         }
32967     }
32968     
32969 });
32970
32971  
32972
32973  /*
32974  * - LGPL
32975  *
32976  * Input
32977  * 
32978  */
32979
32980 /**
32981  * @class Roo.bootstrap.NumberField
32982  * @extends Roo.bootstrap.Input
32983  * Bootstrap NumberField class
32984  * 
32985  * 
32986  * 
32987  * 
32988  * @constructor
32989  * Create a new NumberField
32990  * @param {Object} config The config object
32991  */
32992
32993 Roo.bootstrap.NumberField = function(config){
32994     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
32995 };
32996
32997 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
32998     
32999     /**
33000      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33001      */
33002     allowDecimals : true,
33003     /**
33004      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33005      */
33006     decimalSeparator : ".",
33007     /**
33008      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33009      */
33010     decimalPrecision : 2,
33011     /**
33012      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33013      */
33014     allowNegative : true,
33015     /**
33016      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33017      */
33018     minValue : Number.NEGATIVE_INFINITY,
33019     /**
33020      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33021      */
33022     maxValue : Number.MAX_VALUE,
33023     /**
33024      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33025      */
33026     minText : "The minimum value for this field is {0}",
33027     /**
33028      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33029      */
33030     maxText : "The maximum value for this field is {0}",
33031     /**
33032      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33033      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33034      */
33035     nanText : "{0} is not a valid number",
33036     /**
33037      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
33038      */
33039     castInt : true,
33040     /**
33041      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33042      */
33043     thousandsDelimiter : false,
33044     /**
33045      * @cfg {String} valueAlign alignment of value
33046      */
33047     valueAlign : "left",
33048
33049     getAutoCreate : function()
33050     {
33051         var hiddenInput = {
33052             tag: 'input',
33053             type: 'hidden',
33054             id: Roo.id(),
33055             cls: 'hidden-number-input'
33056         };
33057         
33058         if (this.name) {
33059             hiddenInput.name = this.name;
33060         }
33061         
33062         this.name = '';
33063         
33064         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33065         
33066         this.name = hiddenInput.name;
33067         
33068         if(cfg.cn.length > 0) {
33069             cfg.cn.push(hiddenInput);
33070         }
33071         
33072         return cfg;
33073     },
33074
33075     // private
33076     initEvents : function()
33077     {   
33078         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33079         
33080         var allowed = "0123456789";
33081         
33082         if(this.allowDecimals){
33083             allowed += this.decimalSeparator;
33084         }
33085         
33086         if(this.allowNegative){
33087             allowed += "-";
33088         }
33089         
33090         if(this.thousandsDelimiter) {
33091             allowed += ",";
33092         }
33093         
33094         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33095         
33096         var keyPress = function(e){
33097             
33098             var k = e.getKey();
33099             
33100             var c = e.getCharCode();
33101             
33102             if(
33103                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33104                     allowed.indexOf(String.fromCharCode(c)) === -1
33105             ){
33106                 e.stopEvent();
33107                 return;
33108             }
33109             
33110             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33111                 return;
33112             }
33113             
33114             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33115                 e.stopEvent();
33116             }
33117         };
33118         
33119         this.el.on("keypress", keyPress, this);
33120     },
33121     
33122     validateValue : function(value)
33123     {
33124         
33125         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33126             return false;
33127         }
33128         
33129         var num = this.parseValue(value);
33130         
33131         if(isNaN(num)){
33132             this.markInvalid(String.format(this.nanText, value));
33133             return false;
33134         }
33135         
33136         if(num < this.minValue){
33137             this.markInvalid(String.format(this.minText, this.minValue));
33138             return false;
33139         }
33140         
33141         if(num > this.maxValue){
33142             this.markInvalid(String.format(this.maxText, this.maxValue));
33143             return false;
33144         }
33145         
33146         return true;
33147     },
33148
33149     getValue : function()
33150     {
33151         var v = this.hiddenEl().getValue();
33152         
33153         return this.fixPrecision(this.parseValue(v));
33154     },
33155
33156     parseValue : function(value)
33157     {
33158         if(this.thousandsDelimiter) {
33159             value += "";
33160             r = new RegExp(",", "g");
33161             value = value.replace(r, "");
33162         }
33163         
33164         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33165         return isNaN(value) ? '' : value;
33166     },
33167
33168     fixPrecision : function(value)
33169     {
33170         if(this.thousandsDelimiter) {
33171             value += "";
33172             r = new RegExp(",", "g");
33173             value = value.replace(r, "");
33174         }
33175         
33176         var nan = isNaN(value);
33177         
33178         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33179             return nan ? '' : value;
33180         }
33181         return parseFloat(value).toFixed(this.decimalPrecision);
33182     },
33183
33184     setValue : function(v)
33185     {
33186         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33187         
33188         this.value = v;
33189         
33190         if(this.rendered){
33191             
33192             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33193             
33194             this.inputEl().dom.value = Roo.util.Format.number(v, this.decimalPrecision, 
33195                 this.thousandsDelimiter || ''
33196             );
33197             
33198             if(this.allowBlank && !v) {
33199                 this.inputEl().dom.value = '';
33200             }
33201             
33202             this.validate();
33203         }
33204     },
33205
33206     decimalPrecisionFcn : function(v)
33207     {
33208         return Math.floor(v);
33209     },
33210
33211     beforeBlur : function()
33212     {
33213         if(!this.castInt){
33214             return;
33215         }
33216         
33217         var v = this.parseValue(this.getRawValue());
33218         if(v){
33219             this.setValue(v);
33220         }
33221     },
33222     
33223     hiddenEl : function()
33224     {
33225         return this.el.select('input.hidden-number-input',true).first();
33226     }
33227     
33228 });
33229
33230  
33231
33232 /*
33233 * Licence: LGPL
33234 */
33235
33236 /**
33237  * @class Roo.bootstrap.DocumentSlider
33238  * @extends Roo.bootstrap.Component
33239  * Bootstrap DocumentSlider class
33240  * 
33241  * @constructor
33242  * Create a new DocumentViewer
33243  * @param {Object} config The config object
33244  */
33245
33246 Roo.bootstrap.DocumentSlider = function(config){
33247     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33248     
33249     this.files = [];
33250     
33251     this.addEvents({
33252         /**
33253          * @event initial
33254          * Fire after initEvent
33255          * @param {Roo.bootstrap.DocumentSlider} this
33256          */
33257         "initial" : true,
33258         /**
33259          * @event update
33260          * Fire after update
33261          * @param {Roo.bootstrap.DocumentSlider} this
33262          */
33263         "update" : true,
33264         /**
33265          * @event click
33266          * Fire after click
33267          * @param {Roo.bootstrap.DocumentSlider} this
33268          */
33269         "click" : true
33270     });
33271 };
33272
33273 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33274     
33275     files : false,
33276     
33277     indicator : 0,
33278     
33279     getAutoCreate : function()
33280     {
33281         var cfg = {
33282             tag : 'div',
33283             cls : 'roo-document-slider',
33284             cn : [
33285                 {
33286                     tag : 'div',
33287                     cls : 'roo-document-slider-header',
33288                     cn : [
33289                         {
33290                             tag : 'div',
33291                             cls : 'roo-document-slider-header-title'
33292                         }
33293                     ]
33294                 },
33295                 {
33296                     tag : 'div',
33297                     cls : 'roo-document-slider-body',
33298                     cn : [
33299                         {
33300                             tag : 'div',
33301                             cls : 'roo-document-slider-prev',
33302                             cn : [
33303                                 {
33304                                     tag : 'i',
33305                                     cls : 'fa fa-chevron-left'
33306                                 }
33307                             ]
33308                         },
33309                         {
33310                             tag : 'div',
33311                             cls : 'roo-document-slider-thumb',
33312                             cn : [
33313                                 {
33314                                     tag : 'img',
33315                                     cls : 'roo-document-slider-image'
33316                                 }
33317                             ]
33318                         },
33319                         {
33320                             tag : 'div',
33321                             cls : 'roo-document-slider-next',
33322                             cn : [
33323                                 {
33324                                     tag : 'i',
33325                                     cls : 'fa fa-chevron-right'
33326                                 }
33327                             ]
33328                         }
33329                     ]
33330                 }
33331             ]
33332         };
33333         
33334         return cfg;
33335     },
33336     
33337     initEvents : function()
33338     {
33339         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33340         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33341         
33342         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33343         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33344         
33345         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33346         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33347         
33348         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33349         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33350         
33351         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33352         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33353         
33354         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33355         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33356         
33357         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33358         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33359         
33360         this.thumbEl.on('click', this.onClick, this);
33361         
33362         this.prevIndicator.on('click', this.prev, this);
33363         
33364         this.nextIndicator.on('click', this.next, this);
33365         
33366     },
33367     
33368     initial : function()
33369     {
33370         if(this.files.length){
33371             this.indicator = 1;
33372             this.update()
33373         }
33374         
33375         this.fireEvent('initial', this);
33376     },
33377     
33378     update : function()
33379     {
33380         this.imageEl.attr('src', this.files[this.indicator - 1]);
33381         
33382         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33383         
33384         this.prevIndicator.show();
33385         
33386         if(this.indicator == 1){
33387             this.prevIndicator.hide();
33388         }
33389         
33390         this.nextIndicator.show();
33391         
33392         if(this.indicator == this.files.length){
33393             this.nextIndicator.hide();
33394         }
33395         
33396         this.thumbEl.scrollTo('top');
33397         
33398         this.fireEvent('update', this);
33399     },
33400     
33401     onClick : function(e)
33402     {
33403         e.preventDefault();
33404         
33405         this.fireEvent('click', this);
33406     },
33407     
33408     prev : function(e)
33409     {
33410         e.preventDefault();
33411         
33412         this.indicator = Math.max(1, this.indicator - 1);
33413         
33414         this.update();
33415     },
33416     
33417     next : function(e)
33418     {
33419         e.preventDefault();
33420         
33421         this.indicator = Math.min(this.files.length, this.indicator + 1);
33422         
33423         this.update();
33424     }
33425 });
33426 /*
33427  * - LGPL
33428  *
33429  * RadioSet
33430  *
33431  *
33432  */
33433
33434 /**
33435  * @class Roo.bootstrap.RadioSet
33436  * @extends Roo.bootstrap.Input
33437  * Bootstrap RadioSet class
33438  * @cfg {String} indicatorpos (left|right) default left
33439  * @cfg {Boolean} inline (true|false) inline the element (default true)
33440  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33441  * @constructor
33442  * Create a new RadioSet
33443  * @param {Object} config The config object
33444  */
33445
33446 Roo.bootstrap.RadioSet = function(config){
33447     
33448     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33449     
33450     this.radioes = [];
33451     
33452     Roo.bootstrap.RadioSet.register(this);
33453     
33454     this.addEvents({
33455         /**
33456         * @event check
33457         * Fires when the element is checked or unchecked.
33458         * @param {Roo.bootstrap.RadioSet} this This radio
33459         * @param {Roo.bootstrap.Radio} item The checked item
33460         */
33461        check : true
33462     });
33463     
33464 };
33465
33466 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33467
33468     radioes : false,
33469     
33470     inline : true,
33471     
33472     weight : '',
33473     
33474     indicatorpos : 'left',
33475     
33476     getAutoCreate : function()
33477     {
33478         var label = {
33479             tag : 'label',
33480             cls : 'roo-radio-set-label',
33481             cn : [
33482                 {
33483                     tag : 'span',
33484                     html : this.fieldLabel
33485                 }
33486             ]
33487         };
33488         
33489         if(this.indicatorpos == 'left'){
33490             label.cn.unshift({
33491                 tag : 'i',
33492                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33493                 tooltip : 'This field is required'
33494             });
33495         } else {
33496             label.cn.push({
33497                 tag : 'i',
33498                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33499                 tooltip : 'This field is required'
33500             });
33501         }
33502         
33503         var items = {
33504             tag : 'div',
33505             cls : 'roo-radio-set-items'
33506         };
33507         
33508         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33509         
33510         if (align === 'left' && this.fieldLabel.length) {
33511             
33512             items = {
33513                 cls : "roo-radio-set-right", 
33514                 cn: [
33515                     items
33516                 ]
33517             };
33518             
33519             if(this.labelWidth > 12){
33520                 label.style = "width: " + this.labelWidth + 'px';
33521             }
33522             
33523             if(this.labelWidth < 13 && this.labelmd == 0){
33524                 this.labelmd = this.labelWidth;
33525             }
33526             
33527             if(this.labellg > 0){
33528                 label.cls += ' col-lg-' + this.labellg;
33529                 items.cls += ' col-lg-' + (12 - this.labellg);
33530             }
33531             
33532             if(this.labelmd > 0){
33533                 label.cls += ' col-md-' + this.labelmd;
33534                 items.cls += ' col-md-' + (12 - this.labelmd);
33535             }
33536             
33537             if(this.labelsm > 0){
33538                 label.cls += ' col-sm-' + this.labelsm;
33539                 items.cls += ' col-sm-' + (12 - this.labelsm);
33540             }
33541             
33542             if(this.labelxs > 0){
33543                 label.cls += ' col-xs-' + this.labelxs;
33544                 items.cls += ' col-xs-' + (12 - this.labelxs);
33545             }
33546         }
33547         
33548         var cfg = {
33549             tag : 'div',
33550             cls : 'roo-radio-set',
33551             cn : [
33552                 {
33553                     tag : 'input',
33554                     cls : 'roo-radio-set-input',
33555                     type : 'hidden',
33556                     name : this.name,
33557                     value : this.value ? this.value :  ''
33558                 },
33559                 label,
33560                 items
33561             ]
33562         };
33563         
33564         if(this.weight.length){
33565             cfg.cls += ' roo-radio-' + this.weight;
33566         }
33567         
33568         if(this.inline) {
33569             cfg.cls += ' roo-radio-set-inline';
33570         }
33571         
33572         var settings=this;
33573         ['xs','sm','md','lg'].map(function(size){
33574             if (settings[size]) {
33575                 cfg.cls += ' col-' + size + '-' + settings[size];
33576             }
33577         });
33578         
33579         return cfg;
33580         
33581     },
33582
33583     initEvents : function()
33584     {
33585         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33586         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33587         
33588         if(!this.fieldLabel.length){
33589             this.labelEl.hide();
33590         }
33591         
33592         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33593         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33594         
33595         this.indicatorEl().addClass('invisible');
33596         
33597         this.originalValue = this.getValue();
33598         
33599     },
33600     
33601     inputEl: function ()
33602     {
33603         return this.el.select('.roo-radio-set-input', true).first();
33604     },
33605     
33606     getChildContainer : function()
33607     {
33608         return this.itemsEl;
33609     },
33610     
33611     register : function(item)
33612     {
33613         this.radioes.push(item);
33614         
33615     },
33616     
33617     validate : function()
33618     {   
33619         var valid = false;
33620         
33621         Roo.each(this.radioes, function(i){
33622             if(!i.checked){
33623                 return;
33624             }
33625             
33626             valid = true;
33627             return false;
33628         });
33629         
33630         if(this.allowBlank) {
33631             return true;
33632         }
33633         
33634         if(this.disabled || valid){
33635             this.markValid();
33636             return true;
33637         }
33638         
33639         this.markInvalid();
33640         return false;
33641         
33642     },
33643     
33644     markValid : function()
33645     {
33646         if(this.labelEl.isVisible(true)){
33647             this.indicatorEl().removeClass('visible');
33648             this.indicatorEl().addClass('invisible');
33649         }
33650         
33651         this.el.removeClass([this.invalidClass, this.validClass]);
33652         this.el.addClass(this.validClass);
33653         
33654         this.fireEvent('valid', this);
33655     },
33656     
33657     markInvalid : function(msg)
33658     {
33659         if(this.allowBlank || this.disabled){
33660             return;
33661         }
33662         
33663         if(this.labelEl.isVisible(true)){
33664             this.indicatorEl().removeClass('invisible');
33665             this.indicatorEl().addClass('visible');
33666         }
33667         
33668         this.el.removeClass([this.invalidClass, this.validClass]);
33669         this.el.addClass(this.invalidClass);
33670         
33671         this.fireEvent('invalid', this, msg);
33672         
33673     },
33674     
33675     setValue : function(v, suppressEvent)
33676     {   
33677         if(this.value === v){
33678             return;
33679         }
33680         
33681         this.value = v;
33682         
33683         if(this.rendered){
33684             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33685         }
33686         
33687         Roo.each(this.radioes, function(i){
33688             i.checked = false;
33689             i.el.removeClass('checked');
33690         });
33691         
33692         Roo.each(this.radioes, function(i){
33693             
33694             if(i.value === v || i.value.toString() === v.toString()){
33695                 i.checked = true;
33696                 i.el.addClass('checked');
33697                 
33698                 if(suppressEvent !== true){
33699                     this.fireEvent('check', this, i);
33700                 }
33701                 
33702                 return false;
33703             }
33704             
33705         }, this);
33706         
33707         this.validate();
33708     },
33709     
33710     clearInvalid : function(){
33711         
33712         if(!this.el || this.preventMark){
33713             return;
33714         }
33715         
33716         this.el.removeClass([this.invalidClass]);
33717         
33718         this.fireEvent('valid', this);
33719     }
33720     
33721 });
33722
33723 Roo.apply(Roo.bootstrap.RadioSet, {
33724     
33725     groups: {},
33726     
33727     register : function(set)
33728     {
33729         this.groups[set.name] = set;
33730     },
33731     
33732     get: function(name) 
33733     {
33734         if (typeof(this.groups[name]) == 'undefined') {
33735             return false;
33736         }
33737         
33738         return this.groups[name] ;
33739     }
33740     
33741 });
33742 /*
33743  * Based on:
33744  * Ext JS Library 1.1.1
33745  * Copyright(c) 2006-2007, Ext JS, LLC.
33746  *
33747  * Originally Released Under LGPL - original licence link has changed is not relivant.
33748  *
33749  * Fork - LGPL
33750  * <script type="text/javascript">
33751  */
33752
33753
33754 /**
33755  * @class Roo.bootstrap.SplitBar
33756  * @extends Roo.util.Observable
33757  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33758  * <br><br>
33759  * Usage:
33760  * <pre><code>
33761 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33762                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33763 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
33764 split.minSize = 100;
33765 split.maxSize = 600;
33766 split.animate = true;
33767 split.on('moved', splitterMoved);
33768 </code></pre>
33769  * @constructor
33770  * Create a new SplitBar
33771  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
33772  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
33773  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33774  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
33775                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
33776                         position of the SplitBar).
33777  */
33778 Roo.bootstrap.SplitBar = function(cfg){
33779     
33780     /** @private */
33781     
33782     //{
33783     //  dragElement : elm
33784     //  resizingElement: el,
33785         // optional..
33786     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
33787     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
33788         // existingProxy ???
33789     //}
33790     
33791     this.el = Roo.get(cfg.dragElement, true);
33792     this.el.dom.unselectable = "on";
33793     /** @private */
33794     this.resizingEl = Roo.get(cfg.resizingElement, true);
33795
33796     /**
33797      * @private
33798      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33799      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
33800      * @type Number
33801      */
33802     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
33803     
33804     /**
33805      * The minimum size of the resizing element. (Defaults to 0)
33806      * @type Number
33807      */
33808     this.minSize = 0;
33809     
33810     /**
33811      * The maximum size of the resizing element. (Defaults to 2000)
33812      * @type Number
33813      */
33814     this.maxSize = 2000;
33815     
33816     /**
33817      * Whether to animate the transition to the new size
33818      * @type Boolean
33819      */
33820     this.animate = false;
33821     
33822     /**
33823      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
33824      * @type Boolean
33825      */
33826     this.useShim = false;
33827     
33828     /** @private */
33829     this.shim = null;
33830     
33831     if(!cfg.existingProxy){
33832         /** @private */
33833         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
33834     }else{
33835         this.proxy = Roo.get(cfg.existingProxy).dom;
33836     }
33837     /** @private */
33838     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
33839     
33840     /** @private */
33841     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
33842     
33843     /** @private */
33844     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
33845     
33846     /** @private */
33847     this.dragSpecs = {};
33848     
33849     /**
33850      * @private The adapter to use to positon and resize elements
33851      */
33852     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33853     this.adapter.init(this);
33854     
33855     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33856         /** @private */
33857         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
33858         this.el.addClass("roo-splitbar-h");
33859     }else{
33860         /** @private */
33861         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
33862         this.el.addClass("roo-splitbar-v");
33863     }
33864     
33865     this.addEvents({
33866         /**
33867          * @event resize
33868          * Fires when the splitter is moved (alias for {@link #event-moved})
33869          * @param {Roo.bootstrap.SplitBar} this
33870          * @param {Number} newSize the new width or height
33871          */
33872         "resize" : true,
33873         /**
33874          * @event moved
33875          * Fires when the splitter is moved
33876          * @param {Roo.bootstrap.SplitBar} this
33877          * @param {Number} newSize the new width or height
33878          */
33879         "moved" : true,
33880         /**
33881          * @event beforeresize
33882          * Fires before the splitter is dragged
33883          * @param {Roo.bootstrap.SplitBar} this
33884          */
33885         "beforeresize" : true,
33886
33887         "beforeapply" : true
33888     });
33889
33890     Roo.util.Observable.call(this);
33891 };
33892
33893 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
33894     onStartProxyDrag : function(x, y){
33895         this.fireEvent("beforeresize", this);
33896         if(!this.overlay){
33897             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
33898             o.unselectable();
33899             o.enableDisplayMode("block");
33900             // all splitbars share the same overlay
33901             Roo.bootstrap.SplitBar.prototype.overlay = o;
33902         }
33903         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33904         this.overlay.show();
33905         Roo.get(this.proxy).setDisplayed("block");
33906         var size = this.adapter.getElementSize(this);
33907         this.activeMinSize = this.getMinimumSize();;
33908         this.activeMaxSize = this.getMaximumSize();;
33909         var c1 = size - this.activeMinSize;
33910         var c2 = Math.max(this.activeMaxSize - size, 0);
33911         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33912             this.dd.resetConstraints();
33913             this.dd.setXConstraint(
33914                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
33915                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
33916             );
33917             this.dd.setYConstraint(0, 0);
33918         }else{
33919             this.dd.resetConstraints();
33920             this.dd.setXConstraint(0, 0);
33921             this.dd.setYConstraint(
33922                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
33923                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
33924             );
33925          }
33926         this.dragSpecs.startSize = size;
33927         this.dragSpecs.startPoint = [x, y];
33928         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
33929     },
33930     
33931     /** 
33932      * @private Called after the drag operation by the DDProxy
33933      */
33934     onEndProxyDrag : function(e){
33935         Roo.get(this.proxy).setDisplayed(false);
33936         var endPoint = Roo.lib.Event.getXY(e);
33937         if(this.overlay){
33938             this.overlay.hide();
33939         }
33940         var newSize;
33941         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33942             newSize = this.dragSpecs.startSize + 
33943                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
33944                     endPoint[0] - this.dragSpecs.startPoint[0] :
33945                     this.dragSpecs.startPoint[0] - endPoint[0]
33946                 );
33947         }else{
33948             newSize = this.dragSpecs.startSize + 
33949                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
33950                     endPoint[1] - this.dragSpecs.startPoint[1] :
33951                     this.dragSpecs.startPoint[1] - endPoint[1]
33952                 );
33953         }
33954         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
33955         if(newSize != this.dragSpecs.startSize){
33956             if(this.fireEvent('beforeapply', this, newSize) !== false){
33957                 this.adapter.setElementSize(this, newSize);
33958                 this.fireEvent("moved", this, newSize);
33959                 this.fireEvent("resize", this, newSize);
33960             }
33961         }
33962     },
33963     
33964     /**
33965      * Get the adapter this SplitBar uses
33966      * @return The adapter object
33967      */
33968     getAdapter : function(){
33969         return this.adapter;
33970     },
33971     
33972     /**
33973      * Set the adapter this SplitBar uses
33974      * @param {Object} adapter A SplitBar adapter object
33975      */
33976     setAdapter : function(adapter){
33977         this.adapter = adapter;
33978         this.adapter.init(this);
33979     },
33980     
33981     /**
33982      * Gets the minimum size for the resizing element
33983      * @return {Number} The minimum size
33984      */
33985     getMinimumSize : function(){
33986         return this.minSize;
33987     },
33988     
33989     /**
33990      * Sets the minimum size for the resizing element
33991      * @param {Number} minSize The minimum size
33992      */
33993     setMinimumSize : function(minSize){
33994         this.minSize = minSize;
33995     },
33996     
33997     /**
33998      * Gets the maximum size for the resizing element
33999      * @return {Number} The maximum size
34000      */
34001     getMaximumSize : function(){
34002         return this.maxSize;
34003     },
34004     
34005     /**
34006      * Sets the maximum size for the resizing element
34007      * @param {Number} maxSize The maximum size
34008      */
34009     setMaximumSize : function(maxSize){
34010         this.maxSize = maxSize;
34011     },
34012     
34013     /**
34014      * Sets the initialize size for the resizing element
34015      * @param {Number} size The initial size
34016      */
34017     setCurrentSize : function(size){
34018         var oldAnimate = this.animate;
34019         this.animate = false;
34020         this.adapter.setElementSize(this, size);
34021         this.animate = oldAnimate;
34022     },
34023     
34024     /**
34025      * Destroy this splitbar. 
34026      * @param {Boolean} removeEl True to remove the element
34027      */
34028     destroy : function(removeEl){
34029         if(this.shim){
34030             this.shim.remove();
34031         }
34032         this.dd.unreg();
34033         this.proxy.parentNode.removeChild(this.proxy);
34034         if(removeEl){
34035             this.el.remove();
34036         }
34037     }
34038 });
34039
34040 /**
34041  * @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.
34042  */
34043 Roo.bootstrap.SplitBar.createProxy = function(dir){
34044     var proxy = new Roo.Element(document.createElement("div"));
34045     proxy.unselectable();
34046     var cls = 'roo-splitbar-proxy';
34047     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34048     document.body.appendChild(proxy.dom);
34049     return proxy.dom;
34050 };
34051
34052 /** 
34053  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34054  * Default Adapter. It assumes the splitter and resizing element are not positioned
34055  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34056  */
34057 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34058 };
34059
34060 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34061     // do nothing for now
34062     init : function(s){
34063     
34064     },
34065     /**
34066      * Called before drag operations to get the current size of the resizing element. 
34067      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34068      */
34069      getElementSize : function(s){
34070         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34071             return s.resizingEl.getWidth();
34072         }else{
34073             return s.resizingEl.getHeight();
34074         }
34075     },
34076     
34077     /**
34078      * Called after drag operations to set the size of the resizing element.
34079      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34080      * @param {Number} newSize The new size to set
34081      * @param {Function} onComplete A function to be invoked when resizing is complete
34082      */
34083     setElementSize : function(s, newSize, onComplete){
34084         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34085             if(!s.animate){
34086                 s.resizingEl.setWidth(newSize);
34087                 if(onComplete){
34088                     onComplete(s, newSize);
34089                 }
34090             }else{
34091                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34092             }
34093         }else{
34094             
34095             if(!s.animate){
34096                 s.resizingEl.setHeight(newSize);
34097                 if(onComplete){
34098                     onComplete(s, newSize);
34099                 }
34100             }else{
34101                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34102             }
34103         }
34104     }
34105 };
34106
34107 /** 
34108  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34109  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34110  * Adapter that  moves the splitter element to align with the resized sizing element. 
34111  * Used with an absolute positioned SplitBar.
34112  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34113  * document.body, make sure you assign an id to the body element.
34114  */
34115 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34116     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34117     this.container = Roo.get(container);
34118 };
34119
34120 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34121     init : function(s){
34122         this.basic.init(s);
34123     },
34124     
34125     getElementSize : function(s){
34126         return this.basic.getElementSize(s);
34127     },
34128     
34129     setElementSize : function(s, newSize, onComplete){
34130         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34131     },
34132     
34133     moveSplitter : function(s){
34134         var yes = Roo.bootstrap.SplitBar;
34135         switch(s.placement){
34136             case yes.LEFT:
34137                 s.el.setX(s.resizingEl.getRight());
34138                 break;
34139             case yes.RIGHT:
34140                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34141                 break;
34142             case yes.TOP:
34143                 s.el.setY(s.resizingEl.getBottom());
34144                 break;
34145             case yes.BOTTOM:
34146                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34147                 break;
34148         }
34149     }
34150 };
34151
34152 /**
34153  * Orientation constant - Create a vertical SplitBar
34154  * @static
34155  * @type Number
34156  */
34157 Roo.bootstrap.SplitBar.VERTICAL = 1;
34158
34159 /**
34160  * Orientation constant - Create a horizontal SplitBar
34161  * @static
34162  * @type Number
34163  */
34164 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34165
34166 /**
34167  * Placement constant - The resizing element is to the left of the splitter element
34168  * @static
34169  * @type Number
34170  */
34171 Roo.bootstrap.SplitBar.LEFT = 1;
34172
34173 /**
34174  * Placement constant - The resizing element is to the right of the splitter element
34175  * @static
34176  * @type Number
34177  */
34178 Roo.bootstrap.SplitBar.RIGHT = 2;
34179
34180 /**
34181  * Placement constant - The resizing element is positioned above the splitter element
34182  * @static
34183  * @type Number
34184  */
34185 Roo.bootstrap.SplitBar.TOP = 3;
34186
34187 /**
34188  * Placement constant - The resizing element is positioned under splitter element
34189  * @static
34190  * @type Number
34191  */
34192 Roo.bootstrap.SplitBar.BOTTOM = 4;
34193 Roo.namespace("Roo.bootstrap.layout");/*
34194  * Based on:
34195  * Ext JS Library 1.1.1
34196  * Copyright(c) 2006-2007, Ext JS, LLC.
34197  *
34198  * Originally Released Under LGPL - original licence link has changed is not relivant.
34199  *
34200  * Fork - LGPL
34201  * <script type="text/javascript">
34202  */
34203
34204 /**
34205  * @class Roo.bootstrap.layout.Manager
34206  * @extends Roo.bootstrap.Component
34207  * Base class for layout managers.
34208  */
34209 Roo.bootstrap.layout.Manager = function(config)
34210 {
34211     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34212
34213
34214
34215
34216
34217     /** false to disable window resize monitoring @type Boolean */
34218     this.monitorWindowResize = true;
34219     this.regions = {};
34220     this.addEvents({
34221         /**
34222          * @event layout
34223          * Fires when a layout is performed.
34224          * @param {Roo.LayoutManager} this
34225          */
34226         "layout" : true,
34227         /**
34228          * @event regionresized
34229          * Fires when the user resizes a region.
34230          * @param {Roo.LayoutRegion} region The resized region
34231          * @param {Number} newSize The new size (width for east/west, height for north/south)
34232          */
34233         "regionresized" : true,
34234         /**
34235          * @event regioncollapsed
34236          * Fires when a region is collapsed.
34237          * @param {Roo.LayoutRegion} region The collapsed region
34238          */
34239         "regioncollapsed" : true,
34240         /**
34241          * @event regionexpanded
34242          * Fires when a region is expanded.
34243          * @param {Roo.LayoutRegion} region The expanded region
34244          */
34245         "regionexpanded" : true
34246     });
34247     this.updating = false;
34248
34249     if (config.el) {
34250         this.el = Roo.get(config.el);
34251         this.initEvents();
34252     }
34253
34254 };
34255
34256 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34257
34258
34259     regions : null,
34260
34261     monitorWindowResize : true,
34262
34263
34264     updating : false,
34265
34266
34267     onRender : function(ct, position)
34268     {
34269         if(!this.el){
34270             this.el = Roo.get(ct);
34271             this.initEvents();
34272         }
34273         //this.fireEvent('render',this);
34274     },
34275
34276
34277     initEvents: function()
34278     {
34279
34280
34281         // ie scrollbar fix
34282         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34283             document.body.scroll = "no";
34284         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34285             this.el.position('relative');
34286         }
34287         this.id = this.el.id;
34288         this.el.addClass("roo-layout-container");
34289         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34290         if(this.el.dom != document.body ) {
34291             this.el.on('resize', this.layout,this);
34292             this.el.on('show', this.layout,this);
34293         }
34294
34295     },
34296
34297     /**
34298      * Returns true if this layout is currently being updated
34299      * @return {Boolean}
34300      */
34301     isUpdating : function(){
34302         return this.updating;
34303     },
34304
34305     /**
34306      * Suspend the LayoutManager from doing auto-layouts while
34307      * making multiple add or remove calls
34308      */
34309     beginUpdate : function(){
34310         this.updating = true;
34311     },
34312
34313     /**
34314      * Restore auto-layouts and optionally disable the manager from performing a layout
34315      * @param {Boolean} noLayout true to disable a layout update
34316      */
34317     endUpdate : function(noLayout){
34318         this.updating = false;
34319         if(!noLayout){
34320             this.layout();
34321         }
34322     },
34323
34324     layout: function(){
34325         // abstract...
34326     },
34327
34328     onRegionResized : function(region, newSize){
34329         this.fireEvent("regionresized", region, newSize);
34330         this.layout();
34331     },
34332
34333     onRegionCollapsed : function(region){
34334         this.fireEvent("regioncollapsed", region);
34335     },
34336
34337     onRegionExpanded : function(region){
34338         this.fireEvent("regionexpanded", region);
34339     },
34340
34341     /**
34342      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34343      * performs box-model adjustments.
34344      * @return {Object} The size as an object {width: (the width), height: (the height)}
34345      */
34346     getViewSize : function()
34347     {
34348         var size;
34349         if(this.el.dom != document.body){
34350             size = this.el.getSize();
34351         }else{
34352             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34353         }
34354         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34355         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34356         return size;
34357     },
34358
34359     /**
34360      * Returns the Element this layout is bound to.
34361      * @return {Roo.Element}
34362      */
34363     getEl : function(){
34364         return this.el;
34365     },
34366
34367     /**
34368      * Returns the specified region.
34369      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34370      * @return {Roo.LayoutRegion}
34371      */
34372     getRegion : function(target){
34373         return this.regions[target.toLowerCase()];
34374     },
34375
34376     onWindowResize : function(){
34377         if(this.monitorWindowResize){
34378             this.layout();
34379         }
34380     }
34381 });
34382 /*
34383  * Based on:
34384  * Ext JS Library 1.1.1
34385  * Copyright(c) 2006-2007, Ext JS, LLC.
34386  *
34387  * Originally Released Under LGPL - original licence link has changed is not relivant.
34388  *
34389  * Fork - LGPL
34390  * <script type="text/javascript">
34391  */
34392 /**
34393  * @class Roo.bootstrap.layout.Border
34394  * @extends Roo.bootstrap.layout.Manager
34395  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34396  * please see: examples/bootstrap/nested.html<br><br>
34397  
34398 <b>The container the layout is rendered into can be either the body element or any other element.
34399 If it is not the body element, the container needs to either be an absolute positioned element,
34400 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34401 the container size if it is not the body element.</b>
34402
34403 * @constructor
34404 * Create a new Border
34405 * @param {Object} config Configuration options
34406  */
34407 Roo.bootstrap.layout.Border = function(config){
34408     config = config || {};
34409     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34410     
34411     
34412     
34413     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34414         if(config[region]){
34415             config[region].region = region;
34416             this.addRegion(config[region]);
34417         }
34418     },this);
34419     
34420 };
34421
34422 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34423
34424 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34425     /**
34426      * Creates and adds a new region if it doesn't already exist.
34427      * @param {String} target The target region key (north, south, east, west or center).
34428      * @param {Object} config The regions config object
34429      * @return {BorderLayoutRegion} The new region
34430      */
34431     addRegion : function(config)
34432     {
34433         if(!this.regions[config.region]){
34434             var r = this.factory(config);
34435             this.bindRegion(r);
34436         }
34437         return this.regions[config.region];
34438     },
34439
34440     // private (kinda)
34441     bindRegion : function(r){
34442         this.regions[r.config.region] = r;
34443         
34444         r.on("visibilitychange",    this.layout, this);
34445         r.on("paneladded",          this.layout, this);
34446         r.on("panelremoved",        this.layout, this);
34447         r.on("invalidated",         this.layout, this);
34448         r.on("resized",             this.onRegionResized, this);
34449         r.on("collapsed",           this.onRegionCollapsed, this);
34450         r.on("expanded",            this.onRegionExpanded, this);
34451     },
34452
34453     /**
34454      * Performs a layout update.
34455      */
34456     layout : function()
34457     {
34458         if(this.updating) {
34459             return;
34460         }
34461         
34462         // render all the rebions if they have not been done alreayd?
34463         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34464             if(this.regions[region] && !this.regions[region].bodyEl){
34465                 this.regions[region].onRender(this.el)
34466             }
34467         },this);
34468         
34469         var size = this.getViewSize();
34470         var w = size.width;
34471         var h = size.height;
34472         var centerW = w;
34473         var centerH = h;
34474         var centerY = 0;
34475         var centerX = 0;
34476         //var x = 0, y = 0;
34477
34478         var rs = this.regions;
34479         var north = rs["north"];
34480         var south = rs["south"]; 
34481         var west = rs["west"];
34482         var east = rs["east"];
34483         var center = rs["center"];
34484         //if(this.hideOnLayout){ // not supported anymore
34485             //c.el.setStyle("display", "none");
34486         //}
34487         if(north && north.isVisible()){
34488             var b = north.getBox();
34489             var m = north.getMargins();
34490             b.width = w - (m.left+m.right);
34491             b.x = m.left;
34492             b.y = m.top;
34493             centerY = b.height + b.y + m.bottom;
34494             centerH -= centerY;
34495             north.updateBox(this.safeBox(b));
34496         }
34497         if(south && south.isVisible()){
34498             var b = south.getBox();
34499             var m = south.getMargins();
34500             b.width = w - (m.left+m.right);
34501             b.x = m.left;
34502             var totalHeight = (b.height + m.top + m.bottom);
34503             b.y = h - totalHeight + m.top;
34504             centerH -= totalHeight;
34505             south.updateBox(this.safeBox(b));
34506         }
34507         if(west && west.isVisible()){
34508             var b = west.getBox();
34509             var m = west.getMargins();
34510             b.height = centerH - (m.top+m.bottom);
34511             b.x = m.left;
34512             b.y = centerY + m.top;
34513             var totalWidth = (b.width + m.left + m.right);
34514             centerX += totalWidth;
34515             centerW -= totalWidth;
34516             west.updateBox(this.safeBox(b));
34517         }
34518         if(east && east.isVisible()){
34519             var b = east.getBox();
34520             var m = east.getMargins();
34521             b.height = centerH - (m.top+m.bottom);
34522             var totalWidth = (b.width + m.left + m.right);
34523             b.x = w - totalWidth + m.left;
34524             b.y = centerY + m.top;
34525             centerW -= totalWidth;
34526             east.updateBox(this.safeBox(b));
34527         }
34528         if(center){
34529             var m = center.getMargins();
34530             var centerBox = {
34531                 x: centerX + m.left,
34532                 y: centerY + m.top,
34533                 width: centerW - (m.left+m.right),
34534                 height: centerH - (m.top+m.bottom)
34535             };
34536             //if(this.hideOnLayout){
34537                 //center.el.setStyle("display", "block");
34538             //}
34539             center.updateBox(this.safeBox(centerBox));
34540         }
34541         this.el.repaint();
34542         this.fireEvent("layout", this);
34543     },
34544
34545     // private
34546     safeBox : function(box){
34547         box.width = Math.max(0, box.width);
34548         box.height = Math.max(0, box.height);
34549         return box;
34550     },
34551
34552     /**
34553      * Adds a ContentPanel (or subclass) to this layout.
34554      * @param {String} target The target region key (north, south, east, west or center).
34555      * @param {Roo.ContentPanel} panel The panel to add
34556      * @return {Roo.ContentPanel} The added panel
34557      */
34558     add : function(target, panel){
34559          
34560         target = target.toLowerCase();
34561         return this.regions[target].add(panel);
34562     },
34563
34564     /**
34565      * Remove a ContentPanel (or subclass) to this layout.
34566      * @param {String} target The target region key (north, south, east, west or center).
34567      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34568      * @return {Roo.ContentPanel} The removed panel
34569      */
34570     remove : function(target, panel){
34571         target = target.toLowerCase();
34572         return this.regions[target].remove(panel);
34573     },
34574
34575     /**
34576      * Searches all regions for a panel with the specified id
34577      * @param {String} panelId
34578      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34579      */
34580     findPanel : function(panelId){
34581         var rs = this.regions;
34582         for(var target in rs){
34583             if(typeof rs[target] != "function"){
34584                 var p = rs[target].getPanel(panelId);
34585                 if(p){
34586                     return p;
34587                 }
34588             }
34589         }
34590         return null;
34591     },
34592
34593     /**
34594      * Searches all regions for a panel with the specified id and activates (shows) it.
34595      * @param {String/ContentPanel} panelId The panels id or the panel itself
34596      * @return {Roo.ContentPanel} The shown panel or null
34597      */
34598     showPanel : function(panelId) {
34599       var rs = this.regions;
34600       for(var target in rs){
34601          var r = rs[target];
34602          if(typeof r != "function"){
34603             if(r.hasPanel(panelId)){
34604                return r.showPanel(panelId);
34605             }
34606          }
34607       }
34608       return null;
34609    },
34610
34611    /**
34612      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34613      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34614      */
34615    /*
34616     restoreState : function(provider){
34617         if(!provider){
34618             provider = Roo.state.Manager;
34619         }
34620         var sm = new Roo.LayoutStateManager();
34621         sm.init(this, provider);
34622     },
34623 */
34624  
34625  
34626     /**
34627      * Adds a xtype elements to the layout.
34628      * <pre><code>
34629
34630 layout.addxtype({
34631        xtype : 'ContentPanel',
34632        region: 'west',
34633        items: [ .... ]
34634    }
34635 );
34636
34637 layout.addxtype({
34638         xtype : 'NestedLayoutPanel',
34639         region: 'west',
34640         layout: {
34641            center: { },
34642            west: { }   
34643         },
34644         items : [ ... list of content panels or nested layout panels.. ]
34645    }
34646 );
34647 </code></pre>
34648      * @param {Object} cfg Xtype definition of item to add.
34649      */
34650     addxtype : function(cfg)
34651     {
34652         // basically accepts a pannel...
34653         // can accept a layout region..!?!?
34654         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34655         
34656         
34657         // theory?  children can only be panels??
34658         
34659         //if (!cfg.xtype.match(/Panel$/)) {
34660         //    return false;
34661         //}
34662         var ret = false;
34663         
34664         if (typeof(cfg.region) == 'undefined') {
34665             Roo.log("Failed to add Panel, region was not set");
34666             Roo.log(cfg);
34667             return false;
34668         }
34669         var region = cfg.region;
34670         delete cfg.region;
34671         
34672           
34673         var xitems = [];
34674         if (cfg.items) {
34675             xitems = cfg.items;
34676             delete cfg.items;
34677         }
34678         var nb = false;
34679         
34680         switch(cfg.xtype) 
34681         {
34682             case 'Content':  // ContentPanel (el, cfg)
34683             case 'Scroll':  // ContentPanel (el, cfg)
34684             case 'View': 
34685                 cfg.autoCreate = true;
34686                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34687                 //} else {
34688                 //    var el = this.el.createChild();
34689                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34690                 //}
34691                 
34692                 this.add(region, ret);
34693                 break;
34694             
34695             /*
34696             case 'TreePanel': // our new panel!
34697                 cfg.el = this.el.createChild();
34698                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34699                 this.add(region, ret);
34700                 break;
34701             */
34702             
34703             case 'Nest': 
34704                 // create a new Layout (which is  a Border Layout...
34705                 
34706                 var clayout = cfg.layout;
34707                 clayout.el  = this.el.createChild();
34708                 clayout.items   = clayout.items  || [];
34709                 
34710                 delete cfg.layout;
34711                 
34712                 // replace this exitems with the clayout ones..
34713                 xitems = clayout.items;
34714                  
34715                 // force background off if it's in center...
34716                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34717                     cfg.background = false;
34718                 }
34719                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
34720                 
34721                 
34722                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34723                 //console.log('adding nested layout panel '  + cfg.toSource());
34724                 this.add(region, ret);
34725                 nb = {}; /// find first...
34726                 break;
34727             
34728             case 'Grid':
34729                 
34730                 // needs grid and region
34731                 
34732                 //var el = this.getRegion(region).el.createChild();
34733                 /*
34734                  *var el = this.el.createChild();
34735                 // create the grid first...
34736                 cfg.grid.container = el;
34737                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34738                 */
34739                 
34740                 if (region == 'center' && this.active ) {
34741                     cfg.background = false;
34742                 }
34743                 
34744                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34745                 
34746                 this.add(region, ret);
34747                 /*
34748                 if (cfg.background) {
34749                     // render grid on panel activation (if panel background)
34750                     ret.on('activate', function(gp) {
34751                         if (!gp.grid.rendered) {
34752                     //        gp.grid.render(el);
34753                         }
34754                     });
34755                 } else {
34756                   //  cfg.grid.render(el);
34757                 }
34758                 */
34759                 break;
34760            
34761            
34762             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34763                 // it was the old xcomponent building that caused this before.
34764                 // espeically if border is the top element in the tree.
34765                 ret = this;
34766                 break; 
34767                 
34768                     
34769                 
34770                 
34771                 
34772             default:
34773                 /*
34774                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34775                     
34776                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34777                     this.add(region, ret);
34778                 } else {
34779                 */
34780                     Roo.log(cfg);
34781                     throw "Can not add '" + cfg.xtype + "' to Border";
34782                     return null;
34783              
34784                                 
34785              
34786         }
34787         this.beginUpdate();
34788         // add children..
34789         var region = '';
34790         var abn = {};
34791         Roo.each(xitems, function(i)  {
34792             region = nb && i.region ? i.region : false;
34793             
34794             var add = ret.addxtype(i);
34795            
34796             if (region) {
34797                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
34798                 if (!i.background) {
34799                     abn[region] = nb[region] ;
34800                 }
34801             }
34802             
34803         });
34804         this.endUpdate();
34805
34806         // make the last non-background panel active..
34807         //if (nb) { Roo.log(abn); }
34808         if (nb) {
34809             
34810             for(var r in abn) {
34811                 region = this.getRegion(r);
34812                 if (region) {
34813                     // tried using nb[r], but it does not work..
34814                      
34815                     region.showPanel(abn[r]);
34816                    
34817                 }
34818             }
34819         }
34820         return ret;
34821         
34822     },
34823     
34824     
34825 // private
34826     factory : function(cfg)
34827     {
34828         
34829         var validRegions = Roo.bootstrap.layout.Border.regions;
34830
34831         var target = cfg.region;
34832         cfg.mgr = this;
34833         
34834         var r = Roo.bootstrap.layout;
34835         Roo.log(target);
34836         switch(target){
34837             case "north":
34838                 return new r.North(cfg);
34839             case "south":
34840                 return new r.South(cfg);
34841             case "east":
34842                 return new r.East(cfg);
34843             case "west":
34844                 return new r.West(cfg);
34845             case "center":
34846                 return new r.Center(cfg);
34847         }
34848         throw 'Layout region "'+target+'" not supported.';
34849     }
34850     
34851     
34852 });
34853  /*
34854  * Based on:
34855  * Ext JS Library 1.1.1
34856  * Copyright(c) 2006-2007, Ext JS, LLC.
34857  *
34858  * Originally Released Under LGPL - original licence link has changed is not relivant.
34859  *
34860  * Fork - LGPL
34861  * <script type="text/javascript">
34862  */
34863  
34864 /**
34865  * @class Roo.bootstrap.layout.Basic
34866  * @extends Roo.util.Observable
34867  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
34868  * and does not have a titlebar, tabs or any other features. All it does is size and position 
34869  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
34870  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34871  * @cfg {string}   region  the region that it inhabits..
34872  * @cfg {bool}   skipConfig skip config?
34873  * 
34874
34875  */
34876 Roo.bootstrap.layout.Basic = function(config){
34877     
34878     this.mgr = config.mgr;
34879     
34880     this.position = config.region;
34881     
34882     var skipConfig = config.skipConfig;
34883     
34884     this.events = {
34885         /**
34886          * @scope Roo.BasicLayoutRegion
34887          */
34888         
34889         /**
34890          * @event beforeremove
34891          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
34892          * @param {Roo.LayoutRegion} this
34893          * @param {Roo.ContentPanel} panel The panel
34894          * @param {Object} e The cancel event object
34895          */
34896         "beforeremove" : true,
34897         /**
34898          * @event invalidated
34899          * Fires when the layout for this region is changed.
34900          * @param {Roo.LayoutRegion} this
34901          */
34902         "invalidated" : true,
34903         /**
34904          * @event visibilitychange
34905          * Fires when this region is shown or hidden 
34906          * @param {Roo.LayoutRegion} this
34907          * @param {Boolean} visibility true or false
34908          */
34909         "visibilitychange" : true,
34910         /**
34911          * @event paneladded
34912          * Fires when a panel is added. 
34913          * @param {Roo.LayoutRegion} this
34914          * @param {Roo.ContentPanel} panel The panel
34915          */
34916         "paneladded" : true,
34917         /**
34918          * @event panelremoved
34919          * Fires when a panel is removed. 
34920          * @param {Roo.LayoutRegion} this
34921          * @param {Roo.ContentPanel} panel The panel
34922          */
34923         "panelremoved" : true,
34924         /**
34925          * @event beforecollapse
34926          * Fires when this region before collapse.
34927          * @param {Roo.LayoutRegion} this
34928          */
34929         "beforecollapse" : true,
34930         /**
34931          * @event collapsed
34932          * Fires when this region is collapsed.
34933          * @param {Roo.LayoutRegion} this
34934          */
34935         "collapsed" : true,
34936         /**
34937          * @event expanded
34938          * Fires when this region is expanded.
34939          * @param {Roo.LayoutRegion} this
34940          */
34941         "expanded" : true,
34942         /**
34943          * @event slideshow
34944          * Fires when this region is slid into view.
34945          * @param {Roo.LayoutRegion} this
34946          */
34947         "slideshow" : true,
34948         /**
34949          * @event slidehide
34950          * Fires when this region slides out of view. 
34951          * @param {Roo.LayoutRegion} this
34952          */
34953         "slidehide" : true,
34954         /**
34955          * @event panelactivated
34956          * Fires when a panel is activated. 
34957          * @param {Roo.LayoutRegion} this
34958          * @param {Roo.ContentPanel} panel The activated panel
34959          */
34960         "panelactivated" : true,
34961         /**
34962          * @event resized
34963          * Fires when the user resizes this region. 
34964          * @param {Roo.LayoutRegion} this
34965          * @param {Number} newSize The new size (width for east/west, height for north/south)
34966          */
34967         "resized" : true
34968     };
34969     /** A collection of panels in this region. @type Roo.util.MixedCollection */
34970     this.panels = new Roo.util.MixedCollection();
34971     this.panels.getKey = this.getPanelId.createDelegate(this);
34972     this.box = null;
34973     this.activePanel = null;
34974     // ensure listeners are added...
34975     
34976     if (config.listeners || config.events) {
34977         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
34978             listeners : config.listeners || {},
34979             events : config.events || {}
34980         });
34981     }
34982     
34983     if(skipConfig !== true){
34984         this.applyConfig(config);
34985     }
34986 };
34987
34988 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
34989 {
34990     getPanelId : function(p){
34991         return p.getId();
34992     },
34993     
34994     applyConfig : function(config){
34995         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34996         this.config = config;
34997         
34998     },
34999     
35000     /**
35001      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
35002      * the width, for horizontal (north, south) the height.
35003      * @param {Number} newSize The new width or height
35004      */
35005     resizeTo : function(newSize){
35006         var el = this.el ? this.el :
35007                  (this.activePanel ? this.activePanel.getEl() : null);
35008         if(el){
35009             switch(this.position){
35010                 case "east":
35011                 case "west":
35012                     el.setWidth(newSize);
35013                     this.fireEvent("resized", this, newSize);
35014                 break;
35015                 case "north":
35016                 case "south":
35017                     el.setHeight(newSize);
35018                     this.fireEvent("resized", this, newSize);
35019                 break;                
35020             }
35021         }
35022     },
35023     
35024     getBox : function(){
35025         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35026     },
35027     
35028     getMargins : function(){
35029         return this.margins;
35030     },
35031     
35032     updateBox : function(box){
35033         this.box = box;
35034         var el = this.activePanel.getEl();
35035         el.dom.style.left = box.x + "px";
35036         el.dom.style.top = box.y + "px";
35037         this.activePanel.setSize(box.width, box.height);
35038     },
35039     
35040     /**
35041      * Returns the container element for this region.
35042      * @return {Roo.Element}
35043      */
35044     getEl : function(){
35045         return this.activePanel;
35046     },
35047     
35048     /**
35049      * Returns true if this region is currently visible.
35050      * @return {Boolean}
35051      */
35052     isVisible : function(){
35053         return this.activePanel ? true : false;
35054     },
35055     
35056     setActivePanel : function(panel){
35057         panel = this.getPanel(panel);
35058         if(this.activePanel && this.activePanel != panel){
35059             this.activePanel.setActiveState(false);
35060             this.activePanel.getEl().setLeftTop(-10000,-10000);
35061         }
35062         this.activePanel = panel;
35063         panel.setActiveState(true);
35064         if(this.box){
35065             panel.setSize(this.box.width, this.box.height);
35066         }
35067         this.fireEvent("panelactivated", this, panel);
35068         this.fireEvent("invalidated");
35069     },
35070     
35071     /**
35072      * Show the specified panel.
35073      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35074      * @return {Roo.ContentPanel} The shown panel or null
35075      */
35076     showPanel : function(panel){
35077         panel = this.getPanel(panel);
35078         if(panel){
35079             this.setActivePanel(panel);
35080         }
35081         return panel;
35082     },
35083     
35084     /**
35085      * Get the active panel for this region.
35086      * @return {Roo.ContentPanel} The active panel or null
35087      */
35088     getActivePanel : function(){
35089         return this.activePanel;
35090     },
35091     
35092     /**
35093      * Add the passed ContentPanel(s)
35094      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35095      * @return {Roo.ContentPanel} The panel added (if only one was added)
35096      */
35097     add : function(panel){
35098         if(arguments.length > 1){
35099             for(var i = 0, len = arguments.length; i < len; i++) {
35100                 this.add(arguments[i]);
35101             }
35102             return null;
35103         }
35104         if(this.hasPanel(panel)){
35105             this.showPanel(panel);
35106             return panel;
35107         }
35108         var el = panel.getEl();
35109         if(el.dom.parentNode != this.mgr.el.dom){
35110             this.mgr.el.dom.appendChild(el.dom);
35111         }
35112         if(panel.setRegion){
35113             panel.setRegion(this);
35114         }
35115         this.panels.add(panel);
35116         el.setStyle("position", "absolute");
35117         if(!panel.background){
35118             this.setActivePanel(panel);
35119             if(this.config.initialSize && this.panels.getCount()==1){
35120                 this.resizeTo(this.config.initialSize);
35121             }
35122         }
35123         this.fireEvent("paneladded", this, panel);
35124         return panel;
35125     },
35126     
35127     /**
35128      * Returns true if the panel is in this region.
35129      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35130      * @return {Boolean}
35131      */
35132     hasPanel : function(panel){
35133         if(typeof panel == "object"){ // must be panel obj
35134             panel = panel.getId();
35135         }
35136         return this.getPanel(panel) ? true : false;
35137     },
35138     
35139     /**
35140      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35141      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35142      * @param {Boolean} preservePanel Overrides the config preservePanel option
35143      * @return {Roo.ContentPanel} The panel that was removed
35144      */
35145     remove : function(panel, preservePanel){
35146         panel = this.getPanel(panel);
35147         if(!panel){
35148             return null;
35149         }
35150         var e = {};
35151         this.fireEvent("beforeremove", this, panel, e);
35152         if(e.cancel === true){
35153             return null;
35154         }
35155         var panelId = panel.getId();
35156         this.panels.removeKey(panelId);
35157         return panel;
35158     },
35159     
35160     /**
35161      * Returns the panel specified or null if it's not in this region.
35162      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35163      * @return {Roo.ContentPanel}
35164      */
35165     getPanel : function(id){
35166         if(typeof id == "object"){ // must be panel obj
35167             return id;
35168         }
35169         return this.panels.get(id);
35170     },
35171     
35172     /**
35173      * Returns this regions position (north/south/east/west/center).
35174      * @return {String} 
35175      */
35176     getPosition: function(){
35177         return this.position;    
35178     }
35179 });/*
35180  * Based on:
35181  * Ext JS Library 1.1.1
35182  * Copyright(c) 2006-2007, Ext JS, LLC.
35183  *
35184  * Originally Released Under LGPL - original licence link has changed is not relivant.
35185  *
35186  * Fork - LGPL
35187  * <script type="text/javascript">
35188  */
35189  
35190 /**
35191  * @class Roo.bootstrap.layout.Region
35192  * @extends Roo.bootstrap.layout.Basic
35193  * This class represents a region in a layout manager.
35194  
35195  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35196  * @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})
35197  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35198  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35199  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35200  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35201  * @cfg {String}    title           The title for the region (overrides panel titles)
35202  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35203  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35204  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35205  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35206  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35207  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35208  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35209  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35210  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35211  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35212
35213  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35214  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35215  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35216  * @cfg {Number}    width           For East/West panels
35217  * @cfg {Number}    height          For North/South panels
35218  * @cfg {Boolean}   split           To show the splitter
35219  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35220  * 
35221  * @cfg {string}   cls             Extra CSS classes to add to region
35222  * 
35223  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35224  * @cfg {string}   region  the region that it inhabits..
35225  *
35226
35227  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35228  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35229
35230  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35231  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35232  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35233  */
35234 Roo.bootstrap.layout.Region = function(config)
35235 {
35236     this.applyConfig(config);
35237
35238     var mgr = config.mgr;
35239     var pos = config.region;
35240     config.skipConfig = true;
35241     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35242     
35243     if (mgr.el) {
35244         this.onRender(mgr.el);   
35245     }
35246      
35247     this.visible = true;
35248     this.collapsed = false;
35249     this.unrendered_panels = [];
35250 };
35251
35252 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35253
35254     position: '', // set by wrapper (eg. north/south etc..)
35255     unrendered_panels : null,  // unrendered panels.
35256     createBody : function(){
35257         /** This region's body element 
35258         * @type Roo.Element */
35259         this.bodyEl = this.el.createChild({
35260                 tag: "div",
35261                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35262         });
35263     },
35264
35265     onRender: function(ctr, pos)
35266     {
35267         var dh = Roo.DomHelper;
35268         /** This region's container element 
35269         * @type Roo.Element */
35270         this.el = dh.append(ctr.dom, {
35271                 tag: "div",
35272                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35273             }, true);
35274         /** This region's title element 
35275         * @type Roo.Element */
35276     
35277         this.titleEl = dh.append(this.el.dom,
35278             {
35279                     tag: "div",
35280                     unselectable: "on",
35281                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35282                     children:[
35283                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35284                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35285                     ]}, true);
35286         
35287         this.titleEl.enableDisplayMode();
35288         /** This region's title text element 
35289         * @type HTMLElement */
35290         this.titleTextEl = this.titleEl.dom.firstChild;
35291         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35292         /*
35293         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35294         this.closeBtn.enableDisplayMode();
35295         this.closeBtn.on("click", this.closeClicked, this);
35296         this.closeBtn.hide();
35297     */
35298         this.createBody(this.config);
35299         if(this.config.hideWhenEmpty){
35300             this.hide();
35301             this.on("paneladded", this.validateVisibility, this);
35302             this.on("panelremoved", this.validateVisibility, this);
35303         }
35304         if(this.autoScroll){
35305             this.bodyEl.setStyle("overflow", "auto");
35306         }else{
35307             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35308         }
35309         //if(c.titlebar !== false){
35310             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35311                 this.titleEl.hide();
35312             }else{
35313                 this.titleEl.show();
35314                 if(this.config.title){
35315                     this.titleTextEl.innerHTML = this.config.title;
35316                 }
35317             }
35318         //}
35319         if(this.config.collapsed){
35320             this.collapse(true);
35321         }
35322         if(this.config.hidden){
35323             this.hide();
35324         }
35325         
35326         if (this.unrendered_panels && this.unrendered_panels.length) {
35327             for (var i =0;i< this.unrendered_panels.length; i++) {
35328                 this.add(this.unrendered_panels[i]);
35329             }
35330             this.unrendered_panels = null;
35331             
35332         }
35333         
35334     },
35335     
35336     applyConfig : function(c)
35337     {
35338         /*
35339          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35340             var dh = Roo.DomHelper;
35341             if(c.titlebar !== false){
35342                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35343                 this.collapseBtn.on("click", this.collapse, this);
35344                 this.collapseBtn.enableDisplayMode();
35345                 /*
35346                 if(c.showPin === true || this.showPin){
35347                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35348                     this.stickBtn.enableDisplayMode();
35349                     this.stickBtn.on("click", this.expand, this);
35350                     this.stickBtn.hide();
35351                 }
35352                 
35353             }
35354             */
35355             /** This region's collapsed element
35356             * @type Roo.Element */
35357             /*
35358              *
35359             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35360                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35361             ]}, true);
35362             
35363             if(c.floatable !== false){
35364                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35365                this.collapsedEl.on("click", this.collapseClick, this);
35366             }
35367
35368             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35369                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35370                    id: "message", unselectable: "on", style:{"float":"left"}});
35371                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35372              }
35373             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35374             this.expandBtn.on("click", this.expand, this);
35375             
35376         }
35377         
35378         if(this.collapseBtn){
35379             this.collapseBtn.setVisible(c.collapsible == true);
35380         }
35381         
35382         this.cmargins = c.cmargins || this.cmargins ||
35383                          (this.position == "west" || this.position == "east" ?
35384                              {top: 0, left: 2, right:2, bottom: 0} :
35385                              {top: 2, left: 0, right:0, bottom: 2});
35386         */
35387         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35388         
35389         
35390         this.bottomTabs = c.tabPosition != "top";
35391         
35392         this.autoScroll = c.autoScroll || false;
35393         
35394         
35395        
35396         
35397         this.duration = c.duration || .30;
35398         this.slideDuration = c.slideDuration || .45;
35399         this.config = c;
35400        
35401     },
35402     /**
35403      * Returns true if this region is currently visible.
35404      * @return {Boolean}
35405      */
35406     isVisible : function(){
35407         return this.visible;
35408     },
35409
35410     /**
35411      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35412      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35413      */
35414     //setCollapsedTitle : function(title){
35415     //    title = title || "&#160;";
35416      //   if(this.collapsedTitleTextEl){
35417       //      this.collapsedTitleTextEl.innerHTML = title;
35418        // }
35419     //},
35420
35421     getBox : function(){
35422         var b;
35423       //  if(!this.collapsed){
35424             b = this.el.getBox(false, true);
35425        // }else{
35426           //  b = this.collapsedEl.getBox(false, true);
35427         //}
35428         return b;
35429     },
35430
35431     getMargins : function(){
35432         return this.margins;
35433         //return this.collapsed ? this.cmargins : this.margins;
35434     },
35435 /*
35436     highlight : function(){
35437         this.el.addClass("x-layout-panel-dragover");
35438     },
35439
35440     unhighlight : function(){
35441         this.el.removeClass("x-layout-panel-dragover");
35442     },
35443 */
35444     updateBox : function(box)
35445     {
35446         if (!this.bodyEl) {
35447             return; // not rendered yet..
35448         }
35449         
35450         this.box = box;
35451         if(!this.collapsed){
35452             this.el.dom.style.left = box.x + "px";
35453             this.el.dom.style.top = box.y + "px";
35454             this.updateBody(box.width, box.height);
35455         }else{
35456             this.collapsedEl.dom.style.left = box.x + "px";
35457             this.collapsedEl.dom.style.top = box.y + "px";
35458             this.collapsedEl.setSize(box.width, box.height);
35459         }
35460         if(this.tabs){
35461             this.tabs.autoSizeTabs();
35462         }
35463     },
35464
35465     updateBody : function(w, h)
35466     {
35467         if(w !== null){
35468             this.el.setWidth(w);
35469             w -= this.el.getBorderWidth("rl");
35470             if(this.config.adjustments){
35471                 w += this.config.adjustments[0];
35472             }
35473         }
35474         if(h !== null && h > 0){
35475             this.el.setHeight(h);
35476             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35477             h -= this.el.getBorderWidth("tb");
35478             if(this.config.adjustments){
35479                 h += this.config.adjustments[1];
35480             }
35481             this.bodyEl.setHeight(h);
35482             if(this.tabs){
35483                 h = this.tabs.syncHeight(h);
35484             }
35485         }
35486         if(this.panelSize){
35487             w = w !== null ? w : this.panelSize.width;
35488             h = h !== null ? h : this.panelSize.height;
35489         }
35490         if(this.activePanel){
35491             var el = this.activePanel.getEl();
35492             w = w !== null ? w : el.getWidth();
35493             h = h !== null ? h : el.getHeight();
35494             this.panelSize = {width: w, height: h};
35495             this.activePanel.setSize(w, h);
35496         }
35497         if(Roo.isIE && this.tabs){
35498             this.tabs.el.repaint();
35499         }
35500     },
35501
35502     /**
35503      * Returns the container element for this region.
35504      * @return {Roo.Element}
35505      */
35506     getEl : function(){
35507         return this.el;
35508     },
35509
35510     /**
35511      * Hides this region.
35512      */
35513     hide : function(){
35514         //if(!this.collapsed){
35515             this.el.dom.style.left = "-2000px";
35516             this.el.hide();
35517         //}else{
35518          //   this.collapsedEl.dom.style.left = "-2000px";
35519          //   this.collapsedEl.hide();
35520        // }
35521         this.visible = false;
35522         this.fireEvent("visibilitychange", this, false);
35523     },
35524
35525     /**
35526      * Shows this region if it was previously hidden.
35527      */
35528     show : function(){
35529         //if(!this.collapsed){
35530             this.el.show();
35531         //}else{
35532         //    this.collapsedEl.show();
35533        // }
35534         this.visible = true;
35535         this.fireEvent("visibilitychange", this, true);
35536     },
35537 /*
35538     closeClicked : function(){
35539         if(this.activePanel){
35540             this.remove(this.activePanel);
35541         }
35542     },
35543
35544     collapseClick : function(e){
35545         if(this.isSlid){
35546            e.stopPropagation();
35547            this.slideIn();
35548         }else{
35549            e.stopPropagation();
35550            this.slideOut();
35551         }
35552     },
35553 */
35554     /**
35555      * Collapses this region.
35556      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35557      */
35558     /*
35559     collapse : function(skipAnim, skipCheck = false){
35560         if(this.collapsed) {
35561             return;
35562         }
35563         
35564         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35565             
35566             this.collapsed = true;
35567             if(this.split){
35568                 this.split.el.hide();
35569             }
35570             if(this.config.animate && skipAnim !== true){
35571                 this.fireEvent("invalidated", this);
35572                 this.animateCollapse();
35573             }else{
35574                 this.el.setLocation(-20000,-20000);
35575                 this.el.hide();
35576                 this.collapsedEl.show();
35577                 this.fireEvent("collapsed", this);
35578                 this.fireEvent("invalidated", this);
35579             }
35580         }
35581         
35582     },
35583 */
35584     animateCollapse : function(){
35585         // overridden
35586     },
35587
35588     /**
35589      * Expands this region if it was previously collapsed.
35590      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35591      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35592      */
35593     /*
35594     expand : function(e, skipAnim){
35595         if(e) {
35596             e.stopPropagation();
35597         }
35598         if(!this.collapsed || this.el.hasActiveFx()) {
35599             return;
35600         }
35601         if(this.isSlid){
35602             this.afterSlideIn();
35603             skipAnim = true;
35604         }
35605         this.collapsed = false;
35606         if(this.config.animate && skipAnim !== true){
35607             this.animateExpand();
35608         }else{
35609             this.el.show();
35610             if(this.split){
35611                 this.split.el.show();
35612             }
35613             this.collapsedEl.setLocation(-2000,-2000);
35614             this.collapsedEl.hide();
35615             this.fireEvent("invalidated", this);
35616             this.fireEvent("expanded", this);
35617         }
35618     },
35619 */
35620     animateExpand : function(){
35621         // overridden
35622     },
35623
35624     initTabs : function()
35625     {
35626         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35627         
35628         var ts = new Roo.bootstrap.panel.Tabs({
35629                 el: this.bodyEl.dom,
35630                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35631                 disableTooltips: this.config.disableTabTips,
35632                 toolbar : this.config.toolbar
35633             });
35634         
35635         if(this.config.hideTabs){
35636             ts.stripWrap.setDisplayed(false);
35637         }
35638         this.tabs = ts;
35639         ts.resizeTabs = this.config.resizeTabs === true;
35640         ts.minTabWidth = this.config.minTabWidth || 40;
35641         ts.maxTabWidth = this.config.maxTabWidth || 250;
35642         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35643         ts.monitorResize = false;
35644         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35645         ts.bodyEl.addClass('roo-layout-tabs-body');
35646         this.panels.each(this.initPanelAsTab, this);
35647     },
35648
35649     initPanelAsTab : function(panel){
35650         var ti = this.tabs.addTab(
35651             panel.getEl().id,
35652             panel.getTitle(),
35653             null,
35654             this.config.closeOnTab && panel.isClosable(),
35655             panel.tpl
35656         );
35657         if(panel.tabTip !== undefined){
35658             ti.setTooltip(panel.tabTip);
35659         }
35660         ti.on("activate", function(){
35661               this.setActivePanel(panel);
35662         }, this);
35663         
35664         if(this.config.closeOnTab){
35665             ti.on("beforeclose", function(t, e){
35666                 e.cancel = true;
35667                 this.remove(panel);
35668             }, this);
35669         }
35670         
35671         panel.tabItem = ti;
35672         
35673         return ti;
35674     },
35675
35676     updatePanelTitle : function(panel, title)
35677     {
35678         if(this.activePanel == panel){
35679             this.updateTitle(title);
35680         }
35681         if(this.tabs){
35682             var ti = this.tabs.getTab(panel.getEl().id);
35683             ti.setText(title);
35684             if(panel.tabTip !== undefined){
35685                 ti.setTooltip(panel.tabTip);
35686             }
35687         }
35688     },
35689
35690     updateTitle : function(title){
35691         if(this.titleTextEl && !this.config.title){
35692             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
35693         }
35694     },
35695
35696     setActivePanel : function(panel)
35697     {
35698         panel = this.getPanel(panel);
35699         if(this.activePanel && this.activePanel != panel){
35700             if(this.activePanel.setActiveState(false) === false){
35701                 return;
35702             }
35703         }
35704         this.activePanel = panel;
35705         panel.setActiveState(true);
35706         if(this.panelSize){
35707             panel.setSize(this.panelSize.width, this.panelSize.height);
35708         }
35709         if(this.closeBtn){
35710             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35711         }
35712         this.updateTitle(panel.getTitle());
35713         if(this.tabs){
35714             this.fireEvent("invalidated", this);
35715         }
35716         this.fireEvent("panelactivated", this, panel);
35717     },
35718
35719     /**
35720      * Shows the specified panel.
35721      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35722      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35723      */
35724     showPanel : function(panel)
35725     {
35726         panel = this.getPanel(panel);
35727         if(panel){
35728             if(this.tabs){
35729                 var tab = this.tabs.getTab(panel.getEl().id);
35730                 if(tab.isHidden()){
35731                     this.tabs.unhideTab(tab.id);
35732                 }
35733                 tab.activate();
35734             }else{
35735                 this.setActivePanel(panel);
35736             }
35737         }
35738         return panel;
35739     },
35740
35741     /**
35742      * Get the active panel for this region.
35743      * @return {Roo.ContentPanel} The active panel or null
35744      */
35745     getActivePanel : function(){
35746         return this.activePanel;
35747     },
35748
35749     validateVisibility : function(){
35750         if(this.panels.getCount() < 1){
35751             this.updateTitle("&#160;");
35752             this.closeBtn.hide();
35753             this.hide();
35754         }else{
35755             if(!this.isVisible()){
35756                 this.show();
35757             }
35758         }
35759     },
35760
35761     /**
35762      * Adds the passed ContentPanel(s) to this region.
35763      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35764      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35765      */
35766     add : function(panel)
35767     {
35768         if(arguments.length > 1){
35769             for(var i = 0, len = arguments.length; i < len; i++) {
35770                 this.add(arguments[i]);
35771             }
35772             return null;
35773         }
35774         
35775         // if we have not been rendered yet, then we can not really do much of this..
35776         if (!this.bodyEl) {
35777             this.unrendered_panels.push(panel);
35778             return panel;
35779         }
35780         
35781         
35782         
35783         
35784         if(this.hasPanel(panel)){
35785             this.showPanel(panel);
35786             return panel;
35787         }
35788         panel.setRegion(this);
35789         this.panels.add(panel);
35790        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
35791             // sinle panel - no tab...?? would it not be better to render it with the tabs,
35792             // and hide them... ???
35793             this.bodyEl.dom.appendChild(panel.getEl().dom);
35794             if(panel.background !== true){
35795                 this.setActivePanel(panel);
35796             }
35797             this.fireEvent("paneladded", this, panel);
35798             return panel;
35799         }
35800         */
35801         if(!this.tabs){
35802             this.initTabs();
35803         }else{
35804             this.initPanelAsTab(panel);
35805         }
35806         
35807         
35808         if(panel.background !== true){
35809             this.tabs.activate(panel.getEl().id);
35810         }
35811         this.fireEvent("paneladded", this, panel);
35812         return panel;
35813     },
35814
35815     /**
35816      * Hides the tab for the specified panel.
35817      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35818      */
35819     hidePanel : function(panel){
35820         if(this.tabs && (panel = this.getPanel(panel))){
35821             this.tabs.hideTab(panel.getEl().id);
35822         }
35823     },
35824
35825     /**
35826      * Unhides the tab for a previously hidden panel.
35827      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35828      */
35829     unhidePanel : function(panel){
35830         if(this.tabs && (panel = this.getPanel(panel))){
35831             this.tabs.unhideTab(panel.getEl().id);
35832         }
35833     },
35834
35835     clearPanels : function(){
35836         while(this.panels.getCount() > 0){
35837              this.remove(this.panels.first());
35838         }
35839     },
35840
35841     /**
35842      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35843      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35844      * @param {Boolean} preservePanel Overrides the config preservePanel option
35845      * @return {Roo.ContentPanel} The panel that was removed
35846      */
35847     remove : function(panel, preservePanel)
35848     {
35849         panel = this.getPanel(panel);
35850         if(!panel){
35851             return null;
35852         }
35853         var e = {};
35854         this.fireEvent("beforeremove", this, panel, e);
35855         if(e.cancel === true){
35856             return null;
35857         }
35858         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
35859         var panelId = panel.getId();
35860         this.panels.removeKey(panelId);
35861         if(preservePanel){
35862             document.body.appendChild(panel.getEl().dom);
35863         }
35864         if(this.tabs){
35865             this.tabs.removeTab(panel.getEl().id);
35866         }else if (!preservePanel){
35867             this.bodyEl.dom.removeChild(panel.getEl().dom);
35868         }
35869         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
35870             var p = this.panels.first();
35871             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
35872             tempEl.appendChild(p.getEl().dom);
35873             this.bodyEl.update("");
35874             this.bodyEl.dom.appendChild(p.getEl().dom);
35875             tempEl = null;
35876             this.updateTitle(p.getTitle());
35877             this.tabs = null;
35878             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
35879             this.setActivePanel(p);
35880         }
35881         panel.setRegion(null);
35882         if(this.activePanel == panel){
35883             this.activePanel = null;
35884         }
35885         if(this.config.autoDestroy !== false && preservePanel !== true){
35886             try{panel.destroy();}catch(e){}
35887         }
35888         this.fireEvent("panelremoved", this, panel);
35889         return panel;
35890     },
35891
35892     /**
35893      * Returns the TabPanel component used by this region
35894      * @return {Roo.TabPanel}
35895      */
35896     getTabs : function(){
35897         return this.tabs;
35898     },
35899
35900     createTool : function(parentEl, className){
35901         var btn = Roo.DomHelper.append(parentEl, {
35902             tag: "div",
35903             cls: "x-layout-tools-button",
35904             children: [ {
35905                 tag: "div",
35906                 cls: "roo-layout-tools-button-inner " + className,
35907                 html: "&#160;"
35908             }]
35909         }, true);
35910         btn.addClassOnOver("roo-layout-tools-button-over");
35911         return btn;
35912     }
35913 });/*
35914  * Based on:
35915  * Ext JS Library 1.1.1
35916  * Copyright(c) 2006-2007, Ext JS, LLC.
35917  *
35918  * Originally Released Under LGPL - original licence link has changed is not relivant.
35919  *
35920  * Fork - LGPL
35921  * <script type="text/javascript">
35922  */
35923  
35924
35925
35926 /**
35927  * @class Roo.SplitLayoutRegion
35928  * @extends Roo.LayoutRegion
35929  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
35930  */
35931 Roo.bootstrap.layout.Split = function(config){
35932     this.cursor = config.cursor;
35933     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
35934 };
35935
35936 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
35937 {
35938     splitTip : "Drag to resize.",
35939     collapsibleSplitTip : "Drag to resize. Double click to hide.",
35940     useSplitTips : false,
35941
35942     applyConfig : function(config){
35943         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
35944     },
35945     
35946     onRender : function(ctr,pos) {
35947         
35948         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
35949         if(!this.config.split){
35950             return;
35951         }
35952         if(!this.split){
35953             
35954             var splitEl = Roo.DomHelper.append(ctr.dom,  {
35955                             tag: "div",
35956                             id: this.el.id + "-split",
35957                             cls: "roo-layout-split roo-layout-split-"+this.position,
35958                             html: "&#160;"
35959             });
35960             /** The SplitBar for this region 
35961             * @type Roo.SplitBar */
35962             // does not exist yet...
35963             Roo.log([this.position, this.orientation]);
35964             
35965             this.split = new Roo.bootstrap.SplitBar({
35966                 dragElement : splitEl,
35967                 resizingElement: this.el,
35968                 orientation : this.orientation
35969             });
35970             
35971             this.split.on("moved", this.onSplitMove, this);
35972             this.split.useShim = this.config.useShim === true;
35973             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
35974             if(this.useSplitTips){
35975                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
35976             }
35977             //if(config.collapsible){
35978             //    this.split.el.on("dblclick", this.collapse,  this);
35979             //}
35980         }
35981         if(typeof this.config.minSize != "undefined"){
35982             this.split.minSize = this.config.minSize;
35983         }
35984         if(typeof this.config.maxSize != "undefined"){
35985             this.split.maxSize = this.config.maxSize;
35986         }
35987         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
35988             this.hideSplitter();
35989         }
35990         
35991     },
35992
35993     getHMaxSize : function(){
35994          var cmax = this.config.maxSize || 10000;
35995          var center = this.mgr.getRegion("center");
35996          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
35997     },
35998
35999     getVMaxSize : function(){
36000          var cmax = this.config.maxSize || 10000;
36001          var center = this.mgr.getRegion("center");
36002          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
36003     },
36004
36005     onSplitMove : function(split, newSize){
36006         this.fireEvent("resized", this, newSize);
36007     },
36008     
36009     /** 
36010      * Returns the {@link Roo.SplitBar} for this region.
36011      * @return {Roo.SplitBar}
36012      */
36013     getSplitBar : function(){
36014         return this.split;
36015     },
36016     
36017     hide : function(){
36018         this.hideSplitter();
36019         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36020     },
36021
36022     hideSplitter : function(){
36023         if(this.split){
36024             this.split.el.setLocation(-2000,-2000);
36025             this.split.el.hide();
36026         }
36027     },
36028
36029     show : function(){
36030         if(this.split){
36031             this.split.el.show();
36032         }
36033         Roo.bootstrap.layout.Split.superclass.show.call(this);
36034     },
36035     
36036     beforeSlide: function(){
36037         if(Roo.isGecko){// firefox overflow auto bug workaround
36038             this.bodyEl.clip();
36039             if(this.tabs) {
36040                 this.tabs.bodyEl.clip();
36041             }
36042             if(this.activePanel){
36043                 this.activePanel.getEl().clip();
36044                 
36045                 if(this.activePanel.beforeSlide){
36046                     this.activePanel.beforeSlide();
36047                 }
36048             }
36049         }
36050     },
36051     
36052     afterSlide : function(){
36053         if(Roo.isGecko){// firefox overflow auto bug workaround
36054             this.bodyEl.unclip();
36055             if(this.tabs) {
36056                 this.tabs.bodyEl.unclip();
36057             }
36058             if(this.activePanel){
36059                 this.activePanel.getEl().unclip();
36060                 if(this.activePanel.afterSlide){
36061                     this.activePanel.afterSlide();
36062                 }
36063             }
36064         }
36065     },
36066
36067     initAutoHide : function(){
36068         if(this.autoHide !== false){
36069             if(!this.autoHideHd){
36070                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36071                 this.autoHideHd = {
36072                     "mouseout": function(e){
36073                         if(!e.within(this.el, true)){
36074                             st.delay(500);
36075                         }
36076                     },
36077                     "mouseover" : function(e){
36078                         st.cancel();
36079                     },
36080                     scope : this
36081                 };
36082             }
36083             this.el.on(this.autoHideHd);
36084         }
36085     },
36086
36087     clearAutoHide : function(){
36088         if(this.autoHide !== false){
36089             this.el.un("mouseout", this.autoHideHd.mouseout);
36090             this.el.un("mouseover", this.autoHideHd.mouseover);
36091         }
36092     },
36093
36094     clearMonitor : function(){
36095         Roo.get(document).un("click", this.slideInIf, this);
36096     },
36097
36098     // these names are backwards but not changed for compat
36099     slideOut : function(){
36100         if(this.isSlid || this.el.hasActiveFx()){
36101             return;
36102         }
36103         this.isSlid = true;
36104         if(this.collapseBtn){
36105             this.collapseBtn.hide();
36106         }
36107         this.closeBtnState = this.closeBtn.getStyle('display');
36108         this.closeBtn.hide();
36109         if(this.stickBtn){
36110             this.stickBtn.show();
36111         }
36112         this.el.show();
36113         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36114         this.beforeSlide();
36115         this.el.setStyle("z-index", 10001);
36116         this.el.slideIn(this.getSlideAnchor(), {
36117             callback: function(){
36118                 this.afterSlide();
36119                 this.initAutoHide();
36120                 Roo.get(document).on("click", this.slideInIf, this);
36121                 this.fireEvent("slideshow", this);
36122             },
36123             scope: this,
36124             block: true
36125         });
36126     },
36127
36128     afterSlideIn : function(){
36129         this.clearAutoHide();
36130         this.isSlid = false;
36131         this.clearMonitor();
36132         this.el.setStyle("z-index", "");
36133         if(this.collapseBtn){
36134             this.collapseBtn.show();
36135         }
36136         this.closeBtn.setStyle('display', this.closeBtnState);
36137         if(this.stickBtn){
36138             this.stickBtn.hide();
36139         }
36140         this.fireEvent("slidehide", this);
36141     },
36142
36143     slideIn : function(cb){
36144         if(!this.isSlid || this.el.hasActiveFx()){
36145             Roo.callback(cb);
36146             return;
36147         }
36148         this.isSlid = false;
36149         this.beforeSlide();
36150         this.el.slideOut(this.getSlideAnchor(), {
36151             callback: function(){
36152                 this.el.setLeftTop(-10000, -10000);
36153                 this.afterSlide();
36154                 this.afterSlideIn();
36155                 Roo.callback(cb);
36156             },
36157             scope: this,
36158             block: true
36159         });
36160     },
36161     
36162     slideInIf : function(e){
36163         if(!e.within(this.el)){
36164             this.slideIn();
36165         }
36166     },
36167
36168     animateCollapse : function(){
36169         this.beforeSlide();
36170         this.el.setStyle("z-index", 20000);
36171         var anchor = this.getSlideAnchor();
36172         this.el.slideOut(anchor, {
36173             callback : function(){
36174                 this.el.setStyle("z-index", "");
36175                 this.collapsedEl.slideIn(anchor, {duration:.3});
36176                 this.afterSlide();
36177                 this.el.setLocation(-10000,-10000);
36178                 this.el.hide();
36179                 this.fireEvent("collapsed", this);
36180             },
36181             scope: this,
36182             block: true
36183         });
36184     },
36185
36186     animateExpand : function(){
36187         this.beforeSlide();
36188         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36189         this.el.setStyle("z-index", 20000);
36190         this.collapsedEl.hide({
36191             duration:.1
36192         });
36193         this.el.slideIn(this.getSlideAnchor(), {
36194             callback : function(){
36195                 this.el.setStyle("z-index", "");
36196                 this.afterSlide();
36197                 if(this.split){
36198                     this.split.el.show();
36199                 }
36200                 this.fireEvent("invalidated", this);
36201                 this.fireEvent("expanded", this);
36202             },
36203             scope: this,
36204             block: true
36205         });
36206     },
36207
36208     anchors : {
36209         "west" : "left",
36210         "east" : "right",
36211         "north" : "top",
36212         "south" : "bottom"
36213     },
36214
36215     sanchors : {
36216         "west" : "l",
36217         "east" : "r",
36218         "north" : "t",
36219         "south" : "b"
36220     },
36221
36222     canchors : {
36223         "west" : "tl-tr",
36224         "east" : "tr-tl",
36225         "north" : "tl-bl",
36226         "south" : "bl-tl"
36227     },
36228
36229     getAnchor : function(){
36230         return this.anchors[this.position];
36231     },
36232
36233     getCollapseAnchor : function(){
36234         return this.canchors[this.position];
36235     },
36236
36237     getSlideAnchor : function(){
36238         return this.sanchors[this.position];
36239     },
36240
36241     getAlignAdj : function(){
36242         var cm = this.cmargins;
36243         switch(this.position){
36244             case "west":
36245                 return [0, 0];
36246             break;
36247             case "east":
36248                 return [0, 0];
36249             break;
36250             case "north":
36251                 return [0, 0];
36252             break;
36253             case "south":
36254                 return [0, 0];
36255             break;
36256         }
36257     },
36258
36259     getExpandAdj : function(){
36260         var c = this.collapsedEl, cm = this.cmargins;
36261         switch(this.position){
36262             case "west":
36263                 return [-(cm.right+c.getWidth()+cm.left), 0];
36264             break;
36265             case "east":
36266                 return [cm.right+c.getWidth()+cm.left, 0];
36267             break;
36268             case "north":
36269                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36270             break;
36271             case "south":
36272                 return [0, cm.top+cm.bottom+c.getHeight()];
36273             break;
36274         }
36275     }
36276 });/*
36277  * Based on:
36278  * Ext JS Library 1.1.1
36279  * Copyright(c) 2006-2007, Ext JS, LLC.
36280  *
36281  * Originally Released Under LGPL - original licence link has changed is not relivant.
36282  *
36283  * Fork - LGPL
36284  * <script type="text/javascript">
36285  */
36286 /*
36287  * These classes are private internal classes
36288  */
36289 Roo.bootstrap.layout.Center = function(config){
36290     config.region = "center";
36291     Roo.bootstrap.layout.Region.call(this, config);
36292     this.visible = true;
36293     this.minWidth = config.minWidth || 20;
36294     this.minHeight = config.minHeight || 20;
36295 };
36296
36297 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36298     hide : function(){
36299         // center panel can't be hidden
36300     },
36301     
36302     show : function(){
36303         // center panel can't be hidden
36304     },
36305     
36306     getMinWidth: function(){
36307         return this.minWidth;
36308     },
36309     
36310     getMinHeight: function(){
36311         return this.minHeight;
36312     }
36313 });
36314
36315
36316
36317
36318  
36319
36320
36321
36322
36323
36324 Roo.bootstrap.layout.North = function(config)
36325 {
36326     config.region = 'north';
36327     config.cursor = 'n-resize';
36328     
36329     Roo.bootstrap.layout.Split.call(this, config);
36330     
36331     
36332     if(this.split){
36333         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36334         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36335         this.split.el.addClass("roo-layout-split-v");
36336     }
36337     var size = config.initialSize || config.height;
36338     if(typeof size != "undefined"){
36339         this.el.setHeight(size);
36340     }
36341 };
36342 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36343 {
36344     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36345     
36346     
36347     
36348     getBox : function(){
36349         if(this.collapsed){
36350             return this.collapsedEl.getBox();
36351         }
36352         var box = this.el.getBox();
36353         if(this.split){
36354             box.height += this.split.el.getHeight();
36355         }
36356         return box;
36357     },
36358     
36359     updateBox : function(box){
36360         if(this.split && !this.collapsed){
36361             box.height -= this.split.el.getHeight();
36362             this.split.el.setLeft(box.x);
36363             this.split.el.setTop(box.y+box.height);
36364             this.split.el.setWidth(box.width);
36365         }
36366         if(this.collapsed){
36367             this.updateBody(box.width, null);
36368         }
36369         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36370     }
36371 });
36372
36373
36374
36375
36376
36377 Roo.bootstrap.layout.South = function(config){
36378     config.region = 'south';
36379     config.cursor = 's-resize';
36380     Roo.bootstrap.layout.Split.call(this, config);
36381     if(this.split){
36382         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36383         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36384         this.split.el.addClass("roo-layout-split-v");
36385     }
36386     var size = config.initialSize || config.height;
36387     if(typeof size != "undefined"){
36388         this.el.setHeight(size);
36389     }
36390 };
36391
36392 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36393     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36394     getBox : function(){
36395         if(this.collapsed){
36396             return this.collapsedEl.getBox();
36397         }
36398         var box = this.el.getBox();
36399         if(this.split){
36400             var sh = this.split.el.getHeight();
36401             box.height += sh;
36402             box.y -= sh;
36403         }
36404         return box;
36405     },
36406     
36407     updateBox : function(box){
36408         if(this.split && !this.collapsed){
36409             var sh = this.split.el.getHeight();
36410             box.height -= sh;
36411             box.y += sh;
36412             this.split.el.setLeft(box.x);
36413             this.split.el.setTop(box.y-sh);
36414             this.split.el.setWidth(box.width);
36415         }
36416         if(this.collapsed){
36417             this.updateBody(box.width, null);
36418         }
36419         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36420     }
36421 });
36422
36423 Roo.bootstrap.layout.East = function(config){
36424     config.region = "east";
36425     config.cursor = "e-resize";
36426     Roo.bootstrap.layout.Split.call(this, config);
36427     if(this.split){
36428         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36429         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36430         this.split.el.addClass("roo-layout-split-h");
36431     }
36432     var size = config.initialSize || config.width;
36433     if(typeof size != "undefined"){
36434         this.el.setWidth(size);
36435     }
36436 };
36437 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36438     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36439     getBox : function(){
36440         if(this.collapsed){
36441             return this.collapsedEl.getBox();
36442         }
36443         var box = this.el.getBox();
36444         if(this.split){
36445             var sw = this.split.el.getWidth();
36446             box.width += sw;
36447             box.x -= sw;
36448         }
36449         return box;
36450     },
36451
36452     updateBox : function(box){
36453         if(this.split && !this.collapsed){
36454             var sw = this.split.el.getWidth();
36455             box.width -= sw;
36456             this.split.el.setLeft(box.x);
36457             this.split.el.setTop(box.y);
36458             this.split.el.setHeight(box.height);
36459             box.x += sw;
36460         }
36461         if(this.collapsed){
36462             this.updateBody(null, box.height);
36463         }
36464         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36465     }
36466 });
36467
36468 Roo.bootstrap.layout.West = function(config){
36469     config.region = "west";
36470     config.cursor = "w-resize";
36471     
36472     Roo.bootstrap.layout.Split.call(this, config);
36473     if(this.split){
36474         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36475         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36476         this.split.el.addClass("roo-layout-split-h");
36477     }
36478     
36479 };
36480 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36481     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36482     
36483     onRender: function(ctr, pos)
36484     {
36485         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36486         var size = this.config.initialSize || this.config.width;
36487         if(typeof size != "undefined"){
36488             this.el.setWidth(size);
36489         }
36490     },
36491     
36492     getBox : function(){
36493         if(this.collapsed){
36494             return this.collapsedEl.getBox();
36495         }
36496         var box = this.el.getBox();
36497         if(this.split){
36498             box.width += this.split.el.getWidth();
36499         }
36500         return box;
36501     },
36502     
36503     updateBox : function(box){
36504         if(this.split && !this.collapsed){
36505             var sw = this.split.el.getWidth();
36506             box.width -= sw;
36507             this.split.el.setLeft(box.x+box.width);
36508             this.split.el.setTop(box.y);
36509             this.split.el.setHeight(box.height);
36510         }
36511         if(this.collapsed){
36512             this.updateBody(null, box.height);
36513         }
36514         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36515     }
36516 });
36517 Roo.namespace("Roo.bootstrap.panel");/*
36518  * Based on:
36519  * Ext JS Library 1.1.1
36520  * Copyright(c) 2006-2007, Ext JS, LLC.
36521  *
36522  * Originally Released Under LGPL - original licence link has changed is not relivant.
36523  *
36524  * Fork - LGPL
36525  * <script type="text/javascript">
36526  */
36527 /**
36528  * @class Roo.ContentPanel
36529  * @extends Roo.util.Observable
36530  * A basic ContentPanel element.
36531  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36532  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36533  * @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
36534  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36535  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36536  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36537  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36538  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36539  * @cfg {String} title          The title for this panel
36540  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36541  * @cfg {String} url            Calls {@link #setUrl} with this value
36542  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36543  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36544  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36545  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36546  * @cfg {Boolean} badges render the badges
36547
36548  * @constructor
36549  * Create a new ContentPanel.
36550  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36551  * @param {String/Object} config A string to set only the title or a config object
36552  * @param {String} content (optional) Set the HTML content for this panel
36553  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36554  */
36555 Roo.bootstrap.panel.Content = function( config){
36556     
36557     this.tpl = config.tpl || false;
36558     
36559     var el = config.el;
36560     var content = config.content;
36561
36562     if(config.autoCreate){ // xtype is available if this is called from factory
36563         el = Roo.id();
36564     }
36565     this.el = Roo.get(el);
36566     if(!this.el && config && config.autoCreate){
36567         if(typeof config.autoCreate == "object"){
36568             if(!config.autoCreate.id){
36569                 config.autoCreate.id = config.id||el;
36570             }
36571             this.el = Roo.DomHelper.append(document.body,
36572                         config.autoCreate, true);
36573         }else{
36574             var elcfg =  {   tag: "div",
36575                             cls: "roo-layout-inactive-content",
36576                             id: config.id||el
36577                             };
36578             if (config.html) {
36579                 elcfg.html = config.html;
36580                 
36581             }
36582                         
36583             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36584         }
36585     } 
36586     this.closable = false;
36587     this.loaded = false;
36588     this.active = false;
36589    
36590       
36591     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36592         
36593         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36594         
36595         this.wrapEl = this.el; //this.el.wrap();
36596         var ti = [];
36597         if (config.toolbar.items) {
36598             ti = config.toolbar.items ;
36599             delete config.toolbar.items ;
36600         }
36601         
36602         var nitems = [];
36603         this.toolbar.render(this.wrapEl, 'before');
36604         for(var i =0;i < ti.length;i++) {
36605           //  Roo.log(['add child', items[i]]);
36606             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36607         }
36608         this.toolbar.items = nitems;
36609         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36610         delete config.toolbar;
36611         
36612     }
36613     /*
36614     // xtype created footer. - not sure if will work as we normally have to render first..
36615     if (this.footer && !this.footer.el && this.footer.xtype) {
36616         if (!this.wrapEl) {
36617             this.wrapEl = this.el.wrap();
36618         }
36619     
36620         this.footer.container = this.wrapEl.createChild();
36621          
36622         this.footer = Roo.factory(this.footer, Roo);
36623         
36624     }
36625     */
36626     
36627      if(typeof config == "string"){
36628         this.title = config;
36629     }else{
36630         Roo.apply(this, config);
36631     }
36632     
36633     if(this.resizeEl){
36634         this.resizeEl = Roo.get(this.resizeEl, true);
36635     }else{
36636         this.resizeEl = this.el;
36637     }
36638     // handle view.xtype
36639     
36640  
36641     
36642     
36643     this.addEvents({
36644         /**
36645          * @event activate
36646          * Fires when this panel is activated. 
36647          * @param {Roo.ContentPanel} this
36648          */
36649         "activate" : true,
36650         /**
36651          * @event deactivate
36652          * Fires when this panel is activated. 
36653          * @param {Roo.ContentPanel} this
36654          */
36655         "deactivate" : true,
36656
36657         /**
36658          * @event resize
36659          * Fires when this panel is resized if fitToFrame is true.
36660          * @param {Roo.ContentPanel} this
36661          * @param {Number} width The width after any component adjustments
36662          * @param {Number} height The height after any component adjustments
36663          */
36664         "resize" : true,
36665         
36666          /**
36667          * @event render
36668          * Fires when this tab is created
36669          * @param {Roo.ContentPanel} this
36670          */
36671         "render" : true
36672         
36673         
36674         
36675     });
36676     
36677
36678     
36679     
36680     if(this.autoScroll){
36681         this.resizeEl.setStyle("overflow", "auto");
36682     } else {
36683         // fix randome scrolling
36684         //this.el.on('scroll', function() {
36685         //    Roo.log('fix random scolling');
36686         //    this.scrollTo('top',0); 
36687         //});
36688     }
36689     content = content || this.content;
36690     if(content){
36691         this.setContent(content);
36692     }
36693     if(config && config.url){
36694         this.setUrl(this.url, this.params, this.loadOnce);
36695     }
36696     
36697     
36698     
36699     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36700     
36701     if (this.view && typeof(this.view.xtype) != 'undefined') {
36702         this.view.el = this.el.appendChild(document.createElement("div"));
36703         this.view = Roo.factory(this.view); 
36704         this.view.render  &&  this.view.render(false, '');  
36705     }
36706     
36707     
36708     this.fireEvent('render', this);
36709 };
36710
36711 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36712     
36713     tabTip : '',
36714     
36715     setRegion : function(region){
36716         this.region = region;
36717         this.setActiveClass(region && !this.background);
36718     },
36719     
36720     
36721     setActiveClass: function(state)
36722     {
36723         if(state){
36724            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36725            this.el.setStyle('position','relative');
36726         }else{
36727            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36728            this.el.setStyle('position', 'absolute');
36729         } 
36730     },
36731     
36732     /**
36733      * Returns the toolbar for this Panel if one was configured. 
36734      * @return {Roo.Toolbar} 
36735      */
36736     getToolbar : function(){
36737         return this.toolbar;
36738     },
36739     
36740     setActiveState : function(active)
36741     {
36742         this.active = active;
36743         this.setActiveClass(active);
36744         if(!active){
36745             if(this.fireEvent("deactivate", this) === false){
36746                 return false;
36747             }
36748             return true;
36749         }
36750         this.fireEvent("activate", this);
36751         return true;
36752     },
36753     /**
36754      * Updates this panel's element
36755      * @param {String} content The new content
36756      * @param {Boolean} loadScripts (optional) true to look for and process scripts
36757     */
36758     setContent : function(content, loadScripts){
36759         this.el.update(content, loadScripts);
36760     },
36761
36762     ignoreResize : function(w, h){
36763         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36764             return true;
36765         }else{
36766             this.lastSize = {width: w, height: h};
36767             return false;
36768         }
36769     },
36770     /**
36771      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36772      * @return {Roo.UpdateManager} The UpdateManager
36773      */
36774     getUpdateManager : function(){
36775         return this.el.getUpdateManager();
36776     },
36777      /**
36778      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36779      * @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:
36780 <pre><code>
36781 panel.load({
36782     url: "your-url.php",
36783     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36784     callback: yourFunction,
36785     scope: yourObject, //(optional scope)
36786     discardUrl: false,
36787     nocache: false,
36788     text: "Loading...",
36789     timeout: 30,
36790     scripts: false
36791 });
36792 </code></pre>
36793      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
36794      * 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.
36795      * @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}
36796      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
36797      * @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.
36798      * @return {Roo.ContentPanel} this
36799      */
36800     load : function(){
36801         var um = this.el.getUpdateManager();
36802         um.update.apply(um, arguments);
36803         return this;
36804     },
36805
36806
36807     /**
36808      * 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.
36809      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
36810      * @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)
36811      * @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)
36812      * @return {Roo.UpdateManager} The UpdateManager
36813      */
36814     setUrl : function(url, params, loadOnce){
36815         if(this.refreshDelegate){
36816             this.removeListener("activate", this.refreshDelegate);
36817         }
36818         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36819         this.on("activate", this.refreshDelegate);
36820         return this.el.getUpdateManager();
36821     },
36822     
36823     _handleRefresh : function(url, params, loadOnce){
36824         if(!loadOnce || !this.loaded){
36825             var updater = this.el.getUpdateManager();
36826             updater.update(url, params, this._setLoaded.createDelegate(this));
36827         }
36828     },
36829     
36830     _setLoaded : function(){
36831         this.loaded = true;
36832     }, 
36833     
36834     /**
36835      * Returns this panel's id
36836      * @return {String} 
36837      */
36838     getId : function(){
36839         return this.el.id;
36840     },
36841     
36842     /** 
36843      * Returns this panel's element - used by regiosn to add.
36844      * @return {Roo.Element} 
36845      */
36846     getEl : function(){
36847         return this.wrapEl || this.el;
36848     },
36849     
36850    
36851     
36852     adjustForComponents : function(width, height)
36853     {
36854         //Roo.log('adjustForComponents ');
36855         if(this.resizeEl != this.el){
36856             width -= this.el.getFrameWidth('lr');
36857             height -= this.el.getFrameWidth('tb');
36858         }
36859         if(this.toolbar){
36860             var te = this.toolbar.getEl();
36861             te.setWidth(width);
36862             height -= te.getHeight();
36863         }
36864         if(this.footer){
36865             var te = this.footer.getEl();
36866             te.setWidth(width);
36867             height -= te.getHeight();
36868         }
36869         
36870         
36871         if(this.adjustments){
36872             width += this.adjustments[0];
36873             height += this.adjustments[1];
36874         }
36875         return {"width": width, "height": height};
36876     },
36877     
36878     setSize : function(width, height){
36879         if(this.fitToFrame && !this.ignoreResize(width, height)){
36880             if(this.fitContainer && this.resizeEl != this.el){
36881                 this.el.setSize(width, height);
36882             }
36883             var size = this.adjustForComponents(width, height);
36884             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
36885             this.fireEvent('resize', this, size.width, size.height);
36886         }
36887     },
36888     
36889     /**
36890      * Returns this panel's title
36891      * @return {String} 
36892      */
36893     getTitle : function(){
36894         
36895         if (typeof(this.title) != 'object') {
36896             return this.title;
36897         }
36898         
36899         var t = '';
36900         for (var k in this.title) {
36901             if (!this.title.hasOwnProperty(k)) {
36902                 continue;
36903             }
36904             
36905             if (k.indexOf('-') >= 0) {
36906                 var s = k.split('-');
36907                 for (var i = 0; i<s.length; i++) {
36908                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
36909                 }
36910             } else {
36911                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
36912             }
36913         }
36914         return t;
36915     },
36916     
36917     /**
36918      * Set this panel's title
36919      * @param {String} title
36920      */
36921     setTitle : function(title){
36922         this.title = title;
36923         if(this.region){
36924             this.region.updatePanelTitle(this, title);
36925         }
36926     },
36927     
36928     /**
36929      * Returns true is this panel was configured to be closable
36930      * @return {Boolean} 
36931      */
36932     isClosable : function(){
36933         return this.closable;
36934     },
36935     
36936     beforeSlide : function(){
36937         this.el.clip();
36938         this.resizeEl.clip();
36939     },
36940     
36941     afterSlide : function(){
36942         this.el.unclip();
36943         this.resizeEl.unclip();
36944     },
36945     
36946     /**
36947      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
36948      *   Will fail silently if the {@link #setUrl} method has not been called.
36949      *   This does not activate the panel, just updates its content.
36950      */
36951     refresh : function(){
36952         if(this.refreshDelegate){
36953            this.loaded = false;
36954            this.refreshDelegate();
36955         }
36956     },
36957     
36958     /**
36959      * Destroys this panel
36960      */
36961     destroy : function(){
36962         this.el.removeAllListeners();
36963         var tempEl = document.createElement("span");
36964         tempEl.appendChild(this.el.dom);
36965         tempEl.innerHTML = "";
36966         this.el.remove();
36967         this.el = null;
36968     },
36969     
36970     /**
36971      * form - if the content panel contains a form - this is a reference to it.
36972      * @type {Roo.form.Form}
36973      */
36974     form : false,
36975     /**
36976      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
36977      *    This contains a reference to it.
36978      * @type {Roo.View}
36979      */
36980     view : false,
36981     
36982       /**
36983      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
36984      * <pre><code>
36985
36986 layout.addxtype({
36987        xtype : 'Form',
36988        items: [ .... ]
36989    }
36990 );
36991
36992 </code></pre>
36993      * @param {Object} cfg Xtype definition of item to add.
36994      */
36995     
36996     
36997     getChildContainer: function () {
36998         return this.getEl();
36999     }
37000     
37001     
37002     /*
37003         var  ret = new Roo.factory(cfg);
37004         return ret;
37005         
37006         
37007         // add form..
37008         if (cfg.xtype.match(/^Form$/)) {
37009             
37010             var el;
37011             //if (this.footer) {
37012             //    el = this.footer.container.insertSibling(false, 'before');
37013             //} else {
37014                 el = this.el.createChild();
37015             //}
37016
37017             this.form = new  Roo.form.Form(cfg);
37018             
37019             
37020             if ( this.form.allItems.length) {
37021                 this.form.render(el.dom);
37022             }
37023             return this.form;
37024         }
37025         // should only have one of theses..
37026         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37027             // views.. should not be just added - used named prop 'view''
37028             
37029             cfg.el = this.el.appendChild(document.createElement("div"));
37030             // factory?
37031             
37032             var ret = new Roo.factory(cfg);
37033              
37034              ret.render && ret.render(false, ''); // render blank..
37035             this.view = ret;
37036             return ret;
37037         }
37038         return false;
37039     }
37040     \*/
37041 });
37042  
37043 /**
37044  * @class Roo.bootstrap.panel.Grid
37045  * @extends Roo.bootstrap.panel.Content
37046  * @constructor
37047  * Create a new GridPanel.
37048  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37049  * @param {Object} config A the config object
37050   
37051  */
37052
37053
37054
37055 Roo.bootstrap.panel.Grid = function(config)
37056 {
37057     
37058       
37059     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37060         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37061
37062     config.el = this.wrapper;
37063     //this.el = this.wrapper;
37064     
37065       if (config.container) {
37066         // ctor'ed from a Border/panel.grid
37067         
37068         
37069         this.wrapper.setStyle("overflow", "hidden");
37070         this.wrapper.addClass('roo-grid-container');
37071
37072     }
37073     
37074     
37075     if(config.toolbar){
37076         var tool_el = this.wrapper.createChild();    
37077         this.toolbar = Roo.factory(config.toolbar);
37078         var ti = [];
37079         if (config.toolbar.items) {
37080             ti = config.toolbar.items ;
37081             delete config.toolbar.items ;
37082         }
37083         
37084         var nitems = [];
37085         this.toolbar.render(tool_el);
37086         for(var i =0;i < ti.length;i++) {
37087           //  Roo.log(['add child', items[i]]);
37088             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37089         }
37090         this.toolbar.items = nitems;
37091         
37092         delete config.toolbar;
37093     }
37094     
37095     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37096     config.grid.scrollBody = true;;
37097     config.grid.monitorWindowResize = false; // turn off autosizing
37098     config.grid.autoHeight = false;
37099     config.grid.autoWidth = false;
37100     
37101     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37102     
37103     if (config.background) {
37104         // render grid on panel activation (if panel background)
37105         this.on('activate', function(gp) {
37106             if (!gp.grid.rendered) {
37107                 gp.grid.render(this.wrapper);
37108                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37109             }
37110         });
37111             
37112     } else {
37113         this.grid.render(this.wrapper);
37114         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37115
37116     }
37117     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37118     // ??? needed ??? config.el = this.wrapper;
37119     
37120     
37121     
37122   
37123     // xtype created footer. - not sure if will work as we normally have to render first..
37124     if (this.footer && !this.footer.el && this.footer.xtype) {
37125         
37126         var ctr = this.grid.getView().getFooterPanel(true);
37127         this.footer.dataSource = this.grid.dataSource;
37128         this.footer = Roo.factory(this.footer, Roo);
37129         this.footer.render(ctr);
37130         
37131     }
37132     
37133     
37134     
37135     
37136      
37137 };
37138
37139 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37140     getId : function(){
37141         return this.grid.id;
37142     },
37143     
37144     /**
37145      * Returns the grid for this panel
37146      * @return {Roo.bootstrap.Table} 
37147      */
37148     getGrid : function(){
37149         return this.grid;    
37150     },
37151     
37152     setSize : function(width, height){
37153         if(!this.ignoreResize(width, height)){
37154             var grid = this.grid;
37155             var size = this.adjustForComponents(width, height);
37156             var gridel = grid.getGridEl();
37157             gridel.setSize(size.width, size.height);
37158             /*
37159             var thd = grid.getGridEl().select('thead',true).first();
37160             var tbd = grid.getGridEl().select('tbody', true).first();
37161             if (tbd) {
37162                 tbd.setSize(width, height - thd.getHeight());
37163             }
37164             */
37165             grid.autoSize();
37166         }
37167     },
37168      
37169     
37170     
37171     beforeSlide : function(){
37172         this.grid.getView().scroller.clip();
37173     },
37174     
37175     afterSlide : function(){
37176         this.grid.getView().scroller.unclip();
37177     },
37178     
37179     destroy : function(){
37180         this.grid.destroy();
37181         delete this.grid;
37182         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37183     }
37184 });
37185
37186 /**
37187  * @class Roo.bootstrap.panel.Nest
37188  * @extends Roo.bootstrap.panel.Content
37189  * @constructor
37190  * Create a new Panel, that can contain a layout.Border.
37191  * 
37192  * 
37193  * @param {Roo.BorderLayout} layout The layout for this panel
37194  * @param {String/Object} config A string to set only the title or a config object
37195  */
37196 Roo.bootstrap.panel.Nest = function(config)
37197 {
37198     // construct with only one argument..
37199     /* FIXME - implement nicer consturctors
37200     if (layout.layout) {
37201         config = layout;
37202         layout = config.layout;
37203         delete config.layout;
37204     }
37205     if (layout.xtype && !layout.getEl) {
37206         // then layout needs constructing..
37207         layout = Roo.factory(layout, Roo);
37208     }
37209     */
37210     
37211     config.el =  config.layout.getEl();
37212     
37213     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37214     
37215     config.layout.monitorWindowResize = false; // turn off autosizing
37216     this.layout = config.layout;
37217     this.layout.getEl().addClass("roo-layout-nested-layout");
37218     
37219     
37220     
37221     
37222 };
37223
37224 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37225
37226     setSize : function(width, height){
37227         if(!this.ignoreResize(width, height)){
37228             var size = this.adjustForComponents(width, height);
37229             var el = this.layout.getEl();
37230             if (size.height < 1) {
37231                 el.setWidth(size.width);   
37232             } else {
37233                 el.setSize(size.width, size.height);
37234             }
37235             var touch = el.dom.offsetWidth;
37236             this.layout.layout();
37237             // ie requires a double layout on the first pass
37238             if(Roo.isIE && !this.initialized){
37239                 this.initialized = true;
37240                 this.layout.layout();
37241             }
37242         }
37243     },
37244     
37245     // activate all subpanels if not currently active..
37246     
37247     setActiveState : function(active){
37248         this.active = active;
37249         this.setActiveClass(active);
37250         
37251         if(!active){
37252             this.fireEvent("deactivate", this);
37253             return;
37254         }
37255         
37256         this.fireEvent("activate", this);
37257         // not sure if this should happen before or after..
37258         if (!this.layout) {
37259             return; // should not happen..
37260         }
37261         var reg = false;
37262         for (var r in this.layout.regions) {
37263             reg = this.layout.getRegion(r);
37264             if (reg.getActivePanel()) {
37265                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37266                 reg.setActivePanel(reg.getActivePanel());
37267                 continue;
37268             }
37269             if (!reg.panels.length) {
37270                 continue;
37271             }
37272             reg.showPanel(reg.getPanel(0));
37273         }
37274         
37275         
37276         
37277         
37278     },
37279     
37280     /**
37281      * Returns the nested BorderLayout for this panel
37282      * @return {Roo.BorderLayout} 
37283      */
37284     getLayout : function(){
37285         return this.layout;
37286     },
37287     
37288      /**
37289      * Adds a xtype elements to the layout of the nested panel
37290      * <pre><code>
37291
37292 panel.addxtype({
37293        xtype : 'ContentPanel',
37294        region: 'west',
37295        items: [ .... ]
37296    }
37297 );
37298
37299 panel.addxtype({
37300         xtype : 'NestedLayoutPanel',
37301         region: 'west',
37302         layout: {
37303            center: { },
37304            west: { }   
37305         },
37306         items : [ ... list of content panels or nested layout panels.. ]
37307    }
37308 );
37309 </code></pre>
37310      * @param {Object} cfg Xtype definition of item to add.
37311      */
37312     addxtype : function(cfg) {
37313         return this.layout.addxtype(cfg);
37314     
37315     }
37316 });        /*
37317  * Based on:
37318  * Ext JS Library 1.1.1
37319  * Copyright(c) 2006-2007, Ext JS, LLC.
37320  *
37321  * Originally Released Under LGPL - original licence link has changed is not relivant.
37322  *
37323  * Fork - LGPL
37324  * <script type="text/javascript">
37325  */
37326 /**
37327  * @class Roo.TabPanel
37328  * @extends Roo.util.Observable
37329  * A lightweight tab container.
37330  * <br><br>
37331  * Usage:
37332  * <pre><code>
37333 // basic tabs 1, built from existing content
37334 var tabs = new Roo.TabPanel("tabs1");
37335 tabs.addTab("script", "View Script");
37336 tabs.addTab("markup", "View Markup");
37337 tabs.activate("script");
37338
37339 // more advanced tabs, built from javascript
37340 var jtabs = new Roo.TabPanel("jtabs");
37341 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37342
37343 // set up the UpdateManager
37344 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37345 var updater = tab2.getUpdateManager();
37346 updater.setDefaultUrl("ajax1.htm");
37347 tab2.on('activate', updater.refresh, updater, true);
37348
37349 // Use setUrl for Ajax loading
37350 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37351 tab3.setUrl("ajax2.htm", null, true);
37352
37353 // Disabled tab
37354 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37355 tab4.disable();
37356
37357 jtabs.activate("jtabs-1");
37358  * </code></pre>
37359  * @constructor
37360  * Create a new TabPanel.
37361  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37362  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37363  */
37364 Roo.bootstrap.panel.Tabs = function(config){
37365     /**
37366     * The container element for this TabPanel.
37367     * @type Roo.Element
37368     */
37369     this.el = Roo.get(config.el);
37370     delete config.el;
37371     if(config){
37372         if(typeof config == "boolean"){
37373             this.tabPosition = config ? "bottom" : "top";
37374         }else{
37375             Roo.apply(this, config);
37376         }
37377     }
37378     
37379     if(this.tabPosition == "bottom"){
37380         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37381         this.el.addClass("roo-tabs-bottom");
37382     }
37383     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37384     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37385     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37386     if(Roo.isIE){
37387         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37388     }
37389     if(this.tabPosition != "bottom"){
37390         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37391          * @type Roo.Element
37392          */
37393         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37394         this.el.addClass("roo-tabs-top");
37395     }
37396     this.items = [];
37397
37398     this.bodyEl.setStyle("position", "relative");
37399
37400     this.active = null;
37401     this.activateDelegate = this.activate.createDelegate(this);
37402
37403     this.addEvents({
37404         /**
37405          * @event tabchange
37406          * Fires when the active tab changes
37407          * @param {Roo.TabPanel} this
37408          * @param {Roo.TabPanelItem} activePanel The new active tab
37409          */
37410         "tabchange": true,
37411         /**
37412          * @event beforetabchange
37413          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37414          * @param {Roo.TabPanel} this
37415          * @param {Object} e Set cancel to true on this object to cancel the tab change
37416          * @param {Roo.TabPanelItem} tab The tab being changed to
37417          */
37418         "beforetabchange" : true
37419     });
37420
37421     Roo.EventManager.onWindowResize(this.onResize, this);
37422     this.cpad = this.el.getPadding("lr");
37423     this.hiddenCount = 0;
37424
37425
37426     // toolbar on the tabbar support...
37427     if (this.toolbar) {
37428         alert("no toolbar support yet");
37429         this.toolbar  = false;
37430         /*
37431         var tcfg = this.toolbar;
37432         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37433         this.toolbar = new Roo.Toolbar(tcfg);
37434         if (Roo.isSafari) {
37435             var tbl = tcfg.container.child('table', true);
37436             tbl.setAttribute('width', '100%');
37437         }
37438         */
37439         
37440     }
37441    
37442
37443
37444     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37445 };
37446
37447 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37448     /*
37449      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37450      */
37451     tabPosition : "top",
37452     /*
37453      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37454      */
37455     currentTabWidth : 0,
37456     /*
37457      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37458      */
37459     minTabWidth : 40,
37460     /*
37461      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37462      */
37463     maxTabWidth : 250,
37464     /*
37465      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37466      */
37467     preferredTabWidth : 175,
37468     /*
37469      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37470      */
37471     resizeTabs : false,
37472     /*
37473      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37474      */
37475     monitorResize : true,
37476     /*
37477      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37478      */
37479     toolbar : false,
37480
37481     /**
37482      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37483      * @param {String} id The id of the div to use <b>or create</b>
37484      * @param {String} text The text for the tab
37485      * @param {String} content (optional) Content to put in the TabPanelItem body
37486      * @param {Boolean} closable (optional) True to create a close icon on the tab
37487      * @return {Roo.TabPanelItem} The created TabPanelItem
37488      */
37489     addTab : function(id, text, content, closable, tpl)
37490     {
37491         var item = new Roo.bootstrap.panel.TabItem({
37492             panel: this,
37493             id : id,
37494             text : text,
37495             closable : closable,
37496             tpl : tpl
37497         });
37498         this.addTabItem(item);
37499         if(content){
37500             item.setContent(content);
37501         }
37502         return item;
37503     },
37504
37505     /**
37506      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37507      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37508      * @return {Roo.TabPanelItem}
37509      */
37510     getTab : function(id){
37511         return this.items[id];
37512     },
37513
37514     /**
37515      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37516      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37517      */
37518     hideTab : function(id){
37519         var t = this.items[id];
37520         if(!t.isHidden()){
37521            t.setHidden(true);
37522            this.hiddenCount++;
37523            this.autoSizeTabs();
37524         }
37525     },
37526
37527     /**
37528      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37529      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37530      */
37531     unhideTab : function(id){
37532         var t = this.items[id];
37533         if(t.isHidden()){
37534            t.setHidden(false);
37535            this.hiddenCount--;
37536            this.autoSizeTabs();
37537         }
37538     },
37539
37540     /**
37541      * Adds an existing {@link Roo.TabPanelItem}.
37542      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37543      */
37544     addTabItem : function(item){
37545         this.items[item.id] = item;
37546         this.items.push(item);
37547       //  if(this.resizeTabs){
37548     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37549   //         this.autoSizeTabs();
37550 //        }else{
37551 //            item.autoSize();
37552        // }
37553     },
37554
37555     /**
37556      * Removes a {@link Roo.TabPanelItem}.
37557      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37558      */
37559     removeTab : function(id){
37560         var items = this.items;
37561         var tab = items[id];
37562         if(!tab) { return; }
37563         var index = items.indexOf(tab);
37564         if(this.active == tab && items.length > 1){
37565             var newTab = this.getNextAvailable(index);
37566             if(newTab) {
37567                 newTab.activate();
37568             }
37569         }
37570         this.stripEl.dom.removeChild(tab.pnode.dom);
37571         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37572             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37573         }
37574         items.splice(index, 1);
37575         delete this.items[tab.id];
37576         tab.fireEvent("close", tab);
37577         tab.purgeListeners();
37578         this.autoSizeTabs();
37579     },
37580
37581     getNextAvailable : function(start){
37582         var items = this.items;
37583         var index = start;
37584         // look for a next tab that will slide over to
37585         // replace the one being removed
37586         while(index < items.length){
37587             var item = items[++index];
37588             if(item && !item.isHidden()){
37589                 return item;
37590             }
37591         }
37592         // if one isn't found select the previous tab (on the left)
37593         index = start;
37594         while(index >= 0){
37595             var item = items[--index];
37596             if(item && !item.isHidden()){
37597                 return item;
37598             }
37599         }
37600         return null;
37601     },
37602
37603     /**
37604      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37605      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37606      */
37607     disableTab : function(id){
37608         var tab = this.items[id];
37609         if(tab && this.active != tab){
37610             tab.disable();
37611         }
37612     },
37613
37614     /**
37615      * Enables a {@link Roo.TabPanelItem} that is disabled.
37616      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37617      */
37618     enableTab : function(id){
37619         var tab = this.items[id];
37620         tab.enable();
37621     },
37622
37623     /**
37624      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37625      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37626      * @return {Roo.TabPanelItem} The TabPanelItem.
37627      */
37628     activate : function(id){
37629         var tab = this.items[id];
37630         if(!tab){
37631             return null;
37632         }
37633         if(tab == this.active || tab.disabled){
37634             return tab;
37635         }
37636         var e = {};
37637         this.fireEvent("beforetabchange", this, e, tab);
37638         if(e.cancel !== true && !tab.disabled){
37639             if(this.active){
37640                 this.active.hide();
37641             }
37642             this.active = this.items[id];
37643             this.active.show();
37644             this.fireEvent("tabchange", this, this.active);
37645         }
37646         return tab;
37647     },
37648
37649     /**
37650      * Gets the active {@link Roo.TabPanelItem}.
37651      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37652      */
37653     getActiveTab : function(){
37654         return this.active;
37655     },
37656
37657     /**
37658      * Updates the tab body element to fit the height of the container element
37659      * for overflow scrolling
37660      * @param {Number} targetHeight (optional) Override the starting height from the elements height
37661      */
37662     syncHeight : function(targetHeight){
37663         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37664         var bm = this.bodyEl.getMargins();
37665         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37666         this.bodyEl.setHeight(newHeight);
37667         return newHeight;
37668     },
37669
37670     onResize : function(){
37671         if(this.monitorResize){
37672             this.autoSizeTabs();
37673         }
37674     },
37675
37676     /**
37677      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37678      */
37679     beginUpdate : function(){
37680         this.updating = true;
37681     },
37682
37683     /**
37684      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37685      */
37686     endUpdate : function(){
37687         this.updating = false;
37688         this.autoSizeTabs();
37689     },
37690
37691     /**
37692      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37693      */
37694     autoSizeTabs : function(){
37695         var count = this.items.length;
37696         var vcount = count - this.hiddenCount;
37697         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37698             return;
37699         }
37700         var w = Math.max(this.el.getWidth() - this.cpad, 10);
37701         var availWidth = Math.floor(w / vcount);
37702         var b = this.stripBody;
37703         if(b.getWidth() > w){
37704             var tabs = this.items;
37705             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37706             if(availWidth < this.minTabWidth){
37707                 /*if(!this.sleft){    // incomplete scrolling code
37708                     this.createScrollButtons();
37709                 }
37710                 this.showScroll();
37711                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37712             }
37713         }else{
37714             if(this.currentTabWidth < this.preferredTabWidth){
37715                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37716             }
37717         }
37718     },
37719
37720     /**
37721      * Returns the number of tabs in this TabPanel.
37722      * @return {Number}
37723      */
37724      getCount : function(){
37725          return this.items.length;
37726      },
37727
37728     /**
37729      * Resizes all the tabs to the passed width
37730      * @param {Number} The new width
37731      */
37732     setTabWidth : function(width){
37733         this.currentTabWidth = width;
37734         for(var i = 0, len = this.items.length; i < len; i++) {
37735                 if(!this.items[i].isHidden()) {
37736                 this.items[i].setWidth(width);
37737             }
37738         }
37739     },
37740
37741     /**
37742      * Destroys this TabPanel
37743      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37744      */
37745     destroy : function(removeEl){
37746         Roo.EventManager.removeResizeListener(this.onResize, this);
37747         for(var i = 0, len = this.items.length; i < len; i++){
37748             this.items[i].purgeListeners();
37749         }
37750         if(removeEl === true){
37751             this.el.update("");
37752             this.el.remove();
37753         }
37754     },
37755     
37756     createStrip : function(container)
37757     {
37758         var strip = document.createElement("nav");
37759         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37760         container.appendChild(strip);
37761         return strip;
37762     },
37763     
37764     createStripList : function(strip)
37765     {
37766         // div wrapper for retard IE
37767         // returns the "tr" element.
37768         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
37769         //'<div class="x-tabs-strip-wrap">'+
37770           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
37771           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
37772         return strip.firstChild; //.firstChild.firstChild.firstChild;
37773     },
37774     createBody : function(container)
37775     {
37776         var body = document.createElement("div");
37777         Roo.id(body, "tab-body");
37778         //Roo.fly(body).addClass("x-tabs-body");
37779         Roo.fly(body).addClass("tab-content");
37780         container.appendChild(body);
37781         return body;
37782     },
37783     createItemBody :function(bodyEl, id){
37784         var body = Roo.getDom(id);
37785         if(!body){
37786             body = document.createElement("div");
37787             body.id = id;
37788         }
37789         //Roo.fly(body).addClass("x-tabs-item-body");
37790         Roo.fly(body).addClass("tab-pane");
37791          bodyEl.insertBefore(body, bodyEl.firstChild);
37792         return body;
37793     },
37794     /** @private */
37795     createStripElements :  function(stripEl, text, closable, tpl)
37796     {
37797         var td = document.createElement("li"); // was td..
37798         
37799         
37800         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
37801         
37802         
37803         stripEl.appendChild(td);
37804         /*if(closable){
37805             td.className = "x-tabs-closable";
37806             if(!this.closeTpl){
37807                 this.closeTpl = new Roo.Template(
37808                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37809                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
37810                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
37811                 );
37812             }
37813             var el = this.closeTpl.overwrite(td, {"text": text});
37814             var close = el.getElementsByTagName("div")[0];
37815             var inner = el.getElementsByTagName("em")[0];
37816             return {"el": el, "close": close, "inner": inner};
37817         } else {
37818         */
37819         // not sure what this is..
37820 //            if(!this.tabTpl){
37821                 //this.tabTpl = new Roo.Template(
37822                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37823                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
37824                 //);
37825 //                this.tabTpl = new Roo.Template(
37826 //                   '<a href="#">' +
37827 //                   '<span unselectable="on"' +
37828 //                            (this.disableTooltips ? '' : ' title="{text}"') +
37829 //                            ' >{text}</span></a>'
37830 //                );
37831 //                
37832 //            }
37833
37834
37835             var template = tpl || this.tabTpl || false;
37836             
37837             if(!template){
37838                 
37839                 template = new Roo.Template(
37840                    '<a href="#">' +
37841                    '<span unselectable="on"' +
37842                             (this.disableTooltips ? '' : ' title="{text}"') +
37843                             ' >{text}</span></a>'
37844                 );
37845             }
37846             
37847             switch (typeof(template)) {
37848                 case 'object' :
37849                     break;
37850                 case 'string' :
37851                     template = new Roo.Template(template);
37852                     break;
37853                 default :
37854                     break;
37855             }
37856             
37857             var el = template.overwrite(td, {"text": text});
37858             
37859             var inner = el.getElementsByTagName("span")[0];
37860             
37861             return {"el": el, "inner": inner};
37862             
37863     }
37864         
37865     
37866 });
37867
37868 /**
37869  * @class Roo.TabPanelItem
37870  * @extends Roo.util.Observable
37871  * Represents an individual item (tab plus body) in a TabPanel.
37872  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
37873  * @param {String} id The id of this TabPanelItem
37874  * @param {String} text The text for the tab of this TabPanelItem
37875  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
37876  */
37877 Roo.bootstrap.panel.TabItem = function(config){
37878     /**
37879      * The {@link Roo.TabPanel} this TabPanelItem belongs to
37880      * @type Roo.TabPanel
37881      */
37882     this.tabPanel = config.panel;
37883     /**
37884      * The id for this TabPanelItem
37885      * @type String
37886      */
37887     this.id = config.id;
37888     /** @private */
37889     this.disabled = false;
37890     /** @private */
37891     this.text = config.text;
37892     /** @private */
37893     this.loaded = false;
37894     this.closable = config.closable;
37895
37896     /**
37897      * The body element for this TabPanelItem.
37898      * @type Roo.Element
37899      */
37900     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
37901     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
37902     this.bodyEl.setStyle("display", "block");
37903     this.bodyEl.setStyle("zoom", "1");
37904     //this.hideAction();
37905
37906     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
37907     /** @private */
37908     this.el = Roo.get(els.el);
37909     this.inner = Roo.get(els.inner, true);
37910     this.textEl = Roo.get(this.el.dom.firstChild, true);
37911     this.pnode = Roo.get(els.el.parentNode, true);
37912 //    this.el.on("mousedown", this.onTabMouseDown, this);
37913     this.el.on("click", this.onTabClick, this);
37914     /** @private */
37915     if(config.closable){
37916         var c = Roo.get(els.close, true);
37917         c.dom.title = this.closeText;
37918         c.addClassOnOver("close-over");
37919         c.on("click", this.closeClick, this);
37920      }
37921
37922     this.addEvents({
37923          /**
37924          * @event activate
37925          * Fires when this tab becomes the active tab.
37926          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37927          * @param {Roo.TabPanelItem} this
37928          */
37929         "activate": true,
37930         /**
37931          * @event beforeclose
37932          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
37933          * @param {Roo.TabPanelItem} this
37934          * @param {Object} e Set cancel to true on this object to cancel the close.
37935          */
37936         "beforeclose": true,
37937         /**
37938          * @event close
37939          * Fires when this tab is closed.
37940          * @param {Roo.TabPanelItem} this
37941          */
37942          "close": true,
37943         /**
37944          * @event deactivate
37945          * Fires when this tab is no longer the active tab.
37946          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37947          * @param {Roo.TabPanelItem} this
37948          */
37949          "deactivate" : true
37950     });
37951     this.hidden = false;
37952
37953     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
37954 };
37955
37956 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
37957            {
37958     purgeListeners : function(){
37959        Roo.util.Observable.prototype.purgeListeners.call(this);
37960        this.el.removeAllListeners();
37961     },
37962     /**
37963      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
37964      */
37965     show : function(){
37966         this.pnode.addClass("active");
37967         this.showAction();
37968         if(Roo.isOpera){
37969             this.tabPanel.stripWrap.repaint();
37970         }
37971         this.fireEvent("activate", this.tabPanel, this);
37972     },
37973
37974     /**
37975      * Returns true if this tab is the active tab.
37976      * @return {Boolean}
37977      */
37978     isActive : function(){
37979         return this.tabPanel.getActiveTab() == this;
37980     },
37981
37982     /**
37983      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
37984      */
37985     hide : function(){
37986         this.pnode.removeClass("active");
37987         this.hideAction();
37988         this.fireEvent("deactivate", this.tabPanel, this);
37989     },
37990
37991     hideAction : function(){
37992         this.bodyEl.hide();
37993         this.bodyEl.setStyle("position", "absolute");
37994         this.bodyEl.setLeft("-20000px");
37995         this.bodyEl.setTop("-20000px");
37996     },
37997
37998     showAction : function(){
37999         this.bodyEl.setStyle("position", "relative");
38000         this.bodyEl.setTop("");
38001         this.bodyEl.setLeft("");
38002         this.bodyEl.show();
38003     },
38004
38005     /**
38006      * Set the tooltip for the tab.
38007      * @param {String} tooltip The tab's tooltip
38008      */
38009     setTooltip : function(text){
38010         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38011             this.textEl.dom.qtip = text;
38012             this.textEl.dom.removeAttribute('title');
38013         }else{
38014             this.textEl.dom.title = text;
38015         }
38016     },
38017
38018     onTabClick : function(e){
38019         e.preventDefault();
38020         this.tabPanel.activate(this.id);
38021     },
38022
38023     onTabMouseDown : function(e){
38024         e.preventDefault();
38025         this.tabPanel.activate(this.id);
38026     },
38027 /*
38028     getWidth : function(){
38029         return this.inner.getWidth();
38030     },
38031
38032     setWidth : function(width){
38033         var iwidth = width - this.pnode.getPadding("lr");
38034         this.inner.setWidth(iwidth);
38035         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38036         this.pnode.setWidth(width);
38037     },
38038 */
38039     /**
38040      * Show or hide the tab
38041      * @param {Boolean} hidden True to hide or false to show.
38042      */
38043     setHidden : function(hidden){
38044         this.hidden = hidden;
38045         this.pnode.setStyle("display", hidden ? "none" : "");
38046     },
38047
38048     /**
38049      * Returns true if this tab is "hidden"
38050      * @return {Boolean}
38051      */
38052     isHidden : function(){
38053         return this.hidden;
38054     },
38055
38056     /**
38057      * Returns the text for this tab
38058      * @return {String}
38059      */
38060     getText : function(){
38061         return this.text;
38062     },
38063     /*
38064     autoSize : function(){
38065         //this.el.beginMeasure();
38066         this.textEl.setWidth(1);
38067         /*
38068          *  #2804 [new] Tabs in Roojs
38069          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38070          */
38071         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38072         //this.el.endMeasure();
38073     //},
38074
38075     /**
38076      * Sets the text for the tab (Note: this also sets the tooltip text)
38077      * @param {String} text The tab's text and tooltip
38078      */
38079     setText : function(text){
38080         this.text = text;
38081         this.textEl.update(text);
38082         this.setTooltip(text);
38083         //if(!this.tabPanel.resizeTabs){
38084         //    this.autoSize();
38085         //}
38086     },
38087     /**
38088      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38089      */
38090     activate : function(){
38091         this.tabPanel.activate(this.id);
38092     },
38093
38094     /**
38095      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38096      */
38097     disable : function(){
38098         if(this.tabPanel.active != this){
38099             this.disabled = true;
38100             this.pnode.addClass("disabled");
38101         }
38102     },
38103
38104     /**
38105      * Enables this TabPanelItem if it was previously disabled.
38106      */
38107     enable : function(){
38108         this.disabled = false;
38109         this.pnode.removeClass("disabled");
38110     },
38111
38112     /**
38113      * Sets the content for this TabPanelItem.
38114      * @param {String} content The content
38115      * @param {Boolean} loadScripts true to look for and load scripts
38116      */
38117     setContent : function(content, loadScripts){
38118         this.bodyEl.update(content, loadScripts);
38119     },
38120
38121     /**
38122      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38123      * @return {Roo.UpdateManager} The UpdateManager
38124      */
38125     getUpdateManager : function(){
38126         return this.bodyEl.getUpdateManager();
38127     },
38128
38129     /**
38130      * Set a URL to be used to load the content for this TabPanelItem.
38131      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38132      * @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)
38133      * @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)
38134      * @return {Roo.UpdateManager} The UpdateManager
38135      */
38136     setUrl : function(url, params, loadOnce){
38137         if(this.refreshDelegate){
38138             this.un('activate', this.refreshDelegate);
38139         }
38140         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38141         this.on("activate", this.refreshDelegate);
38142         return this.bodyEl.getUpdateManager();
38143     },
38144
38145     /** @private */
38146     _handleRefresh : function(url, params, loadOnce){
38147         if(!loadOnce || !this.loaded){
38148             var updater = this.bodyEl.getUpdateManager();
38149             updater.update(url, params, this._setLoaded.createDelegate(this));
38150         }
38151     },
38152
38153     /**
38154      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38155      *   Will fail silently if the setUrl method has not been called.
38156      *   This does not activate the panel, just updates its content.
38157      */
38158     refresh : function(){
38159         if(this.refreshDelegate){
38160            this.loaded = false;
38161            this.refreshDelegate();
38162         }
38163     },
38164
38165     /** @private */
38166     _setLoaded : function(){
38167         this.loaded = true;
38168     },
38169
38170     /** @private */
38171     closeClick : function(e){
38172         var o = {};
38173         e.stopEvent();
38174         this.fireEvent("beforeclose", this, o);
38175         if(o.cancel !== true){
38176             this.tabPanel.removeTab(this.id);
38177         }
38178     },
38179     /**
38180      * The text displayed in the tooltip for the close icon.
38181      * @type String
38182      */
38183     closeText : "Close this tab"
38184 });
38185 /**
38186 *    This script refer to:
38187 *    Title: International Telephone Input
38188 *    Author: Jack O'Connor
38189 *    Code version:  v12.1.12
38190 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38191 **/
38192
38193 Roo.bootstrap.PhoneInputData = function() {
38194     var d = [
38195       [
38196         "Afghanistan (‫افغانستان‬‎)",
38197         "af",
38198         "93"
38199       ],
38200       [
38201         "Albania (Shqipëri)",
38202         "al",
38203         "355"
38204       ],
38205       [
38206         "Algeria (‫الجزائر‬‎)",
38207         "dz",
38208         "213"
38209       ],
38210       [
38211         "American Samoa",
38212         "as",
38213         "1684"
38214       ],
38215       [
38216         "Andorra",
38217         "ad",
38218         "376"
38219       ],
38220       [
38221         "Angola",
38222         "ao",
38223         "244"
38224       ],
38225       [
38226         "Anguilla",
38227         "ai",
38228         "1264"
38229       ],
38230       [
38231         "Antigua and Barbuda",
38232         "ag",
38233         "1268"
38234       ],
38235       [
38236         "Argentina",
38237         "ar",
38238         "54"
38239       ],
38240       [
38241         "Armenia (Հայաստան)",
38242         "am",
38243         "374"
38244       ],
38245       [
38246         "Aruba",
38247         "aw",
38248         "297"
38249       ],
38250       [
38251         "Australia",
38252         "au",
38253         "61",
38254         0
38255       ],
38256       [
38257         "Austria (Österreich)",
38258         "at",
38259         "43"
38260       ],
38261       [
38262         "Azerbaijan (Azərbaycan)",
38263         "az",
38264         "994"
38265       ],
38266       [
38267         "Bahamas",
38268         "bs",
38269         "1242"
38270       ],
38271       [
38272         "Bahrain (‫البحرين‬‎)",
38273         "bh",
38274         "973"
38275       ],
38276       [
38277         "Bangladesh (বাংলাদেশ)",
38278         "bd",
38279         "880"
38280       ],
38281       [
38282         "Barbados",
38283         "bb",
38284         "1246"
38285       ],
38286       [
38287         "Belarus (Беларусь)",
38288         "by",
38289         "375"
38290       ],
38291       [
38292         "Belgium (België)",
38293         "be",
38294         "32"
38295       ],
38296       [
38297         "Belize",
38298         "bz",
38299         "501"
38300       ],
38301       [
38302         "Benin (Bénin)",
38303         "bj",
38304         "229"
38305       ],
38306       [
38307         "Bermuda",
38308         "bm",
38309         "1441"
38310       ],
38311       [
38312         "Bhutan (འབྲུག)",
38313         "bt",
38314         "975"
38315       ],
38316       [
38317         "Bolivia",
38318         "bo",
38319         "591"
38320       ],
38321       [
38322         "Bosnia and Herzegovina (Босна и Херцеговина)",
38323         "ba",
38324         "387"
38325       ],
38326       [
38327         "Botswana",
38328         "bw",
38329         "267"
38330       ],
38331       [
38332         "Brazil (Brasil)",
38333         "br",
38334         "55"
38335       ],
38336       [
38337         "British Indian Ocean Territory",
38338         "io",
38339         "246"
38340       ],
38341       [
38342         "British Virgin Islands",
38343         "vg",
38344         "1284"
38345       ],
38346       [
38347         "Brunei",
38348         "bn",
38349         "673"
38350       ],
38351       [
38352         "Bulgaria (България)",
38353         "bg",
38354         "359"
38355       ],
38356       [
38357         "Burkina Faso",
38358         "bf",
38359         "226"
38360       ],
38361       [
38362         "Burundi (Uburundi)",
38363         "bi",
38364         "257"
38365       ],
38366       [
38367         "Cambodia (កម្ពុជា)",
38368         "kh",
38369         "855"
38370       ],
38371       [
38372         "Cameroon (Cameroun)",
38373         "cm",
38374         "237"
38375       ],
38376       [
38377         "Canada",
38378         "ca",
38379         "1",
38380         1,
38381         ["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"]
38382       ],
38383       [
38384         "Cape Verde (Kabu Verdi)",
38385         "cv",
38386         "238"
38387       ],
38388       [
38389         "Caribbean Netherlands",
38390         "bq",
38391         "599",
38392         1
38393       ],
38394       [
38395         "Cayman Islands",
38396         "ky",
38397         "1345"
38398       ],
38399       [
38400         "Central African Republic (République centrafricaine)",
38401         "cf",
38402         "236"
38403       ],
38404       [
38405         "Chad (Tchad)",
38406         "td",
38407         "235"
38408       ],
38409       [
38410         "Chile",
38411         "cl",
38412         "56"
38413       ],
38414       [
38415         "China (中国)",
38416         "cn",
38417         "86"
38418       ],
38419       [
38420         "Christmas Island",
38421         "cx",
38422         "61",
38423         2
38424       ],
38425       [
38426         "Cocos (Keeling) Islands",
38427         "cc",
38428         "61",
38429         1
38430       ],
38431       [
38432         "Colombia",
38433         "co",
38434         "57"
38435       ],
38436       [
38437         "Comoros (‫جزر القمر‬‎)",
38438         "km",
38439         "269"
38440       ],
38441       [
38442         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38443         "cd",
38444         "243"
38445       ],
38446       [
38447         "Congo (Republic) (Congo-Brazzaville)",
38448         "cg",
38449         "242"
38450       ],
38451       [
38452         "Cook Islands",
38453         "ck",
38454         "682"
38455       ],
38456       [
38457         "Costa Rica",
38458         "cr",
38459         "506"
38460       ],
38461       [
38462         "Côte d’Ivoire",
38463         "ci",
38464         "225"
38465       ],
38466       [
38467         "Croatia (Hrvatska)",
38468         "hr",
38469         "385"
38470       ],
38471       [
38472         "Cuba",
38473         "cu",
38474         "53"
38475       ],
38476       [
38477         "Curaçao",
38478         "cw",
38479         "599",
38480         0
38481       ],
38482       [
38483         "Cyprus (Κύπρος)",
38484         "cy",
38485         "357"
38486       ],
38487       [
38488         "Czech Republic (Česká republika)",
38489         "cz",
38490         "420"
38491       ],
38492       [
38493         "Denmark (Danmark)",
38494         "dk",
38495         "45"
38496       ],
38497       [
38498         "Djibouti",
38499         "dj",
38500         "253"
38501       ],
38502       [
38503         "Dominica",
38504         "dm",
38505         "1767"
38506       ],
38507       [
38508         "Dominican Republic (República Dominicana)",
38509         "do",
38510         "1",
38511         2,
38512         ["809", "829", "849"]
38513       ],
38514       [
38515         "Ecuador",
38516         "ec",
38517         "593"
38518       ],
38519       [
38520         "Egypt (‫مصر‬‎)",
38521         "eg",
38522         "20"
38523       ],
38524       [
38525         "El Salvador",
38526         "sv",
38527         "503"
38528       ],
38529       [
38530         "Equatorial Guinea (Guinea Ecuatorial)",
38531         "gq",
38532         "240"
38533       ],
38534       [
38535         "Eritrea",
38536         "er",
38537         "291"
38538       ],
38539       [
38540         "Estonia (Eesti)",
38541         "ee",
38542         "372"
38543       ],
38544       [
38545         "Ethiopia",
38546         "et",
38547         "251"
38548       ],
38549       [
38550         "Falkland Islands (Islas Malvinas)",
38551         "fk",
38552         "500"
38553       ],
38554       [
38555         "Faroe Islands (Føroyar)",
38556         "fo",
38557         "298"
38558       ],
38559       [
38560         "Fiji",
38561         "fj",
38562         "679"
38563       ],
38564       [
38565         "Finland (Suomi)",
38566         "fi",
38567         "358",
38568         0
38569       ],
38570       [
38571         "France",
38572         "fr",
38573         "33"
38574       ],
38575       [
38576         "French Guiana (Guyane française)",
38577         "gf",
38578         "594"
38579       ],
38580       [
38581         "French Polynesia (Polynésie française)",
38582         "pf",
38583         "689"
38584       ],
38585       [
38586         "Gabon",
38587         "ga",
38588         "241"
38589       ],
38590       [
38591         "Gambia",
38592         "gm",
38593         "220"
38594       ],
38595       [
38596         "Georgia (საქართველო)",
38597         "ge",
38598         "995"
38599       ],
38600       [
38601         "Germany (Deutschland)",
38602         "de",
38603         "49"
38604       ],
38605       [
38606         "Ghana (Gaana)",
38607         "gh",
38608         "233"
38609       ],
38610       [
38611         "Gibraltar",
38612         "gi",
38613         "350"
38614       ],
38615       [
38616         "Greece (Ελλάδα)",
38617         "gr",
38618         "30"
38619       ],
38620       [
38621         "Greenland (Kalaallit Nunaat)",
38622         "gl",
38623         "299"
38624       ],
38625       [
38626         "Grenada",
38627         "gd",
38628         "1473"
38629       ],
38630       [
38631         "Guadeloupe",
38632         "gp",
38633         "590",
38634         0
38635       ],
38636       [
38637         "Guam",
38638         "gu",
38639         "1671"
38640       ],
38641       [
38642         "Guatemala",
38643         "gt",
38644         "502"
38645       ],
38646       [
38647         "Guernsey",
38648         "gg",
38649         "44",
38650         1
38651       ],
38652       [
38653         "Guinea (Guinée)",
38654         "gn",
38655         "224"
38656       ],
38657       [
38658         "Guinea-Bissau (Guiné Bissau)",
38659         "gw",
38660         "245"
38661       ],
38662       [
38663         "Guyana",
38664         "gy",
38665         "592"
38666       ],
38667       [
38668         "Haiti",
38669         "ht",
38670         "509"
38671       ],
38672       [
38673         "Honduras",
38674         "hn",
38675         "504"
38676       ],
38677       [
38678         "Hong Kong (香港)",
38679         "hk",
38680         "852"
38681       ],
38682       [
38683         "Hungary (Magyarország)",
38684         "hu",
38685         "36"
38686       ],
38687       [
38688         "Iceland (Ísland)",
38689         "is",
38690         "354"
38691       ],
38692       [
38693         "India (भारत)",
38694         "in",
38695         "91"
38696       ],
38697       [
38698         "Indonesia",
38699         "id",
38700         "62"
38701       ],
38702       [
38703         "Iran (‫ایران‬‎)",
38704         "ir",
38705         "98"
38706       ],
38707       [
38708         "Iraq (‫العراق‬‎)",
38709         "iq",
38710         "964"
38711       ],
38712       [
38713         "Ireland",
38714         "ie",
38715         "353"
38716       ],
38717       [
38718         "Isle of Man",
38719         "im",
38720         "44",
38721         2
38722       ],
38723       [
38724         "Israel (‫ישראל‬‎)",
38725         "il",
38726         "972"
38727       ],
38728       [
38729         "Italy (Italia)",
38730         "it",
38731         "39",
38732         0
38733       ],
38734       [
38735         "Jamaica",
38736         "jm",
38737         "1876"
38738       ],
38739       [
38740         "Japan (日本)",
38741         "jp",
38742         "81"
38743       ],
38744       [
38745         "Jersey",
38746         "je",
38747         "44",
38748         3
38749       ],
38750       [
38751         "Jordan (‫الأردن‬‎)",
38752         "jo",
38753         "962"
38754       ],
38755       [
38756         "Kazakhstan (Казахстан)",
38757         "kz",
38758         "7",
38759         1
38760       ],
38761       [
38762         "Kenya",
38763         "ke",
38764         "254"
38765       ],
38766       [
38767         "Kiribati",
38768         "ki",
38769         "686"
38770       ],
38771       [
38772         "Kosovo",
38773         "xk",
38774         "383"
38775       ],
38776       [
38777         "Kuwait (‫الكويت‬‎)",
38778         "kw",
38779         "965"
38780       ],
38781       [
38782         "Kyrgyzstan (Кыргызстан)",
38783         "kg",
38784         "996"
38785       ],
38786       [
38787         "Laos (ລາວ)",
38788         "la",
38789         "856"
38790       ],
38791       [
38792         "Latvia (Latvija)",
38793         "lv",
38794         "371"
38795       ],
38796       [
38797         "Lebanon (‫لبنان‬‎)",
38798         "lb",
38799         "961"
38800       ],
38801       [
38802         "Lesotho",
38803         "ls",
38804         "266"
38805       ],
38806       [
38807         "Liberia",
38808         "lr",
38809         "231"
38810       ],
38811       [
38812         "Libya (‫ليبيا‬‎)",
38813         "ly",
38814         "218"
38815       ],
38816       [
38817         "Liechtenstein",
38818         "li",
38819         "423"
38820       ],
38821       [
38822         "Lithuania (Lietuva)",
38823         "lt",
38824         "370"
38825       ],
38826       [
38827         "Luxembourg",
38828         "lu",
38829         "352"
38830       ],
38831       [
38832         "Macau (澳門)",
38833         "mo",
38834         "853"
38835       ],
38836       [
38837         "Macedonia (FYROM) (Македонија)",
38838         "mk",
38839         "389"
38840       ],
38841       [
38842         "Madagascar (Madagasikara)",
38843         "mg",
38844         "261"
38845       ],
38846       [
38847         "Malawi",
38848         "mw",
38849         "265"
38850       ],
38851       [
38852         "Malaysia",
38853         "my",
38854         "60"
38855       ],
38856       [
38857         "Maldives",
38858         "mv",
38859         "960"
38860       ],
38861       [
38862         "Mali",
38863         "ml",
38864         "223"
38865       ],
38866       [
38867         "Malta",
38868         "mt",
38869         "356"
38870       ],
38871       [
38872         "Marshall Islands",
38873         "mh",
38874         "692"
38875       ],
38876       [
38877         "Martinique",
38878         "mq",
38879         "596"
38880       ],
38881       [
38882         "Mauritania (‫موريتانيا‬‎)",
38883         "mr",
38884         "222"
38885       ],
38886       [
38887         "Mauritius (Moris)",
38888         "mu",
38889         "230"
38890       ],
38891       [
38892         "Mayotte",
38893         "yt",
38894         "262",
38895         1
38896       ],
38897       [
38898         "Mexico (México)",
38899         "mx",
38900         "52"
38901       ],
38902       [
38903         "Micronesia",
38904         "fm",
38905         "691"
38906       ],
38907       [
38908         "Moldova (Republica Moldova)",
38909         "md",
38910         "373"
38911       ],
38912       [
38913         "Monaco",
38914         "mc",
38915         "377"
38916       ],
38917       [
38918         "Mongolia (Монгол)",
38919         "mn",
38920         "976"
38921       ],
38922       [
38923         "Montenegro (Crna Gora)",
38924         "me",
38925         "382"
38926       ],
38927       [
38928         "Montserrat",
38929         "ms",
38930         "1664"
38931       ],
38932       [
38933         "Morocco (‫المغرب‬‎)",
38934         "ma",
38935         "212",
38936         0
38937       ],
38938       [
38939         "Mozambique (Moçambique)",
38940         "mz",
38941         "258"
38942       ],
38943       [
38944         "Myanmar (Burma) (မြန်မာ)",
38945         "mm",
38946         "95"
38947       ],
38948       [
38949         "Namibia (Namibië)",
38950         "na",
38951         "264"
38952       ],
38953       [
38954         "Nauru",
38955         "nr",
38956         "674"
38957       ],
38958       [
38959         "Nepal (नेपाल)",
38960         "np",
38961         "977"
38962       ],
38963       [
38964         "Netherlands (Nederland)",
38965         "nl",
38966         "31"
38967       ],
38968       [
38969         "New Caledonia (Nouvelle-Calédonie)",
38970         "nc",
38971         "687"
38972       ],
38973       [
38974         "New Zealand",
38975         "nz",
38976         "64"
38977       ],
38978       [
38979         "Nicaragua",
38980         "ni",
38981         "505"
38982       ],
38983       [
38984         "Niger (Nijar)",
38985         "ne",
38986         "227"
38987       ],
38988       [
38989         "Nigeria",
38990         "ng",
38991         "234"
38992       ],
38993       [
38994         "Niue",
38995         "nu",
38996         "683"
38997       ],
38998       [
38999         "Norfolk Island",
39000         "nf",
39001         "672"
39002       ],
39003       [
39004         "North Korea (조선 민주주의 인민 공화국)",
39005         "kp",
39006         "850"
39007       ],
39008       [
39009         "Northern Mariana Islands",
39010         "mp",
39011         "1670"
39012       ],
39013       [
39014         "Norway (Norge)",
39015         "no",
39016         "47",
39017         0
39018       ],
39019       [
39020         "Oman (‫عُمان‬‎)",
39021         "om",
39022         "968"
39023       ],
39024       [
39025         "Pakistan (‫پاکستان‬‎)",
39026         "pk",
39027         "92"
39028       ],
39029       [
39030         "Palau",
39031         "pw",
39032         "680"
39033       ],
39034       [
39035         "Palestine (‫فلسطين‬‎)",
39036         "ps",
39037         "970"
39038       ],
39039       [
39040         "Panama (Panamá)",
39041         "pa",
39042         "507"
39043       ],
39044       [
39045         "Papua New Guinea",
39046         "pg",
39047         "675"
39048       ],
39049       [
39050         "Paraguay",
39051         "py",
39052         "595"
39053       ],
39054       [
39055         "Peru (Perú)",
39056         "pe",
39057         "51"
39058       ],
39059       [
39060         "Philippines",
39061         "ph",
39062         "63"
39063       ],
39064       [
39065         "Poland (Polska)",
39066         "pl",
39067         "48"
39068       ],
39069       [
39070         "Portugal",
39071         "pt",
39072         "351"
39073       ],
39074       [
39075         "Puerto Rico",
39076         "pr",
39077         "1",
39078         3,
39079         ["787", "939"]
39080       ],
39081       [
39082         "Qatar (‫قطر‬‎)",
39083         "qa",
39084         "974"
39085       ],
39086       [
39087         "Réunion (La Réunion)",
39088         "re",
39089         "262",
39090         0
39091       ],
39092       [
39093         "Romania (România)",
39094         "ro",
39095         "40"
39096       ],
39097       [
39098         "Russia (Россия)",
39099         "ru",
39100         "7",
39101         0
39102       ],
39103       [
39104         "Rwanda",
39105         "rw",
39106         "250"
39107       ],
39108       [
39109         "Saint Barthélemy",
39110         "bl",
39111         "590",
39112         1
39113       ],
39114       [
39115         "Saint Helena",
39116         "sh",
39117         "290"
39118       ],
39119       [
39120         "Saint Kitts and Nevis",
39121         "kn",
39122         "1869"
39123       ],
39124       [
39125         "Saint Lucia",
39126         "lc",
39127         "1758"
39128       ],
39129       [
39130         "Saint Martin (Saint-Martin (partie française))",
39131         "mf",
39132         "590",
39133         2
39134       ],
39135       [
39136         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39137         "pm",
39138         "508"
39139       ],
39140       [
39141         "Saint Vincent and the Grenadines",
39142         "vc",
39143         "1784"
39144       ],
39145       [
39146         "Samoa",
39147         "ws",
39148         "685"
39149       ],
39150       [
39151         "San Marino",
39152         "sm",
39153         "378"
39154       ],
39155       [
39156         "São Tomé and Príncipe (São Tomé e Príncipe)",
39157         "st",
39158         "239"
39159       ],
39160       [
39161         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39162         "sa",
39163         "966"
39164       ],
39165       [
39166         "Senegal (Sénégal)",
39167         "sn",
39168         "221"
39169       ],
39170       [
39171         "Serbia (Србија)",
39172         "rs",
39173         "381"
39174       ],
39175       [
39176         "Seychelles",
39177         "sc",
39178         "248"
39179       ],
39180       [
39181         "Sierra Leone",
39182         "sl",
39183         "232"
39184       ],
39185       [
39186         "Singapore",
39187         "sg",
39188         "65"
39189       ],
39190       [
39191         "Sint Maarten",
39192         "sx",
39193         "1721"
39194       ],
39195       [
39196         "Slovakia (Slovensko)",
39197         "sk",
39198         "421"
39199       ],
39200       [
39201         "Slovenia (Slovenija)",
39202         "si",
39203         "386"
39204       ],
39205       [
39206         "Solomon Islands",
39207         "sb",
39208         "677"
39209       ],
39210       [
39211         "Somalia (Soomaaliya)",
39212         "so",
39213         "252"
39214       ],
39215       [
39216         "South Africa",
39217         "za",
39218         "27"
39219       ],
39220       [
39221         "South Korea (대한민국)",
39222         "kr",
39223         "82"
39224       ],
39225       [
39226         "South Sudan (‫جنوب السودان‬‎)",
39227         "ss",
39228         "211"
39229       ],
39230       [
39231         "Spain (España)",
39232         "es",
39233         "34"
39234       ],
39235       [
39236         "Sri Lanka (ශ්‍රී ලංකාව)",
39237         "lk",
39238         "94"
39239       ],
39240       [
39241         "Sudan (‫السودان‬‎)",
39242         "sd",
39243         "249"
39244       ],
39245       [
39246         "Suriname",
39247         "sr",
39248         "597"
39249       ],
39250       [
39251         "Svalbard and Jan Mayen",
39252         "sj",
39253         "47",
39254         1
39255       ],
39256       [
39257         "Swaziland",
39258         "sz",
39259         "268"
39260       ],
39261       [
39262         "Sweden (Sverige)",
39263         "se",
39264         "46"
39265       ],
39266       [
39267         "Switzerland (Schweiz)",
39268         "ch",
39269         "41"
39270       ],
39271       [
39272         "Syria (‫سوريا‬‎)",
39273         "sy",
39274         "963"
39275       ],
39276       [
39277         "Taiwan (台灣)",
39278         "tw",
39279         "886"
39280       ],
39281       [
39282         "Tajikistan",
39283         "tj",
39284         "992"
39285       ],
39286       [
39287         "Tanzania",
39288         "tz",
39289         "255"
39290       ],
39291       [
39292         "Thailand (ไทย)",
39293         "th",
39294         "66"
39295       ],
39296       [
39297         "Timor-Leste",
39298         "tl",
39299         "670"
39300       ],
39301       [
39302         "Togo",
39303         "tg",
39304         "228"
39305       ],
39306       [
39307         "Tokelau",
39308         "tk",
39309         "690"
39310       ],
39311       [
39312         "Tonga",
39313         "to",
39314         "676"
39315       ],
39316       [
39317         "Trinidad and Tobago",
39318         "tt",
39319         "1868"
39320       ],
39321       [
39322         "Tunisia (‫تونس‬‎)",
39323         "tn",
39324         "216"
39325       ],
39326       [
39327         "Turkey (Türkiye)",
39328         "tr",
39329         "90"
39330       ],
39331       [
39332         "Turkmenistan",
39333         "tm",
39334         "993"
39335       ],
39336       [
39337         "Turks and Caicos Islands",
39338         "tc",
39339         "1649"
39340       ],
39341       [
39342         "Tuvalu",
39343         "tv",
39344         "688"
39345       ],
39346       [
39347         "U.S. Virgin Islands",
39348         "vi",
39349         "1340"
39350       ],
39351       [
39352         "Uganda",
39353         "ug",
39354         "256"
39355       ],
39356       [
39357         "Ukraine (Україна)",
39358         "ua",
39359         "380"
39360       ],
39361       [
39362         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39363         "ae",
39364         "971"
39365       ],
39366       [
39367         "United Kingdom",
39368         "gb",
39369         "44",
39370         0
39371       ],
39372       [
39373         "United States",
39374         "us",
39375         "1",
39376         0
39377       ],
39378       [
39379         "Uruguay",
39380         "uy",
39381         "598"
39382       ],
39383       [
39384         "Uzbekistan (Oʻzbekiston)",
39385         "uz",
39386         "998"
39387       ],
39388       [
39389         "Vanuatu",
39390         "vu",
39391         "678"
39392       ],
39393       [
39394         "Vatican City (Città del Vaticano)",
39395         "va",
39396         "39",
39397         1
39398       ],
39399       [
39400         "Venezuela",
39401         "ve",
39402         "58"
39403       ],
39404       [
39405         "Vietnam (Việt Nam)",
39406         "vn",
39407         "84"
39408       ],
39409       [
39410         "Wallis and Futuna (Wallis-et-Futuna)",
39411         "wf",
39412         "681"
39413       ],
39414       [
39415         "Western Sahara (‫الصحراء الغربية‬‎)",
39416         "eh",
39417         "212",
39418         1
39419       ],
39420       [
39421         "Yemen (‫اليمن‬‎)",
39422         "ye",
39423         "967"
39424       ],
39425       [
39426         "Zambia",
39427         "zm",
39428         "260"
39429       ],
39430       [
39431         "Zimbabwe",
39432         "zw",
39433         "263"
39434       ],
39435       [
39436         "Åland Islands",
39437         "ax",
39438         "358",
39439         1
39440       ]
39441   ];
39442   
39443   return d;
39444 }/**
39445 *    This script refer to:
39446 *    Title: International Telephone Input
39447 *    Author: Jack O'Connor
39448 *    Code version:  v12.1.12
39449 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39450 **/
39451
39452 /**
39453  * @class Roo.bootstrap.PhoneInput
39454  * @extends Roo.bootstrap.TriggerField
39455  * An input with International dial-code selection
39456  
39457  * @cfg {String} defaultDialCode default '+852'
39458  * @cfg {Array} preferedCountries default []
39459   
39460  * @constructor
39461  * Create a new PhoneInput.
39462  * @param {Object} config Configuration options
39463  */
39464
39465 Roo.bootstrap.PhoneInput = function(config) {
39466     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39467 };
39468
39469 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39470         
39471         listWidth: undefined,
39472         
39473         selectedClass: 'active',
39474         
39475         invalidClass : "has-warning",
39476         
39477         validClass: 'has-success',
39478         
39479         allowed: '0123456789',
39480         
39481         /**
39482          * @cfg {String} defaultDialCode The default dial code when initializing the input
39483          */
39484         defaultDialCode: '+852',
39485         
39486         /**
39487          * @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
39488          */
39489         preferedCountries: false,
39490         
39491         getAutoCreate : function()
39492         {
39493             var data = Roo.bootstrap.PhoneInputData();
39494             var align = this.labelAlign || this.parentLabelAlign();
39495             var id = Roo.id();
39496             
39497             this.allCountries = [];
39498             this.dialCodeMapping = [];
39499             
39500             for (var i = 0; i < data.length; i++) {
39501               var c = data[i];
39502               this.allCountries[i] = {
39503                 name: c[0],
39504                 iso2: c[1],
39505                 dialCode: c[2],
39506                 priority: c[3] || 0,
39507                 areaCodes: c[4] || null
39508               };
39509               this.dialCodeMapping[c[2]] = {
39510                   name: c[0],
39511                   iso2: c[1],
39512                   priority: c[3] || 0,
39513                   areaCodes: c[4] || null
39514               };
39515             }
39516             
39517             var cfg = {
39518                 cls: 'form-group',
39519                 cn: []
39520             };
39521             
39522             var input =  {
39523                 tag: 'input',
39524                 id : id,
39525                 cls : 'form-control tel-input',
39526                 autocomplete: 'new-password'
39527             };
39528             
39529             var hiddenInput = {
39530                 tag: 'input',
39531                 type: 'hidden',
39532                 cls: 'hidden-tel-input'
39533             };
39534             
39535             if (this.name) {
39536                 hiddenInput.name = this.name;
39537             }
39538             
39539             if (this.disabled) {
39540                 input.disabled = true;
39541             }
39542             
39543             var flag_container = {
39544                 tag: 'div',
39545                 cls: 'flag-box',
39546                 cn: [
39547                     {
39548                         tag: 'div',
39549                         cls: 'flag'
39550                     },
39551                     {
39552                         tag: 'div',
39553                         cls: 'caret'
39554                     }
39555                 ]
39556             };
39557             
39558             var box = {
39559                 tag: 'div',
39560                 cls: this.hasFeedback ? 'has-feedback' : '',
39561                 cn: [
39562                     hiddenInput,
39563                     input,
39564                     {
39565                         tag: 'input',
39566                         cls: 'dial-code-holder',
39567                         disabled: true
39568                     }
39569                 ]
39570             };
39571             
39572             var container = {
39573                 cls: 'roo-select2-container input-group',
39574                 cn: [
39575                     flag_container,
39576                     box
39577                 ]
39578             };
39579             
39580             if (this.fieldLabel.length) {
39581                 var indicator = {
39582                     tag: 'i',
39583                     tooltip: 'This field is required'
39584                 };
39585                 
39586                 var label = {
39587                     tag: 'label',
39588                     'for':  id,
39589                     cls: 'control-label',
39590                     cn: []
39591                 };
39592                 
39593                 var label_text = {
39594                     tag: 'span',
39595                     html: this.fieldLabel
39596                 };
39597                 
39598                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39599                 label.cn = [
39600                     indicator,
39601                     label_text
39602                 ];
39603                 
39604                 if(this.indicatorpos == 'right') {
39605                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39606                     label.cn = [
39607                         label_text,
39608                         indicator
39609                     ];
39610                 }
39611                 
39612                 if(align == 'left') {
39613                     container = {
39614                         tag: 'div',
39615                         cn: [
39616                             container
39617                         ]
39618                     };
39619                     
39620                     if(this.labelWidth > 12){
39621                         label.style = "width: " + this.labelWidth + 'px';
39622                     }
39623                     if(this.labelWidth < 13 && this.labelmd == 0){
39624                         this.labelmd = this.labelWidth;
39625                     }
39626                     if(this.labellg > 0){
39627                         label.cls += ' col-lg-' + this.labellg;
39628                         input.cls += ' col-lg-' + (12 - this.labellg);
39629                     }
39630                     if(this.labelmd > 0){
39631                         label.cls += ' col-md-' + this.labelmd;
39632                         container.cls += ' col-md-' + (12 - this.labelmd);
39633                     }
39634                     if(this.labelsm > 0){
39635                         label.cls += ' col-sm-' + this.labelsm;
39636                         container.cls += ' col-sm-' + (12 - this.labelsm);
39637                     }
39638                     if(this.labelxs > 0){
39639                         label.cls += ' col-xs-' + this.labelxs;
39640                         container.cls += ' col-xs-' + (12 - this.labelxs);
39641                     }
39642                 }
39643             }
39644             
39645             cfg.cn = [
39646                 label,
39647                 container
39648             ];
39649             
39650             var settings = this;
39651             
39652             ['xs','sm','md','lg'].map(function(size){
39653                 if (settings[size]) {
39654                     cfg.cls += ' col-' + size + '-' + settings[size];
39655                 }
39656             });
39657             
39658             this.store = new Roo.data.Store({
39659                 proxy : new Roo.data.MemoryProxy({}),
39660                 reader : new Roo.data.JsonReader({
39661                     fields : [
39662                         {
39663                             'name' : 'name',
39664                             'type' : 'string'
39665                         },
39666                         {
39667                             'name' : 'iso2',
39668                             'type' : 'string'
39669                         },
39670                         {
39671                             'name' : 'dialCode',
39672                             'type' : 'string'
39673                         },
39674                         {
39675                             'name' : 'priority',
39676                             'type' : 'string'
39677                         },
39678                         {
39679                             'name' : 'areaCodes',
39680                             'type' : 'string'
39681                         }
39682                     ]
39683                 })
39684             });
39685             
39686             if(!this.preferedCountries) {
39687                 this.preferedCountries = [
39688                     'hk',
39689                     'gb',
39690                     'us'
39691                 ];
39692             }
39693             
39694             var p = this.preferedCountries.reverse();
39695             
39696             if(p) {
39697                 for (var i = 0; i < p.length; i++) {
39698                     for (var j = 0; j < this.allCountries.length; j++) {
39699                         if(this.allCountries[j].iso2 == p[i]) {
39700                             var t = this.allCountries[j];
39701                             this.allCountries.splice(j,1);
39702                             this.allCountries.unshift(t);
39703                         }
39704                     } 
39705                 }
39706             }
39707             
39708             this.store.proxy.data = {
39709                 success: true,
39710                 data: this.allCountries
39711             };
39712             
39713             return cfg;
39714         },
39715         
39716         initEvents : function()
39717         {
39718             this.createList();
39719             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39720             
39721             this.indicator = this.indicatorEl();
39722             this.flag = this.flagEl();
39723             this.dialCodeHolder = this.dialCodeHolderEl();
39724             
39725             this.trigger = this.el.select('div.flag-box',true).first();
39726             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39727             
39728             var _this = this;
39729             
39730             (function(){
39731                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39732                 _this.list.setWidth(lw);
39733             }).defer(100);
39734             
39735             this.list.on('mouseover', this.onViewOver, this);
39736             this.list.on('mousemove', this.onViewMove, this);
39737             this.inputEl().on("keyup", this.onKeyUp, this);
39738             
39739             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
39740
39741             this.view = new Roo.View(this.list, this.tpl, {
39742                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
39743             });
39744             
39745             this.view.on('click', this.onViewClick, this);
39746             this.setValue(this.defaultDialCode);
39747         },
39748         
39749         onTriggerClick : function(e)
39750         {
39751             Roo.log('trigger click');
39752             if(this.disabled){
39753                 return;
39754             }
39755             
39756             if(this.isExpanded()){
39757                 this.collapse();
39758                 this.hasFocus = false;
39759             }else {
39760                 this.store.load({});
39761                 this.hasFocus = true;
39762                 this.expand();
39763             }
39764         },
39765         
39766         isExpanded : function()
39767         {
39768             return this.list.isVisible();
39769         },
39770         
39771         collapse : function()
39772         {
39773             if(!this.isExpanded()){
39774                 return;
39775             }
39776             this.list.hide();
39777             Roo.get(document).un('mousedown', this.collapseIf, this);
39778             Roo.get(document).un('mousewheel', this.collapseIf, this);
39779             this.fireEvent('collapse', this);
39780             this.validate();
39781         },
39782         
39783         expand : function()
39784         {
39785             Roo.log('expand');
39786
39787             if(this.isExpanded() || !this.hasFocus){
39788                 return;
39789             }
39790             
39791             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
39792             this.list.setWidth(lw);
39793             
39794             this.list.show();
39795             this.restrictHeight();
39796             
39797             Roo.get(document).on('mousedown', this.collapseIf, this);
39798             Roo.get(document).on('mousewheel', this.collapseIf, this);
39799             
39800             this.fireEvent('expand', this);
39801         },
39802         
39803         restrictHeight : function()
39804         {
39805             this.list.alignTo(this.inputEl(), this.listAlign);
39806             this.list.alignTo(this.inputEl(), this.listAlign);
39807         },
39808         
39809         onViewOver : function(e, t)
39810         {
39811             if(this.inKeyMode){
39812                 return;
39813             }
39814             var item = this.view.findItemFromChild(t);
39815             
39816             if(item){
39817                 var index = this.view.indexOf(item);
39818                 this.select(index, false);
39819             }
39820         },
39821
39822         // private
39823         onViewClick : function(view, doFocus, el, e)
39824         {
39825             var index = this.view.getSelectedIndexes()[0];
39826             
39827             var r = this.store.getAt(index);
39828             
39829             if(r){
39830                 this.onSelect(r, index);
39831             }
39832             if(doFocus !== false && !this.blockFocus){
39833                 this.inputEl().focus();
39834             }
39835         },
39836         
39837         onViewMove : function(e, t)
39838         {
39839             this.inKeyMode = false;
39840         },
39841         
39842         select : function(index, scrollIntoView)
39843         {
39844             this.selectedIndex = index;
39845             this.view.select(index);
39846             if(scrollIntoView !== false){
39847                 var el = this.view.getNode(index);
39848                 if(el){
39849                     this.list.scrollChildIntoView(el, false);
39850                 }
39851             }
39852         },
39853         
39854         createList : function()
39855         {
39856             this.list = Roo.get(document.body).createChild({
39857                 tag: 'ul',
39858                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
39859                 style: 'display:none'
39860             });
39861             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
39862         },
39863         
39864         collapseIf : function(e)
39865         {
39866             var in_combo  = e.within(this.el);
39867             var in_list =  e.within(this.list);
39868             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
39869             
39870             if (in_combo || in_list || is_list) {
39871                 return;
39872             }
39873             this.collapse();
39874         },
39875         
39876         onSelect : function(record, index)
39877         {
39878             if(this.fireEvent('beforeselect', this, record, index) !== false){
39879                 
39880                 this.setFlagClass(record.data.iso2);
39881                 this.setDialCode(record.data.dialCode);
39882                 this.hasFocus = false;
39883                 this.collapse();
39884                 this.fireEvent('select', this, record, index);
39885             }
39886         },
39887         
39888         flagEl : function()
39889         {
39890             var flag = this.el.select('div.flag',true).first();
39891             if(!flag){
39892                 return false;
39893             }
39894             return flag;
39895         },
39896         
39897         dialCodeHolderEl : function()
39898         {
39899             var d = this.el.select('input.dial-code-holder',true).first();
39900             if(!d){
39901                 return false;
39902             }
39903             return d;
39904         },
39905         
39906         setDialCode : function(v)
39907         {
39908             this.dialCodeHolder.dom.value = '+'+v;
39909         },
39910         
39911         setFlagClass : function(n)
39912         {
39913             this.flag.dom.className = 'flag '+n;
39914         },
39915         
39916         getValue : function()
39917         {
39918             var v = this.inputEl().getValue();
39919             if(this.dialCodeHolder) {
39920                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
39921             }
39922             return v;
39923         },
39924         
39925         setValue : function(v)
39926         {
39927             var d = this.getDialCode(v);
39928             
39929             //invalid dial code
39930             if(v.length == 0 || !d || d.length == 0) {
39931                 if(this.rendered){
39932                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
39933                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
39934                 }
39935                 return;
39936             }
39937             
39938             //valid dial code
39939             this.setFlagClass(this.dialCodeMapping[d].iso2);
39940             this.setDialCode(d);
39941             this.inputEl().dom.value = v.replace('+'+d,'');
39942             this.hiddenEl().dom.value = this.getValue();
39943             
39944             this.validate();
39945         },
39946         
39947         getDialCode : function(v = '')
39948         {
39949             if (v.length == 0) {
39950                 return this.dialCodeHolder.dom.value;
39951             }
39952             
39953             var dialCode = "";
39954             if (v.charAt(0) != "+") {
39955                 return false;
39956             }
39957             var numericChars = "";
39958             for (var i = 1; i < v.length; i++) {
39959               var c = v.charAt(i);
39960               if (!isNaN(c)) {
39961                 numericChars += c;
39962                 if (this.dialCodeMapping[numericChars]) {
39963                   dialCode = v.substr(1, i);
39964                 }
39965                 if (numericChars.length == 4) {
39966                   break;
39967                 }
39968               }
39969             }
39970             return dialCode;
39971         },
39972         
39973         reset : function()
39974         {
39975             this.setValue(this.defaultDialCode);
39976             this.validate();
39977         },
39978         
39979         hiddenEl : function()
39980         {
39981             return this.el.select('input.hidden-tel-input',true).first();
39982         },
39983         
39984         onKeyUp : function(e){
39985             
39986             var k = e.getKey();
39987             var c = e.getCharCode();
39988             
39989             if(
39990                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
39991                     this.allowed.indexOf(String.fromCharCode(c)) === -1
39992             ){
39993                 e.stopEvent();
39994             }
39995             
39996             // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
39997             //     return;
39998             // }
39999             if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
40000                 e.stopEvent();
40001             }
40002             
40003             this.setValue(this.getValue());
40004         }
40005         
40006 });
40007 /**
40008  * @class Roo.bootstrap.MoneyField
40009  * @extends Roo.bootstrap.ComboBox
40010  * Bootstrap MoneyField class
40011  * 
40012  * @constructor
40013  * Create a new MoneyField.
40014  * @param {Object} config Configuration options
40015  */
40016
40017 Roo.bootstrap.MoneyField = function(config) {
40018     
40019     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40020     
40021 };
40022
40023 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40024     
40025     /**
40026      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40027      */
40028     allowDecimals : true,
40029     /**
40030      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40031      */
40032     decimalSeparator : ".",
40033     /**
40034      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40035      */
40036     decimalPrecision : 0,
40037     /**
40038      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40039      */
40040     allowNegative : true,
40041     /**
40042      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40043      */
40044     minValue : Number.NEGATIVE_INFINITY,
40045     /**
40046      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40047      */
40048     maxValue : Number.MAX_VALUE,
40049     /**
40050      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40051      */
40052     minText : "The minimum value for this field is {0}",
40053     /**
40054      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40055      */
40056     maxText : "The maximum value for this field is {0}",
40057     /**
40058      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40059      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40060      */
40061     nanText : "{0} is not a valid number",
40062     /**
40063      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40064      */
40065     castInt : true,
40066     /**
40067      * @cfg {String} defaults currency of the MoneyField
40068      * value should be in lkey
40069      */
40070     defaultCurrency : false,
40071     /**
40072      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
40073      */
40074     thousandsDelimiter : false,
40075     
40076     
40077     inputlg : 9,
40078     inputmd : 9,
40079     inputsm : 9,
40080     inputxs : 6,
40081     
40082     store : false,
40083     
40084     getAutoCreate : function()
40085     {
40086         var align = this.labelAlign || this.parentLabelAlign();
40087         
40088         var id = Roo.id();
40089
40090         var cfg = {
40091             cls: 'form-group',
40092             cn: []
40093         };
40094
40095         var input =  {
40096             tag: 'input',
40097             id : id,
40098             cls : 'form-control roo-money-amount-input',
40099             autocomplete: 'new-password'
40100         };
40101         
40102         var hiddenInput = {
40103             tag: 'input',
40104             type: 'hidden',
40105             id: Roo.id(),
40106             cls: 'hidden-number-input'
40107         };
40108         
40109         if (this.name) {
40110             hiddenInput.name = this.name;
40111         }
40112
40113         if (this.disabled) {
40114             input.disabled = true;
40115         }
40116
40117         var clg = 12 - this.inputlg;
40118         var cmd = 12 - this.inputmd;
40119         var csm = 12 - this.inputsm;
40120         var cxs = 12 - this.inputxs;
40121         
40122         var container = {
40123             tag : 'div',
40124             cls : 'row roo-money-field',
40125             cn : [
40126                 {
40127                     tag : 'div',
40128                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40129                     cn : [
40130                         {
40131                             tag : 'div',
40132                             cls: 'roo-select2-container input-group',
40133                             cn: [
40134                                 {
40135                                     tag : 'input',
40136                                     cls : 'form-control roo-money-currency-input',
40137                                     autocomplete: 'new-password',
40138                                     readOnly : 1,
40139                                     name : this.currencyName
40140                                 },
40141                                 {
40142                                     tag :'span',
40143                                     cls : 'input-group-addon',
40144                                     cn : [
40145                                         {
40146                                             tag: 'span',
40147                                             cls: 'caret'
40148                                         }
40149                                     ]
40150                                 }
40151                             ]
40152                         }
40153                     ]
40154                 },
40155                 {
40156                     tag : 'div',
40157                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40158                     cn : [
40159                         {
40160                             tag: 'div',
40161                             cls: this.hasFeedback ? 'has-feedback' : '',
40162                             cn: [
40163                                 input
40164                             ]
40165                         }
40166                     ]
40167                 }
40168             ]
40169             
40170         };
40171         
40172         if (this.fieldLabel.length) {
40173             var indicator = {
40174                 tag: 'i',
40175                 tooltip: 'This field is required'
40176             };
40177
40178             var label = {
40179                 tag: 'label',
40180                 'for':  id,
40181                 cls: 'control-label',
40182                 cn: []
40183             };
40184
40185             var label_text = {
40186                 tag: 'span',
40187                 html: this.fieldLabel
40188             };
40189
40190             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40191             label.cn = [
40192                 indicator,
40193                 label_text
40194             ];
40195
40196             if(this.indicatorpos == 'right') {
40197                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40198                 label.cn = [
40199                     label_text,
40200                     indicator
40201                 ];
40202             }
40203
40204             if(align == 'left') {
40205                 container = {
40206                     tag: 'div',
40207                     cn: [
40208                         container
40209                     ]
40210                 };
40211
40212                 if(this.labelWidth > 12){
40213                     label.style = "width: " + this.labelWidth + 'px';
40214                 }
40215                 if(this.labelWidth < 13 && this.labelmd == 0){
40216                     this.labelmd = this.labelWidth;
40217                 }
40218                 if(this.labellg > 0){
40219                     label.cls += ' col-lg-' + this.labellg;
40220                     input.cls += ' col-lg-' + (12 - this.labellg);
40221                 }
40222                 if(this.labelmd > 0){
40223                     label.cls += ' col-md-' + this.labelmd;
40224                     container.cls += ' col-md-' + (12 - this.labelmd);
40225                 }
40226                 if(this.labelsm > 0){
40227                     label.cls += ' col-sm-' + this.labelsm;
40228                     container.cls += ' col-sm-' + (12 - this.labelsm);
40229                 }
40230                 if(this.labelxs > 0){
40231                     label.cls += ' col-xs-' + this.labelxs;
40232                     container.cls += ' col-xs-' + (12 - this.labelxs);
40233                 }
40234             }
40235         }
40236
40237         cfg.cn = [
40238             label,
40239             container,
40240             hiddenInput
40241         ];
40242         
40243         var settings = this;
40244
40245         ['xs','sm','md','lg'].map(function(size){
40246             if (settings[size]) {
40247                 cfg.cls += ' col-' + size + '-' + settings[size];
40248             }
40249         });
40250         
40251         return cfg;
40252     },
40253     
40254     initEvents : function()
40255     {
40256         this.indicator = this.indicatorEl();
40257         
40258         this.initCurrencyEvent();
40259         
40260         this.initNumberEvent();
40261     },
40262     
40263     initCurrencyEvent : function()
40264     {
40265         if (!this.store) {
40266             throw "can not find store for combo";
40267         }
40268         
40269         this.store = Roo.factory(this.store, Roo.data);
40270         this.store.parent = this;
40271         
40272         this.createList();
40273         
40274         this.triggerEl = this.el.select('.input-group-addon', true).first();
40275         
40276         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40277         
40278         var _this = this;
40279         
40280         (function(){
40281             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40282             _this.list.setWidth(lw);
40283         }).defer(100);
40284         
40285         this.list.on('mouseover', this.onViewOver, this);
40286         this.list.on('mousemove', this.onViewMove, this);
40287         this.list.on('scroll', this.onViewScroll, this);
40288         
40289         if(!this.tpl){
40290             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40291         }
40292         
40293         this.view = new Roo.View(this.list, this.tpl, {
40294             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40295         });
40296         
40297         this.view.on('click', this.onViewClick, this);
40298         
40299         this.store.on('beforeload', this.onBeforeLoad, this);
40300         this.store.on('load', this.onLoad, this);
40301         this.store.on('loadexception', this.onLoadException, this);
40302         
40303         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40304             "up" : function(e){
40305                 this.inKeyMode = true;
40306                 this.selectPrev();
40307             },
40308
40309             "down" : function(e){
40310                 if(!this.isExpanded()){
40311                     this.onTriggerClick();
40312                 }else{
40313                     this.inKeyMode = true;
40314                     this.selectNext();
40315                 }
40316             },
40317
40318             "enter" : function(e){
40319                 this.collapse();
40320                 
40321                 if(this.fireEvent("specialkey", this, e)){
40322                     this.onViewClick(false);
40323                 }
40324                 
40325                 return true;
40326             },
40327
40328             "esc" : function(e){
40329                 this.collapse();
40330             },
40331
40332             "tab" : function(e){
40333                 this.collapse();
40334                 
40335                 if(this.fireEvent("specialkey", this, e)){
40336                     this.onViewClick(false);
40337                 }
40338                 
40339                 return true;
40340             },
40341
40342             scope : this,
40343
40344             doRelay : function(foo, bar, hname){
40345                 if(hname == 'down' || this.scope.isExpanded()){
40346                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40347                 }
40348                 return true;
40349             },
40350
40351             forceKeyDown: true
40352         });
40353         
40354         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40355         
40356     },
40357     
40358     initNumberEvent : function(e)
40359     {
40360         this.inputEl().on("keydown" , this.fireKey,  this);
40361         this.inputEl().on("focus", this.onFocus,  this);
40362         this.inputEl().on("blur", this.onBlur,  this);
40363         
40364         this.inputEl().relayEvent('keyup', this);
40365         
40366         if(this.indicator){
40367             this.indicator.addClass('invisible');
40368         }
40369  
40370         this.originalValue = this.getValue();
40371         
40372         if(this.validationEvent == 'keyup'){
40373             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40374             this.inputEl().on('keyup', this.filterValidation, this);
40375         }
40376         else if(this.validationEvent !== false){
40377             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40378         }
40379         
40380         if(this.selectOnFocus){
40381             this.on("focus", this.preFocus, this);
40382             
40383         }
40384         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40385             this.inputEl().on("keypress", this.filterKeys, this);
40386         } else {
40387             this.inputEl().relayEvent('keypress', this);
40388         }
40389         
40390         var allowed = "0123456789";
40391         
40392         if(this.allowDecimals){
40393             allowed += this.decimalSeparator;
40394         }
40395         
40396         if(this.allowNegative){
40397             allowed += "-";
40398         }
40399         
40400         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40401         
40402         var keyPress = function(e){
40403             
40404             var k = e.getKey();
40405             
40406             var c = e.getCharCode();
40407             
40408             if(
40409                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40410                     allowed.indexOf(String.fromCharCode(c)) === -1
40411             ){
40412                 e.stopEvent();
40413                 return;
40414             }
40415             
40416             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40417                 return;
40418             }
40419             
40420             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40421                 e.stopEvent();
40422             }
40423         };
40424         
40425         this.inputEl().on("keypress", keyPress, this);
40426         
40427     },
40428     
40429     onTriggerClick : function(e)
40430     {   
40431         if(this.disabled){
40432             return;
40433         }
40434         
40435         this.page = 0;
40436         this.loadNext = false;
40437         
40438         if(this.isExpanded()){
40439             this.collapse();
40440             return;
40441         }
40442         
40443         this.hasFocus = true;
40444         
40445         if(this.triggerAction == 'all') {
40446             this.doQuery(this.allQuery, true);
40447             return;
40448         }
40449         
40450         this.doQuery(this.getRawValue());
40451     },
40452     
40453     getCurrency : function()
40454     {   
40455         var v = this.currencyEl().getValue();
40456         
40457         return v;
40458     },
40459     
40460     restrictHeight : function()
40461     {
40462         this.list.alignTo(this.currencyEl(), this.listAlign);
40463         this.list.alignTo(this.currencyEl(), this.listAlign);
40464     },
40465     
40466     onViewClick : function(view, doFocus, el, e)
40467     {
40468         var index = this.view.getSelectedIndexes()[0];
40469         
40470         var r = this.store.getAt(index);
40471         
40472         if(r){
40473             this.onSelect(r, index);
40474         }
40475     },
40476     
40477     onSelect : function(record, index){
40478         
40479         if(this.fireEvent('beforeselect', this, record, index) !== false){
40480         
40481             this.setFromCurrencyData(index > -1 ? record.data : false);
40482             
40483             this.collapse();
40484             
40485             this.fireEvent('select', this, record, index);
40486         }
40487     },
40488     
40489     setFromCurrencyData : function(o)
40490     {
40491         var currency = '';
40492         
40493         this.lastCurrency = o;
40494         
40495         if (this.currencyField) {
40496             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40497         } else {
40498             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40499         }
40500         
40501         this.lastSelectionText = currency;
40502         
40503         //setting default currency
40504         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40505             this.setCurrency(this.defaultCurrency);
40506             return;
40507         }
40508         
40509         this.setCurrency(currency);
40510     },
40511     
40512     setFromData : function(o)
40513     {
40514         var c = {};
40515         
40516         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40517         
40518         this.setFromCurrencyData(c);
40519         
40520         var value = '';
40521         
40522         if (this.name) {
40523             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40524         } else {
40525             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40526         }
40527         
40528         this.setValue(value);
40529         
40530     },
40531     
40532     setCurrency : function(v)
40533     {   
40534         this.currencyValue = v;
40535         
40536         if(this.rendered){
40537             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40538             this.validate();
40539         }
40540     },
40541     
40542     setValue : function(v)
40543     {
40544         v = this.fixPrecision(v);
40545         
40546         v = String(v).replace(".", this.decimalSeparator);
40547         
40548         this.value = v;
40549         
40550         if(this.rendered){
40551             
40552             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
40553             
40554             this.inputEl().dom.value = Roo.util.Format.number(v, this.decimalPrecision, 
40555                 this.thousandsDelimiter || ','
40556             );
40557             
40558             if(this.allowBlank && !v) {
40559                 this.inputEl().dom.value = '';
40560             }
40561             
40562             this.validate();
40563         }
40564     },
40565     
40566     getRawValue : function()
40567     {
40568         var v = this.inputEl().getValue();
40569         
40570         return v;
40571     },
40572     
40573     getValue : function()
40574     {
40575         return this.fixPrecision(this.parseValue(this.getRawValue()));
40576     },
40577     
40578     parseValue : function(value)
40579     {
40580         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40581         return isNaN(value) ? '' : value;
40582     },
40583     
40584     fixPrecision : function(value)
40585     {
40586         var nan = isNaN(value);
40587         
40588         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40589             return nan ? '' : value;
40590         }
40591         
40592         return parseFloat(value).toFixed(this.decimalPrecision);
40593     },
40594     
40595     decimalPrecisionFcn : function(v)
40596     {
40597         return Math.floor(v);
40598     },
40599     
40600     validateValue : function(value)
40601     {
40602         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40603             return false;
40604         }
40605         
40606         var num = this.parseValue(value);
40607         
40608         if(isNaN(num)){
40609             this.markInvalid(String.format(this.nanText, value));
40610             return false;
40611         }
40612         
40613         if(num < this.minValue){
40614             this.markInvalid(String.format(this.minText, this.minValue));
40615             return false;
40616         }
40617         
40618         if(num > this.maxValue){
40619             this.markInvalid(String.format(this.maxText, this.maxValue));
40620             return false;
40621         }
40622         
40623         return true;
40624     },
40625     
40626     validate : function()
40627     {
40628         if(this.disabled || this.allowBlank){
40629             this.markValid();
40630             return true;
40631         }
40632         
40633         var currency = this.getCurrency();
40634         
40635         if(this.validateValue(this.getRawValue()) && currency.length){
40636             this.markValid();
40637             return true;
40638         }
40639         
40640         this.markInvalid();
40641         return false;
40642     },
40643     
40644     getName: function()
40645     {
40646         return this.name;
40647     },
40648     
40649     beforeBlur : function()
40650     {
40651         if(!this.castInt){
40652             return;
40653         }
40654         
40655         var v = this.parseValue(this.getRawValue());
40656         
40657         if(v){
40658             this.setValue(v);
40659         }
40660     },
40661     
40662     onBlur : function()
40663     {
40664         this.beforeBlur();
40665         
40666         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40667             //this.el.removeClass(this.focusClass);
40668         }
40669         
40670         this.hasFocus = false;
40671         
40672         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40673             this.validate();
40674         }
40675         
40676         var v = this.getValue();
40677         
40678         if(String(v) !== String(this.startValue)){
40679             this.fireEvent('change', this, v, this.startValue);
40680         }
40681         
40682         this.fireEvent("blur", this);
40683     },
40684     
40685     inputEl : function()
40686     {
40687         return this.el.select('.roo-money-amount-input', true).first();
40688     },
40689     
40690     currencyEl : function()
40691     {
40692         return this.el.select('.roo-money-currency-input', true).first();
40693     },
40694     
40695     hiddenEl : function()
40696     {
40697         return this.el.select('input.hidden-number-input',true).first();
40698     }
40699     
40700 });