35905bf60a55bd2643aa4c4365a8c7126dcf50c4
[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
8067
8068         return this;
8069     },
8070
8071     /**
8072      * Resets this form.
8073      * @return {BasicForm} this
8074      */
8075     reset : function(){
8076         var items = this.getItems();
8077         items.each(function(f){
8078             f.reset();
8079         });
8080
8081         Roo.each(this.childForms || [], function (f) {
8082             f.reset();
8083         });
8084
8085
8086         return this;
8087     },
8088     
8089     getItems : function()
8090     {
8091         var r=new Roo.util.MixedCollection(false, function(o){
8092             return o.id || (o.id = Roo.id());
8093         });
8094         var iter = function(el) {
8095             if (el.inputEl) {
8096                 r.add(el);
8097             }
8098             if (!el.items) {
8099                 return;
8100             }
8101             Roo.each(el.items,function(e) {
8102                 iter(e);
8103             });
8104
8105
8106         };
8107
8108         iter(this);
8109         return r;
8110         
8111     }
8112
8113 });
8114
8115 Roo.apply(Roo.bootstrap.Form, {
8116     
8117     popover : {
8118         
8119         padding : 5,
8120         
8121         isApplied : false,
8122         
8123         isMasked : false,
8124         
8125         form : false,
8126         
8127         target : false,
8128         
8129         toolTip : false,
8130         
8131         intervalID : false,
8132         
8133         maskEl : false,
8134         
8135         apply : function()
8136         {
8137             if(this.isApplied){
8138                 return;
8139             }
8140             
8141             this.maskEl = {
8142                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8143                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8144                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8145                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8146             };
8147             
8148             this.maskEl.top.enableDisplayMode("block");
8149             this.maskEl.left.enableDisplayMode("block");
8150             this.maskEl.bottom.enableDisplayMode("block");
8151             this.maskEl.right.enableDisplayMode("block");
8152             
8153             this.toolTip = new Roo.bootstrap.Tooltip({
8154                 cls : 'roo-form-error-popover',
8155                 alignment : {
8156                     'left' : ['r-l', [-2,0], 'right'],
8157                     'right' : ['l-r', [2,0], 'left'],
8158                     'bottom' : ['tl-bl', [0,2], 'top'],
8159                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8160                 }
8161             });
8162             
8163             this.toolTip.render(Roo.get(document.body));
8164
8165             this.toolTip.el.enableDisplayMode("block");
8166             
8167             Roo.get(document.body).on('click', function(){
8168                 this.unmask();
8169             }, this);
8170             
8171             Roo.get(document.body).on('touchstart', function(){
8172                 this.unmask();
8173             }, this);
8174             
8175             this.isApplied = true
8176         },
8177         
8178         mask : function(form, target)
8179         {
8180             this.form = form;
8181             
8182             this.target = target;
8183             
8184             if(!this.form.errorMask || !target.el){
8185                 return;
8186             }
8187             
8188             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8189             
8190             Roo.log(scrollable);
8191             
8192             var ot = this.target.el.calcOffsetsTo(scrollable);
8193             
8194             var scrollTo = ot[1] - this.form.maskOffset;
8195             
8196             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8197             
8198             scrollable.scrollTo('top', scrollTo);
8199             
8200             var box = this.target.el.getBox();
8201             Roo.log(box);
8202             var zIndex = Roo.bootstrap.Modal.zIndex++;
8203
8204             
8205             this.maskEl.top.setStyle('position', 'absolute');
8206             this.maskEl.top.setStyle('z-index', zIndex);
8207             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8208             this.maskEl.top.setLeft(0);
8209             this.maskEl.top.setTop(0);
8210             this.maskEl.top.show();
8211             
8212             this.maskEl.left.setStyle('position', 'absolute');
8213             this.maskEl.left.setStyle('z-index', zIndex);
8214             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8215             this.maskEl.left.setLeft(0);
8216             this.maskEl.left.setTop(box.y - this.padding);
8217             this.maskEl.left.show();
8218
8219             this.maskEl.bottom.setStyle('position', 'absolute');
8220             this.maskEl.bottom.setStyle('z-index', zIndex);
8221             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8222             this.maskEl.bottom.setLeft(0);
8223             this.maskEl.bottom.setTop(box.bottom + this.padding);
8224             this.maskEl.bottom.show();
8225
8226             this.maskEl.right.setStyle('position', 'absolute');
8227             this.maskEl.right.setStyle('z-index', zIndex);
8228             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8229             this.maskEl.right.setLeft(box.right + this.padding);
8230             this.maskEl.right.setTop(box.y - this.padding);
8231             this.maskEl.right.show();
8232
8233             this.toolTip.bindEl = this.target.el;
8234
8235             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8236
8237             var tip = this.target.blankText;
8238
8239             if(this.target.getValue() !== '' ) {
8240                 
8241                 if (this.target.invalidText.length) {
8242                     tip = this.target.invalidText;
8243                 } else if (this.target.regexText.length){
8244                     tip = this.target.regexText;
8245                 }
8246             }
8247
8248             this.toolTip.show(tip);
8249
8250             this.intervalID = window.setInterval(function() {
8251                 Roo.bootstrap.Form.popover.unmask();
8252             }, 10000);
8253
8254             window.onwheel = function(){ return false;};
8255             
8256             (function(){ this.isMasked = true; }).defer(500, this);
8257             
8258         },
8259         
8260         unmask : function()
8261         {
8262             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8263                 return;
8264             }
8265             
8266             this.maskEl.top.setStyle('position', 'absolute');
8267             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8268             this.maskEl.top.hide();
8269
8270             this.maskEl.left.setStyle('position', 'absolute');
8271             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8272             this.maskEl.left.hide();
8273
8274             this.maskEl.bottom.setStyle('position', 'absolute');
8275             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8276             this.maskEl.bottom.hide();
8277
8278             this.maskEl.right.setStyle('position', 'absolute');
8279             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8280             this.maskEl.right.hide();
8281             
8282             this.toolTip.hide();
8283             
8284             this.toolTip.el.hide();
8285             
8286             window.onwheel = function(){ return true;};
8287             
8288             if(this.intervalID){
8289                 window.clearInterval(this.intervalID);
8290                 this.intervalID = false;
8291             }
8292             
8293             this.isMasked = false;
8294             
8295         }
8296         
8297     }
8298     
8299 });
8300
8301 /*
8302  * Based on:
8303  * Ext JS Library 1.1.1
8304  * Copyright(c) 2006-2007, Ext JS, LLC.
8305  *
8306  * Originally Released Under LGPL - original licence link has changed is not relivant.
8307  *
8308  * Fork - LGPL
8309  * <script type="text/javascript">
8310  */
8311 /**
8312  * @class Roo.form.VTypes
8313  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8314  * @singleton
8315  */
8316 Roo.form.VTypes = function(){
8317     // closure these in so they are only created once.
8318     var alpha = /^[a-zA-Z_]+$/;
8319     var alphanum = /^[a-zA-Z0-9_]+$/;
8320     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8321     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8322
8323     // All these messages and functions are configurable
8324     return {
8325         /**
8326          * The function used to validate email addresses
8327          * @param {String} value The email address
8328          */
8329         'email' : function(v){
8330             return email.test(v);
8331         },
8332         /**
8333          * The error text to display when the email validation function returns false
8334          * @type String
8335          */
8336         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8337         /**
8338          * The keystroke filter mask to be applied on email input
8339          * @type RegExp
8340          */
8341         'emailMask' : /[a-z0-9_\.\-@]/i,
8342
8343         /**
8344          * The function used to validate URLs
8345          * @param {String} value The URL
8346          */
8347         'url' : function(v){
8348             return url.test(v);
8349         },
8350         /**
8351          * The error text to display when the url validation function returns false
8352          * @type String
8353          */
8354         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8355         
8356         /**
8357          * The function used to validate alpha values
8358          * @param {String} value The value
8359          */
8360         'alpha' : function(v){
8361             return alpha.test(v);
8362         },
8363         /**
8364          * The error text to display when the alpha validation function returns false
8365          * @type String
8366          */
8367         'alphaText' : 'This field should only contain letters and _',
8368         /**
8369          * The keystroke filter mask to be applied on alpha input
8370          * @type RegExp
8371          */
8372         'alphaMask' : /[a-z_]/i,
8373
8374         /**
8375          * The function used to validate alphanumeric values
8376          * @param {String} value The value
8377          */
8378         'alphanum' : function(v){
8379             return alphanum.test(v);
8380         },
8381         /**
8382          * The error text to display when the alphanumeric validation function returns false
8383          * @type String
8384          */
8385         'alphanumText' : 'This field should only contain letters, numbers and _',
8386         /**
8387          * The keystroke filter mask to be applied on alphanumeric input
8388          * @type RegExp
8389          */
8390         'alphanumMask' : /[a-z0-9_]/i
8391     };
8392 }();/*
8393  * - LGPL
8394  *
8395  * Input
8396  * 
8397  */
8398
8399 /**
8400  * @class Roo.bootstrap.Input
8401  * @extends Roo.bootstrap.Component
8402  * Bootstrap Input class
8403  * @cfg {Boolean} disabled is it disabled
8404  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8405  * @cfg {String} name name of the input
8406  * @cfg {string} fieldLabel - the label associated
8407  * @cfg {string} placeholder - placeholder to put in text.
8408  * @cfg {string}  before - input group add on before
8409  * @cfg {string} after - input group add on after
8410  * @cfg {string} size - (lg|sm) or leave empty..
8411  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8412  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8413  * @cfg {Number} md colspan out of 12 for computer-sized screens
8414  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8415  * @cfg {string} value default value of the input
8416  * @cfg {Number} labelWidth set the width of label 
8417  * @cfg {Number} labellg set the width of label (1-12)
8418  * @cfg {Number} labelmd set the width of label (1-12)
8419  * @cfg {Number} labelsm set the width of label (1-12)
8420  * @cfg {Number} labelxs set the width of label (1-12)
8421  * @cfg {String} labelAlign (top|left)
8422  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8423  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8424  * @cfg {String} indicatorpos (left|right) default left
8425
8426  * @cfg {String} align (left|center|right) Default left
8427  * @cfg {Boolean} forceFeedback (true|false) Default false
8428  * 
8429  * 
8430  * 
8431  * 
8432  * @constructor
8433  * Create a new Input
8434  * @param {Object} config The config object
8435  */
8436
8437 Roo.bootstrap.Input = function(config){
8438     
8439     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8440     
8441     this.addEvents({
8442         /**
8443          * @event focus
8444          * Fires when this field receives input focus.
8445          * @param {Roo.form.Field} this
8446          */
8447         focus : true,
8448         /**
8449          * @event blur
8450          * Fires when this field loses input focus.
8451          * @param {Roo.form.Field} this
8452          */
8453         blur : true,
8454         /**
8455          * @event specialkey
8456          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8457          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8458          * @param {Roo.form.Field} this
8459          * @param {Roo.EventObject} e The event object
8460          */
8461         specialkey : true,
8462         /**
8463          * @event change
8464          * Fires just before the field blurs if the field value has changed.
8465          * @param {Roo.form.Field} this
8466          * @param {Mixed} newValue The new value
8467          * @param {Mixed} oldValue The original value
8468          */
8469         change : true,
8470         /**
8471          * @event invalid
8472          * Fires after the field has been marked as invalid.
8473          * @param {Roo.form.Field} this
8474          * @param {String} msg The validation message
8475          */
8476         invalid : true,
8477         /**
8478          * @event valid
8479          * Fires after the field has been validated with no errors.
8480          * @param {Roo.form.Field} this
8481          */
8482         valid : true,
8483          /**
8484          * @event keyup
8485          * Fires after the key up
8486          * @param {Roo.form.Field} this
8487          * @param {Roo.EventObject}  e The event Object
8488          */
8489         keyup : true
8490     });
8491 };
8492
8493 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8494      /**
8495      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8496       automatic validation (defaults to "keyup").
8497      */
8498     validationEvent : "keyup",
8499      /**
8500      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8501      */
8502     validateOnBlur : true,
8503     /**
8504      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8505      */
8506     validationDelay : 250,
8507      /**
8508      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8509      */
8510     focusClass : "x-form-focus",  // not needed???
8511     
8512        
8513     /**
8514      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8515      */
8516     invalidClass : "has-warning",
8517     
8518     /**
8519      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8520      */
8521     validClass : "has-success",
8522     
8523     /**
8524      * @cfg {Boolean} hasFeedback (true|false) default true
8525      */
8526     hasFeedback : true,
8527     
8528     /**
8529      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8530      */
8531     invalidFeedbackClass : "glyphicon-warning-sign",
8532     
8533     /**
8534      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8535      */
8536     validFeedbackClass : "glyphicon-ok",
8537     
8538     /**
8539      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8540      */
8541     selectOnFocus : false,
8542     
8543      /**
8544      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8545      */
8546     maskRe : null,
8547        /**
8548      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8549      */
8550     vtype : null,
8551     
8552       /**
8553      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8554      */
8555     disableKeyFilter : false,
8556     
8557        /**
8558      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8559      */
8560     disabled : false,
8561      /**
8562      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8563      */
8564     allowBlank : true,
8565     /**
8566      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8567      */
8568     blankText : "Please complete this mandatory field",
8569     
8570      /**
8571      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8572      */
8573     minLength : 0,
8574     /**
8575      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8576      */
8577     maxLength : Number.MAX_VALUE,
8578     /**
8579      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8580      */
8581     minLengthText : "The minimum length for this field is {0}",
8582     /**
8583      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8584      */
8585     maxLengthText : "The maximum length for this field is {0}",
8586   
8587     
8588     /**
8589      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8590      * If available, this function will be called only after the basic validators all return true, and will be passed the
8591      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8592      */
8593     validator : null,
8594     /**
8595      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8596      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8597      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8598      */
8599     regex : null,
8600     /**
8601      * @cfg {String} regexText -- Depricated - use Invalid Text
8602      */
8603     regexText : "",
8604     
8605     /**
8606      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8607      */
8608     invalidText : "",
8609     
8610     
8611     
8612     autocomplete: false,
8613     
8614     
8615     fieldLabel : '',
8616     inputType : 'text',
8617     
8618     name : false,
8619     placeholder: false,
8620     before : false,
8621     after : false,
8622     size : false,
8623     hasFocus : false,
8624     preventMark: false,
8625     isFormField : true,
8626     value : '',
8627     labelWidth : 2,
8628     labelAlign : false,
8629     readOnly : false,
8630     align : false,
8631     formatedValue : false,
8632     forceFeedback : false,
8633     
8634     indicatorpos : 'left',
8635     
8636     labellg : 0,
8637     labelmd : 0,
8638     labelsm : 0,
8639     labelxs : 0,
8640     
8641     parentLabelAlign : function()
8642     {
8643         var parent = this;
8644         while (parent.parent()) {
8645             parent = parent.parent();
8646             if (typeof(parent.labelAlign) !='undefined') {
8647                 return parent.labelAlign;
8648             }
8649         }
8650         return 'left';
8651         
8652     },
8653     
8654     getAutoCreate : function()
8655     {
8656         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8657         
8658         var id = Roo.id();
8659         
8660         var cfg = {};
8661         
8662         if(this.inputType != 'hidden'){
8663             cfg.cls = 'form-group' //input-group
8664         }
8665         
8666         var input =  {
8667             tag: 'input',
8668             id : id,
8669             type : this.inputType,
8670             value : this.value,
8671             cls : 'form-control',
8672             placeholder : this.placeholder || '',
8673             autocomplete : this.autocomplete || 'new-password'
8674         };
8675         
8676         if(this.align){
8677             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8678         }
8679         
8680         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8681             input.maxLength = this.maxLength;
8682         }
8683         
8684         if (this.disabled) {
8685             input.disabled=true;
8686         }
8687         
8688         if (this.readOnly) {
8689             input.readonly=true;
8690         }
8691         
8692         if (this.name) {
8693             input.name = this.name;
8694         }
8695         
8696         if (this.size) {
8697             input.cls += ' input-' + this.size;
8698         }
8699         
8700         var settings=this;
8701         ['xs','sm','md','lg'].map(function(size){
8702             if (settings[size]) {
8703                 cfg.cls += ' col-' + size + '-' + settings[size];
8704             }
8705         });
8706         
8707         var inputblock = input;
8708         
8709         var feedback = {
8710             tag: 'span',
8711             cls: 'glyphicon form-control-feedback'
8712         };
8713             
8714         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8715             
8716             inputblock = {
8717                 cls : 'has-feedback',
8718                 cn :  [
8719                     input,
8720                     feedback
8721                 ] 
8722             };  
8723         }
8724         
8725         if (this.before || this.after) {
8726             
8727             inputblock = {
8728                 cls : 'input-group',
8729                 cn :  [] 
8730             };
8731             
8732             if (this.before && typeof(this.before) == 'string') {
8733                 
8734                 inputblock.cn.push({
8735                     tag :'span',
8736                     cls : 'roo-input-before input-group-addon',
8737                     html : this.before
8738                 });
8739             }
8740             if (this.before && typeof(this.before) == 'object') {
8741                 this.before = Roo.factory(this.before);
8742                 
8743                 inputblock.cn.push({
8744                     tag :'span',
8745                     cls : 'roo-input-before input-group-' +
8746                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8747                 });
8748             }
8749             
8750             inputblock.cn.push(input);
8751             
8752             if (this.after && typeof(this.after) == 'string') {
8753                 inputblock.cn.push({
8754                     tag :'span',
8755                     cls : 'roo-input-after input-group-addon',
8756                     html : this.after
8757                 });
8758             }
8759             if (this.after && typeof(this.after) == 'object') {
8760                 this.after = Roo.factory(this.after);
8761                 
8762                 inputblock.cn.push({
8763                     tag :'span',
8764                     cls : 'roo-input-after input-group-' +
8765                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8766                 });
8767             }
8768             
8769             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8770                 inputblock.cls += ' has-feedback';
8771                 inputblock.cn.push(feedback);
8772             }
8773         };
8774         
8775         if (align ==='left' && this.fieldLabel.length) {
8776             
8777             cfg.cls += ' roo-form-group-label-left';
8778             
8779             cfg.cn = [
8780                 {
8781                     tag : 'i',
8782                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8783                     tooltip : 'This field is required'
8784                 },
8785                 {
8786                     tag: 'label',
8787                     'for' :  id,
8788                     cls : 'control-label',
8789                     html : this.fieldLabel
8790
8791                 },
8792                 {
8793                     cls : "", 
8794                     cn: [
8795                         inputblock
8796                     ]
8797                 }
8798             ];
8799             
8800             var labelCfg = cfg.cn[1];
8801             var contentCfg = cfg.cn[2];
8802             
8803             if(this.indicatorpos == 'right'){
8804                 cfg.cn = [
8805                     {
8806                         tag: 'label',
8807                         'for' :  id,
8808                         cls : 'control-label',
8809                         cn : [
8810                             {
8811                                 tag : 'span',
8812                                 html : this.fieldLabel
8813                             },
8814                             {
8815                                 tag : 'i',
8816                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8817                                 tooltip : 'This field is required'
8818                             }
8819                         ]
8820                     },
8821                     {
8822                         cls : "",
8823                         cn: [
8824                             inputblock
8825                         ]
8826                     }
8827
8828                 ];
8829                 
8830                 labelCfg = cfg.cn[0];
8831                 contentCfg = cfg.cn[1];
8832             
8833             }
8834             
8835             if(this.labelWidth > 12){
8836                 labelCfg.style = "width: " + this.labelWidth + 'px';
8837             }
8838             
8839             if(this.labelWidth < 13 && this.labelmd == 0){
8840                 this.labelmd = this.labelWidth;
8841             }
8842             
8843             if(this.labellg > 0){
8844                 labelCfg.cls += ' col-lg-' + this.labellg;
8845                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
8846             }
8847             
8848             if(this.labelmd > 0){
8849                 labelCfg.cls += ' col-md-' + this.labelmd;
8850                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
8851             }
8852             
8853             if(this.labelsm > 0){
8854                 labelCfg.cls += ' col-sm-' + this.labelsm;
8855                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
8856             }
8857             
8858             if(this.labelxs > 0){
8859                 labelCfg.cls += ' col-xs-' + this.labelxs;
8860                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
8861             }
8862             
8863             
8864         } else if ( this.fieldLabel.length) {
8865                 
8866             cfg.cn = [
8867                 {
8868                     tag : 'i',
8869                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8870                     tooltip : 'This field is required'
8871                 },
8872                 {
8873                     tag: 'label',
8874                    //cls : 'input-group-addon',
8875                     html : this.fieldLabel
8876
8877                 },
8878
8879                inputblock
8880
8881            ];
8882            
8883            if(this.indicatorpos == 'right'){
8884                 
8885                 cfg.cn = [
8886                     {
8887                         tag: 'label',
8888                        //cls : 'input-group-addon',
8889                         html : this.fieldLabel
8890
8891                     },
8892                     {
8893                         tag : 'i',
8894                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8895                         tooltip : 'This field is required'
8896                     },
8897
8898                    inputblock
8899
8900                ];
8901
8902             }
8903
8904         } else {
8905             
8906             cfg.cn = [
8907
8908                     inputblock
8909
8910             ];
8911                 
8912                 
8913         };
8914         
8915         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8916            cfg.cls += ' navbar-form';
8917         }
8918         
8919         if (this.parentType === 'NavGroup') {
8920            cfg.cls += ' navbar-form';
8921            cfg.tag = 'li';
8922         }
8923         
8924         return cfg;
8925         
8926     },
8927     /**
8928      * return the real input element.
8929      */
8930     inputEl: function ()
8931     {
8932         return this.el.select('input.form-control',true).first();
8933     },
8934     
8935     tooltipEl : function()
8936     {
8937         return this.inputEl();
8938     },
8939     
8940     indicatorEl : function()
8941     {
8942         var indicator = this.el.select('i.roo-required-indicator',true).first();
8943         
8944         if(!indicator){
8945             return false;
8946         }
8947         
8948         return indicator;
8949         
8950     },
8951     
8952     setDisabled : function(v)
8953     {
8954         var i  = this.inputEl().dom;
8955         if (!v) {
8956             i.removeAttribute('disabled');
8957             return;
8958             
8959         }
8960         i.setAttribute('disabled','true');
8961     },
8962     initEvents : function()
8963     {
8964           
8965         this.inputEl().on("keydown" , this.fireKey,  this);
8966         this.inputEl().on("focus", this.onFocus,  this);
8967         this.inputEl().on("blur", this.onBlur,  this);
8968         
8969         this.inputEl().relayEvent('keyup', this);
8970         
8971         this.indicator = this.indicatorEl();
8972         
8973         if(this.indicator){
8974             this.indicator.addClass('invisible');
8975             
8976         }
8977  
8978         // reference to original value for reset
8979         this.originalValue = this.getValue();
8980         //Roo.form.TextField.superclass.initEvents.call(this);
8981         if(this.validationEvent == 'keyup'){
8982             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8983             this.inputEl().on('keyup', this.filterValidation, this);
8984         }
8985         else if(this.validationEvent !== false){
8986             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8987         }
8988         
8989         if(this.selectOnFocus){
8990             this.on("focus", this.preFocus, this);
8991             
8992         }
8993         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8994             this.inputEl().on("keypress", this.filterKeys, this);
8995         } else {
8996             this.inputEl().relayEvent('keypress', this);
8997         }
8998        /* if(this.grow){
8999             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
9000             this.el.on("click", this.autoSize,  this);
9001         }
9002         */
9003         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
9004             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
9005         }
9006         
9007         if (typeof(this.before) == 'object') {
9008             this.before.render(this.el.select('.roo-input-before',true).first());
9009         }
9010         if (typeof(this.after) == 'object') {
9011             this.after.render(this.el.select('.roo-input-after',true).first());
9012         }
9013         
9014         
9015     },
9016     filterValidation : function(e){
9017         if(!e.isNavKeyPress()){
9018             this.validationTask.delay(this.validationDelay);
9019         }
9020     },
9021      /**
9022      * Validates the field value
9023      * @return {Boolean} True if the value is valid, else false
9024      */
9025     validate : function(){
9026         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9027         if(this.disabled || this.validateValue(this.getRawValue())){
9028             this.markValid();
9029             return true;
9030         }
9031         
9032         this.markInvalid();
9033         return false;
9034     },
9035     
9036     
9037     /**
9038      * Validates a value according to the field's validation rules and marks the field as invalid
9039      * if the validation fails
9040      * @param {Mixed} value The value to validate
9041      * @return {Boolean} True if the value is valid, else false
9042      */
9043     validateValue : function(value){
9044         if(value.length < 1)  { // if it's blank
9045             if(this.allowBlank){
9046                 return true;
9047             }            
9048             return this.inputEl().hasClass('hide') ? true : false;
9049         }
9050         
9051         if(value.length < this.minLength){
9052             return false;
9053         }
9054         if(value.length > this.maxLength){
9055             return false;
9056         }
9057         if(this.vtype){
9058             var vt = Roo.form.VTypes;
9059             if(!vt[this.vtype](value, this)){
9060                 return false;
9061             }
9062         }
9063         if(typeof this.validator == "function"){
9064             var msg = this.validator(value);
9065             if(msg !== true){
9066                 return false;
9067             }
9068             if (typeof(msg) == 'string') {
9069                 this.invalidText = msg;
9070             }
9071         }
9072         
9073         if(this.regex && !this.regex.test(value)){
9074             return false;
9075         }
9076         
9077         return true;
9078     },
9079
9080     
9081     
9082      // private
9083     fireKey : function(e){
9084         //Roo.log('field ' + e.getKey());
9085         if(e.isNavKeyPress()){
9086             this.fireEvent("specialkey", this, e);
9087         }
9088     },
9089     focus : function (selectText){
9090         if(this.rendered){
9091             this.inputEl().focus();
9092             if(selectText === true){
9093                 this.inputEl().dom.select();
9094             }
9095         }
9096         return this;
9097     } ,
9098     
9099     onFocus : function(){
9100         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9101            // this.el.addClass(this.focusClass);
9102         }
9103         if(!this.hasFocus){
9104             this.hasFocus = true;
9105             this.startValue = this.getValue();
9106             this.fireEvent("focus", this);
9107         }
9108     },
9109     
9110     beforeBlur : Roo.emptyFn,
9111
9112     
9113     // private
9114     onBlur : function(){
9115         this.beforeBlur();
9116         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9117             //this.el.removeClass(this.focusClass);
9118         }
9119         this.hasFocus = false;
9120         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9121             this.validate();
9122         }
9123         var v = this.getValue();
9124         if(String(v) !== String(this.startValue)){
9125             this.fireEvent('change', this, v, this.startValue);
9126         }
9127         this.fireEvent("blur", this);
9128     },
9129     
9130     /**
9131      * Resets the current field value to the originally loaded value and clears any validation messages
9132      */
9133     reset : function(){
9134         this.setValue(this.originalValue);
9135         this.validate();
9136     },
9137      /**
9138      * Returns the name of the field
9139      * @return {Mixed} name The name field
9140      */
9141     getName: function(){
9142         return this.name;
9143     },
9144      /**
9145      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9146      * @return {Mixed} value The field value
9147      */
9148     getValue : function(){
9149         
9150         var v = this.inputEl().getValue();
9151         
9152         return v;
9153     },
9154     /**
9155      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9156      * @return {Mixed} value The field value
9157      */
9158     getRawValue : function(){
9159         var v = this.inputEl().getValue();
9160         
9161         return v;
9162     },
9163     
9164     /**
9165      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9166      * @param {Mixed} value The value to set
9167      */
9168     setRawValue : function(v){
9169         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9170     },
9171     
9172     selectText : function(start, end){
9173         var v = this.getRawValue();
9174         if(v.length > 0){
9175             start = start === undefined ? 0 : start;
9176             end = end === undefined ? v.length : end;
9177             var d = this.inputEl().dom;
9178             if(d.setSelectionRange){
9179                 d.setSelectionRange(start, end);
9180             }else if(d.createTextRange){
9181                 var range = d.createTextRange();
9182                 range.moveStart("character", start);
9183                 range.moveEnd("character", v.length-end);
9184                 range.select();
9185             }
9186         }
9187     },
9188     
9189     /**
9190      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9191      * @param {Mixed} value The value to set
9192      */
9193     setValue : function(v){
9194         this.value = v;
9195         if(this.rendered){
9196             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9197             this.validate();
9198         }
9199     },
9200     
9201     /*
9202     processValue : function(value){
9203         if(this.stripCharsRe){
9204             var newValue = value.replace(this.stripCharsRe, '');
9205             if(newValue !== value){
9206                 this.setRawValue(newValue);
9207                 return newValue;
9208             }
9209         }
9210         return value;
9211     },
9212   */
9213     preFocus : function(){
9214         
9215         if(this.selectOnFocus){
9216             this.inputEl().dom.select();
9217         }
9218     },
9219     filterKeys : function(e){
9220         var k = e.getKey();
9221         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9222             return;
9223         }
9224         var c = e.getCharCode(), cc = String.fromCharCode(c);
9225         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9226             return;
9227         }
9228         if(!this.maskRe.test(cc)){
9229             e.stopEvent();
9230         }
9231     },
9232      /**
9233      * Clear any invalid styles/messages for this field
9234      */
9235     clearInvalid : function(){
9236         
9237         if(!this.el || this.preventMark){ // not rendered
9238             return;
9239         }
9240         
9241      
9242         this.el.removeClass(this.invalidClass);
9243         
9244         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9245             
9246             var feedback = this.el.select('.form-control-feedback', true).first();
9247             
9248             if(feedback){
9249                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9250             }
9251             
9252         }
9253         
9254         this.fireEvent('valid', this);
9255     },
9256     
9257      /**
9258      * Mark this field as valid
9259      */
9260     markValid : function()
9261     {
9262         if(!this.el  || this.preventMark){ // not rendered...
9263             return;
9264         }
9265         
9266         this.el.removeClass([this.invalidClass, this.validClass]);
9267         
9268         var feedback = this.el.select('.form-control-feedback', true).first();
9269             
9270         if(feedback){
9271             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9272         }
9273         
9274         if(this.indicator){
9275             this.indicator.removeClass('visible');
9276             this.indicator.addClass('invisible');
9277         }
9278         
9279         if(this.disabled){
9280             return;
9281         }
9282         
9283         if(this.allowBlank && !this.getRawValue().length){
9284             return;
9285         }
9286         
9287         this.el.addClass(this.validClass);
9288         
9289         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9290             
9291             var feedback = this.el.select('.form-control-feedback', true).first();
9292             
9293             if(feedback){
9294                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9295                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9296             }
9297             
9298         }
9299         
9300         this.fireEvent('valid', this);
9301     },
9302     
9303      /**
9304      * Mark this field as invalid
9305      * @param {String} msg The validation message
9306      */
9307     markInvalid : function(msg)
9308     {
9309         if(!this.el  || this.preventMark){ // not rendered
9310             return;
9311         }
9312         
9313         this.el.removeClass([this.invalidClass, this.validClass]);
9314         
9315         var feedback = this.el.select('.form-control-feedback', true).first();
9316             
9317         if(feedback){
9318             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9319         }
9320
9321         if(this.disabled){
9322             return;
9323         }
9324         
9325         if(this.allowBlank && !this.getRawValue().length){
9326             return;
9327         }
9328         
9329         if(this.indicator){
9330             this.indicator.removeClass('invisible');
9331             this.indicator.addClass('visible');
9332         }
9333         
9334         this.el.addClass(this.invalidClass);
9335         
9336         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9337             
9338             var feedback = this.el.select('.form-control-feedback', true).first();
9339             
9340             if(feedback){
9341                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9342                 
9343                 if(this.getValue().length || this.forceFeedback){
9344                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9345                 }
9346                 
9347             }
9348             
9349         }
9350         
9351         this.fireEvent('invalid', this, msg);
9352     },
9353     // private
9354     SafariOnKeyDown : function(event)
9355     {
9356         // this is a workaround for a password hang bug on chrome/ webkit.
9357         if (this.inputEl().dom.type != 'password') {
9358             return;
9359         }
9360         
9361         var isSelectAll = false;
9362         
9363         if(this.inputEl().dom.selectionEnd > 0){
9364             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9365         }
9366         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9367             event.preventDefault();
9368             this.setValue('');
9369             return;
9370         }
9371         
9372         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9373             
9374             event.preventDefault();
9375             // this is very hacky as keydown always get's upper case.
9376             //
9377             var cc = String.fromCharCode(event.getCharCode());
9378             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9379             
9380         }
9381     },
9382     adjustWidth : function(tag, w){
9383         tag = tag.toLowerCase();
9384         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9385             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9386                 if(tag == 'input'){
9387                     return w + 2;
9388                 }
9389                 if(tag == 'textarea'){
9390                     return w-2;
9391                 }
9392             }else if(Roo.isOpera){
9393                 if(tag == 'input'){
9394                     return w + 2;
9395                 }
9396                 if(tag == 'textarea'){
9397                     return w-2;
9398                 }
9399             }
9400         }
9401         return w;
9402     },
9403     
9404     setFieldLabel : function(v)
9405     {
9406         if(!this.rendered){
9407             return;
9408         }
9409         
9410         if(this.indicator){
9411             var ar = this.el.select('label > span',true);
9412             
9413             if (ar.elements.length) {
9414                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9415                 this.fieldLabel = v;
9416                 return;
9417             }
9418             
9419             var br = this.el.select('label',true);
9420             
9421             if(br.elements.length) {
9422                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9423                 this.fieldLabel = v;
9424                 return;
9425             }
9426             
9427             Roo.log('Cannot Found any of label > span || label in input');
9428             return;
9429         }
9430         
9431         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9432         this.fieldLabel = v;
9433         
9434         
9435     }
9436 });
9437
9438  
9439 /*
9440  * - LGPL
9441  *
9442  * Input
9443  * 
9444  */
9445
9446 /**
9447  * @class Roo.bootstrap.TextArea
9448  * @extends Roo.bootstrap.Input
9449  * Bootstrap TextArea class
9450  * @cfg {Number} cols Specifies the visible width of a text area
9451  * @cfg {Number} rows Specifies the visible number of lines in a text area
9452  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9453  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9454  * @cfg {string} html text
9455  * 
9456  * @constructor
9457  * Create a new TextArea
9458  * @param {Object} config The config object
9459  */
9460
9461 Roo.bootstrap.TextArea = function(config){
9462     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9463    
9464 };
9465
9466 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9467      
9468     cols : false,
9469     rows : 5,
9470     readOnly : false,
9471     warp : 'soft',
9472     resize : false,
9473     value: false,
9474     html: false,
9475     
9476     getAutoCreate : function(){
9477         
9478         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9479         
9480         var id = Roo.id();
9481         
9482         var cfg = {};
9483         
9484         if(this.inputType != 'hidden'){
9485             cfg.cls = 'form-group' //input-group
9486         }
9487         
9488         var input =  {
9489             tag: 'textarea',
9490             id : id,
9491             warp : this.warp,
9492             rows : this.rows,
9493             value : this.value || '',
9494             html: this.html || '',
9495             cls : 'form-control',
9496             placeholder : this.placeholder || '' 
9497             
9498         };
9499         
9500         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9501             input.maxLength = this.maxLength;
9502         }
9503         
9504         if(this.resize){
9505             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9506         }
9507         
9508         if(this.cols){
9509             input.cols = this.cols;
9510         }
9511         
9512         if (this.readOnly) {
9513             input.readonly = true;
9514         }
9515         
9516         if (this.name) {
9517             input.name = this.name;
9518         }
9519         
9520         if (this.size) {
9521             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9522         }
9523         
9524         var settings=this;
9525         ['xs','sm','md','lg'].map(function(size){
9526             if (settings[size]) {
9527                 cfg.cls += ' col-' + size + '-' + settings[size];
9528             }
9529         });
9530         
9531         var inputblock = input;
9532         
9533         if(this.hasFeedback && !this.allowBlank){
9534             
9535             var feedback = {
9536                 tag: 'span',
9537                 cls: 'glyphicon form-control-feedback'
9538             };
9539
9540             inputblock = {
9541                 cls : 'has-feedback',
9542                 cn :  [
9543                     input,
9544                     feedback
9545                 ] 
9546             };  
9547         }
9548         
9549         
9550         if (this.before || this.after) {
9551             
9552             inputblock = {
9553                 cls : 'input-group',
9554                 cn :  [] 
9555             };
9556             if (this.before) {
9557                 inputblock.cn.push({
9558                     tag :'span',
9559                     cls : 'input-group-addon',
9560                     html : this.before
9561                 });
9562             }
9563             
9564             inputblock.cn.push(input);
9565             
9566             if(this.hasFeedback && !this.allowBlank){
9567                 inputblock.cls += ' has-feedback';
9568                 inputblock.cn.push(feedback);
9569             }
9570             
9571             if (this.after) {
9572                 inputblock.cn.push({
9573                     tag :'span',
9574                     cls : 'input-group-addon',
9575                     html : this.after
9576                 });
9577             }
9578             
9579         }
9580         
9581         if (align ==='left' && this.fieldLabel.length) {
9582             cfg.cn = [
9583                 {
9584                     tag: 'label',
9585                     'for' :  id,
9586                     cls : 'control-label',
9587                     html : this.fieldLabel
9588                 },
9589                 {
9590                     cls : "",
9591                     cn: [
9592                         inputblock
9593                     ]
9594                 }
9595
9596             ];
9597             
9598             if(this.labelWidth > 12){
9599                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9600             }
9601
9602             if(this.labelWidth < 13 && this.labelmd == 0){
9603                 this.labelmd = this.labelWidth;
9604             }
9605
9606             if(this.labellg > 0){
9607                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9608                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9609             }
9610
9611             if(this.labelmd > 0){
9612                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9613                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9614             }
9615
9616             if(this.labelsm > 0){
9617                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9618                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9619             }
9620
9621             if(this.labelxs > 0){
9622                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9623                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9624             }
9625             
9626         } else if ( this.fieldLabel.length) {
9627             cfg.cn = [
9628
9629                {
9630                    tag: 'label',
9631                    //cls : 'input-group-addon',
9632                    html : this.fieldLabel
9633
9634                },
9635
9636                inputblock
9637
9638            ];
9639
9640         } else {
9641
9642             cfg.cn = [
9643
9644                 inputblock
9645
9646             ];
9647                 
9648         }
9649         
9650         if (this.disabled) {
9651             input.disabled=true;
9652         }
9653         
9654         return cfg;
9655         
9656     },
9657     /**
9658      * return the real textarea element.
9659      */
9660     inputEl: function ()
9661     {
9662         return this.el.select('textarea.form-control',true).first();
9663     },
9664     
9665     /**
9666      * Clear any invalid styles/messages for this field
9667      */
9668     clearInvalid : function()
9669     {
9670         
9671         if(!this.el || this.preventMark){ // not rendered
9672             return;
9673         }
9674         
9675         var label = this.el.select('label', true).first();
9676         var icon = this.el.select('i.fa-star', true).first();
9677         
9678         if(label && icon){
9679             icon.remove();
9680         }
9681         
9682         this.el.removeClass(this.invalidClass);
9683         
9684         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9685             
9686             var feedback = this.el.select('.form-control-feedback', true).first();
9687             
9688             if(feedback){
9689                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9690             }
9691             
9692         }
9693         
9694         this.fireEvent('valid', this);
9695     },
9696     
9697      /**
9698      * Mark this field as valid
9699      */
9700     markValid : function()
9701     {
9702         if(!this.el  || this.preventMark){ // not rendered
9703             return;
9704         }
9705         
9706         this.el.removeClass([this.invalidClass, this.validClass]);
9707         
9708         var feedback = this.el.select('.form-control-feedback', true).first();
9709             
9710         if(feedback){
9711             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9712         }
9713
9714         if(this.disabled || this.allowBlank){
9715             return;
9716         }
9717         
9718         var label = this.el.select('label', true).first();
9719         var icon = this.el.select('i.fa-star', true).first();
9720         
9721         if(label && icon){
9722             icon.remove();
9723         }
9724         
9725         this.el.addClass(this.validClass);
9726         
9727         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9728             
9729             var feedback = this.el.select('.form-control-feedback', true).first();
9730             
9731             if(feedback){
9732                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9733                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9734             }
9735             
9736         }
9737         
9738         this.fireEvent('valid', this);
9739     },
9740     
9741      /**
9742      * Mark this field as invalid
9743      * @param {String} msg The validation message
9744      */
9745     markInvalid : function(msg)
9746     {
9747         if(!this.el  || this.preventMark){ // not rendered
9748             return;
9749         }
9750         
9751         this.el.removeClass([this.invalidClass, this.validClass]);
9752         
9753         var feedback = this.el.select('.form-control-feedback', true).first();
9754             
9755         if(feedback){
9756             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9757         }
9758
9759         if(this.disabled || this.allowBlank){
9760             return;
9761         }
9762         
9763         var label = this.el.select('label', true).first();
9764         var icon = this.el.select('i.fa-star', true).first();
9765         
9766         if(!this.getValue().length && label && !icon){
9767             this.el.createChild({
9768                 tag : 'i',
9769                 cls : 'text-danger fa fa-lg fa-star',
9770                 tooltip : 'This field is required',
9771                 style : 'margin-right:5px;'
9772             }, label, true);
9773         }
9774
9775         this.el.addClass(this.invalidClass);
9776         
9777         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9778             
9779             var feedback = this.el.select('.form-control-feedback', true).first();
9780             
9781             if(feedback){
9782                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9783                 
9784                 if(this.getValue().length || this.forceFeedback){
9785                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9786                 }
9787                 
9788             }
9789             
9790         }
9791         
9792         this.fireEvent('invalid', this, msg);
9793     }
9794 });
9795
9796  
9797 /*
9798  * - LGPL
9799  *
9800  * trigger field - base class for combo..
9801  * 
9802  */
9803  
9804 /**
9805  * @class Roo.bootstrap.TriggerField
9806  * @extends Roo.bootstrap.Input
9807  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9808  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9809  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9810  * for which you can provide a custom implementation.  For example:
9811  * <pre><code>
9812 var trigger = new Roo.bootstrap.TriggerField();
9813 trigger.onTriggerClick = myTriggerFn;
9814 trigger.applyTo('my-field');
9815 </code></pre>
9816  *
9817  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9818  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9819  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9820  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9821  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9822
9823  * @constructor
9824  * Create a new TriggerField.
9825  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9826  * to the base TextField)
9827  */
9828 Roo.bootstrap.TriggerField = function(config){
9829     this.mimicing = false;
9830     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9831 };
9832
9833 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9834     /**
9835      * @cfg {String} triggerClass A CSS class to apply to the trigger
9836      */
9837      /**
9838      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9839      */
9840     hideTrigger:false,
9841
9842     /**
9843      * @cfg {Boolean} removable (true|false) special filter default false
9844      */
9845     removable : false,
9846     
9847     /** @cfg {Boolean} grow @hide */
9848     /** @cfg {Number} growMin @hide */
9849     /** @cfg {Number} growMax @hide */
9850
9851     /**
9852      * @hide 
9853      * @method
9854      */
9855     autoSize: Roo.emptyFn,
9856     // private
9857     monitorTab : true,
9858     // private
9859     deferHeight : true,
9860
9861     
9862     actionMode : 'wrap',
9863     
9864     caret : false,
9865     
9866     
9867     getAutoCreate : function(){
9868        
9869         var align = this.labelAlign || this.parentLabelAlign();
9870         
9871         var id = Roo.id();
9872         
9873         var cfg = {
9874             cls: 'form-group' //input-group
9875         };
9876         
9877         
9878         var input =  {
9879             tag: 'input',
9880             id : id,
9881             type : this.inputType,
9882             cls : 'form-control',
9883             autocomplete: 'new-password',
9884             placeholder : this.placeholder || '' 
9885             
9886         };
9887         if (this.name) {
9888             input.name = this.name;
9889         }
9890         if (this.size) {
9891             input.cls += ' input-' + this.size;
9892         }
9893         
9894         if (this.disabled) {
9895             input.disabled=true;
9896         }
9897         
9898         var inputblock = input;
9899         
9900         if(this.hasFeedback && !this.allowBlank){
9901             
9902             var feedback = {
9903                 tag: 'span',
9904                 cls: 'glyphicon form-control-feedback'
9905             };
9906             
9907             if(this.removable && !this.editable && !this.tickable){
9908                 inputblock = {
9909                     cls : 'has-feedback',
9910                     cn :  [
9911                         inputblock,
9912                         {
9913                             tag: 'button',
9914                             html : 'x',
9915                             cls : 'roo-combo-removable-btn close'
9916                         },
9917                         feedback
9918                     ] 
9919                 };
9920             } else {
9921                 inputblock = {
9922                     cls : 'has-feedback',
9923                     cn :  [
9924                         inputblock,
9925                         feedback
9926                     ] 
9927                 };
9928             }
9929
9930         } else {
9931             if(this.removable && !this.editable && !this.tickable){
9932                 inputblock = {
9933                     cls : 'roo-removable',
9934                     cn :  [
9935                         inputblock,
9936                         {
9937                             tag: 'button',
9938                             html : 'x',
9939                             cls : 'roo-combo-removable-btn close'
9940                         }
9941                     ] 
9942                 };
9943             }
9944         }
9945         
9946         if (this.before || this.after) {
9947             
9948             inputblock = {
9949                 cls : 'input-group',
9950                 cn :  [] 
9951             };
9952             if (this.before) {
9953                 inputblock.cn.push({
9954                     tag :'span',
9955                     cls : 'input-group-addon',
9956                     html : this.before
9957                 });
9958             }
9959             
9960             inputblock.cn.push(input);
9961             
9962             if(this.hasFeedback && !this.allowBlank){
9963                 inputblock.cls += ' has-feedback';
9964                 inputblock.cn.push(feedback);
9965             }
9966             
9967             if (this.after) {
9968                 inputblock.cn.push({
9969                     tag :'span',
9970                     cls : 'input-group-addon',
9971                     html : this.after
9972                 });
9973             }
9974             
9975         };
9976         
9977         var box = {
9978             tag: 'div',
9979             cn: [
9980                 {
9981                     tag: 'input',
9982                     type : 'hidden',
9983                     cls: 'form-hidden-field'
9984                 },
9985                 inputblock
9986             ]
9987             
9988         };
9989         
9990         if(this.multiple){
9991             box = {
9992                 tag: 'div',
9993                 cn: [
9994                     {
9995                         tag: 'input',
9996                         type : 'hidden',
9997                         cls: 'form-hidden-field'
9998                     },
9999                     {
10000                         tag: 'ul',
10001                         cls: 'roo-select2-choices',
10002                         cn:[
10003                             {
10004                                 tag: 'li',
10005                                 cls: 'roo-select2-search-field',
10006                                 cn: [
10007
10008                                     inputblock
10009                                 ]
10010                             }
10011                         ]
10012                     }
10013                 ]
10014             }
10015         };
10016         
10017         var combobox = {
10018             cls: 'roo-select2-container input-group',
10019             cn: [
10020                 box
10021 //                {
10022 //                    tag: 'ul',
10023 //                    cls: 'typeahead typeahead-long dropdown-menu',
10024 //                    style: 'display:none'
10025 //                }
10026             ]
10027         };
10028         
10029         if(!this.multiple && this.showToggleBtn){
10030             
10031             var caret = {
10032                         tag: 'span',
10033                         cls: 'caret'
10034              };
10035             if (this.caret != false) {
10036                 caret = {
10037                      tag: 'i',
10038                      cls: 'fa fa-' + this.caret
10039                 };
10040                 
10041             }
10042             
10043             combobox.cn.push({
10044                 tag :'span',
10045                 cls : 'input-group-addon btn dropdown-toggle',
10046                 cn : [
10047                     caret,
10048                     {
10049                         tag: 'span',
10050                         cls: 'combobox-clear',
10051                         cn  : [
10052                             {
10053                                 tag : 'i',
10054                                 cls: 'icon-remove'
10055                             }
10056                         ]
10057                     }
10058                 ]
10059
10060             })
10061         }
10062         
10063         if(this.multiple){
10064             combobox.cls += ' roo-select2-container-multi';
10065         }
10066         
10067         if (align ==='left' && this.fieldLabel.length) {
10068             
10069             cfg.cls += ' roo-form-group-label-left';
10070
10071             cfg.cn = [
10072                 {
10073                     tag : 'i',
10074                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10075                     tooltip : 'This field is required'
10076                 },
10077                 {
10078                     tag: 'label',
10079                     'for' :  id,
10080                     cls : 'control-label',
10081                     html : this.fieldLabel
10082
10083                 },
10084                 {
10085                     cls : "", 
10086                     cn: [
10087                         combobox
10088                     ]
10089                 }
10090
10091             ];
10092             
10093             var labelCfg = cfg.cn[1];
10094             var contentCfg = cfg.cn[2];
10095             
10096             if(this.indicatorpos == 'right'){
10097                 cfg.cn = [
10098                     {
10099                         tag: 'label',
10100                         'for' :  id,
10101                         cls : 'control-label',
10102                         cn : [
10103                             {
10104                                 tag : 'span',
10105                                 html : this.fieldLabel
10106                             },
10107                             {
10108                                 tag : 'i',
10109                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10110                                 tooltip : 'This field is required'
10111                             }
10112                         ]
10113                     },
10114                     {
10115                         cls : "", 
10116                         cn: [
10117                             combobox
10118                         ]
10119                     }
10120
10121                 ];
10122                 
10123                 labelCfg = cfg.cn[0];
10124                 contentCfg = cfg.cn[1];
10125             }
10126             
10127             if(this.labelWidth > 12){
10128                 labelCfg.style = "width: " + this.labelWidth + 'px';
10129             }
10130             
10131             if(this.labelWidth < 13 && this.labelmd == 0){
10132                 this.labelmd = this.labelWidth;
10133             }
10134             
10135             if(this.labellg > 0){
10136                 labelCfg.cls += ' col-lg-' + this.labellg;
10137                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10138             }
10139             
10140             if(this.labelmd > 0){
10141                 labelCfg.cls += ' col-md-' + this.labelmd;
10142                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10143             }
10144             
10145             if(this.labelsm > 0){
10146                 labelCfg.cls += ' col-sm-' + this.labelsm;
10147                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10148             }
10149             
10150             if(this.labelxs > 0){
10151                 labelCfg.cls += ' col-xs-' + this.labelxs;
10152                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10153             }
10154             
10155         } else if ( this.fieldLabel.length) {
10156 //                Roo.log(" label");
10157             cfg.cn = [
10158                 {
10159                    tag : 'i',
10160                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10161                    tooltip : 'This field is required'
10162                },
10163                {
10164                    tag: 'label',
10165                    //cls : 'input-group-addon',
10166                    html : this.fieldLabel
10167
10168                },
10169
10170                combobox
10171
10172             ];
10173             
10174             if(this.indicatorpos == 'right'){
10175                 
10176                 cfg.cn = [
10177                     {
10178                        tag: 'label',
10179                        cn : [
10180                            {
10181                                tag : 'span',
10182                                html : this.fieldLabel
10183                            },
10184                            {
10185                               tag : 'i',
10186                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10187                               tooltip : 'This field is required'
10188                            }
10189                        ]
10190
10191                     },
10192                     combobox
10193
10194                 ];
10195
10196             }
10197
10198         } else {
10199             
10200 //                Roo.log(" no label && no align");
10201                 cfg = combobox
10202                      
10203                 
10204         }
10205         
10206         var settings=this;
10207         ['xs','sm','md','lg'].map(function(size){
10208             if (settings[size]) {
10209                 cfg.cls += ' col-' + size + '-' + settings[size];
10210             }
10211         });
10212         
10213         return cfg;
10214         
10215     },
10216     
10217     
10218     
10219     // private
10220     onResize : function(w, h){
10221 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10222 //        if(typeof w == 'number'){
10223 //            var x = w - this.trigger.getWidth();
10224 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10225 //            this.trigger.setStyle('left', x+'px');
10226 //        }
10227     },
10228
10229     // private
10230     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10231
10232     // private
10233     getResizeEl : function(){
10234         return this.inputEl();
10235     },
10236
10237     // private
10238     getPositionEl : function(){
10239         return this.inputEl();
10240     },
10241
10242     // private
10243     alignErrorIcon : function(){
10244         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10245     },
10246
10247     // private
10248     initEvents : function(){
10249         
10250         this.createList();
10251         
10252         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10253         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10254         if(!this.multiple && this.showToggleBtn){
10255             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10256             if(this.hideTrigger){
10257                 this.trigger.setDisplayed(false);
10258             }
10259             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10260         }
10261         
10262         if(this.multiple){
10263             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10264         }
10265         
10266         if(this.removable && !this.editable && !this.tickable){
10267             var close = this.closeTriggerEl();
10268             
10269             if(close){
10270                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10271                 close.on('click', this.removeBtnClick, this, close);
10272             }
10273         }
10274         
10275         //this.trigger.addClassOnOver('x-form-trigger-over');
10276         //this.trigger.addClassOnClick('x-form-trigger-click');
10277         
10278         //if(!this.width){
10279         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10280         //}
10281     },
10282     
10283     closeTriggerEl : function()
10284     {
10285         var close = this.el.select('.roo-combo-removable-btn', true).first();
10286         return close ? close : false;
10287     },
10288     
10289     removeBtnClick : function(e, h, el)
10290     {
10291         e.preventDefault();
10292         
10293         if(this.fireEvent("remove", this) !== false){
10294             this.reset();
10295             this.fireEvent("afterremove", this)
10296         }
10297     },
10298     
10299     createList : function()
10300     {
10301         this.list = Roo.get(document.body).createChild({
10302             tag: 'ul',
10303             cls: 'typeahead typeahead-long dropdown-menu',
10304             style: 'display:none'
10305         });
10306         
10307         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10308         
10309     },
10310
10311     // private
10312     initTrigger : function(){
10313        
10314     },
10315
10316     // private
10317     onDestroy : function(){
10318         if(this.trigger){
10319             this.trigger.removeAllListeners();
10320           //  this.trigger.remove();
10321         }
10322         //if(this.wrap){
10323         //    this.wrap.remove();
10324         //}
10325         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10326     },
10327
10328     // private
10329     onFocus : function(){
10330         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10331         /*
10332         if(!this.mimicing){
10333             this.wrap.addClass('x-trigger-wrap-focus');
10334             this.mimicing = true;
10335             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10336             if(this.monitorTab){
10337                 this.el.on("keydown", this.checkTab, this);
10338             }
10339         }
10340         */
10341     },
10342
10343     // private
10344     checkTab : function(e){
10345         if(e.getKey() == e.TAB){
10346             this.triggerBlur();
10347         }
10348     },
10349
10350     // private
10351     onBlur : function(){
10352         // do nothing
10353     },
10354
10355     // private
10356     mimicBlur : function(e, t){
10357         /*
10358         if(!this.wrap.contains(t) && this.validateBlur()){
10359             this.triggerBlur();
10360         }
10361         */
10362     },
10363
10364     // private
10365     triggerBlur : function(){
10366         this.mimicing = false;
10367         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10368         if(this.monitorTab){
10369             this.el.un("keydown", this.checkTab, this);
10370         }
10371         //this.wrap.removeClass('x-trigger-wrap-focus');
10372         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10373     },
10374
10375     // private
10376     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10377     validateBlur : function(e, t){
10378         return true;
10379     },
10380
10381     // private
10382     onDisable : function(){
10383         this.inputEl().dom.disabled = true;
10384         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10385         //if(this.wrap){
10386         //    this.wrap.addClass('x-item-disabled');
10387         //}
10388     },
10389
10390     // private
10391     onEnable : function(){
10392         this.inputEl().dom.disabled = false;
10393         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10394         //if(this.wrap){
10395         //    this.el.removeClass('x-item-disabled');
10396         //}
10397     },
10398
10399     // private
10400     onShow : function(){
10401         var ae = this.getActionEl();
10402         
10403         if(ae){
10404             ae.dom.style.display = '';
10405             ae.dom.style.visibility = 'visible';
10406         }
10407     },
10408
10409     // private
10410     
10411     onHide : function(){
10412         var ae = this.getActionEl();
10413         ae.dom.style.display = 'none';
10414     },
10415
10416     /**
10417      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10418      * by an implementing function.
10419      * @method
10420      * @param {EventObject} e
10421      */
10422     onTriggerClick : Roo.emptyFn
10423 });
10424  /*
10425  * Based on:
10426  * Ext JS Library 1.1.1
10427  * Copyright(c) 2006-2007, Ext JS, LLC.
10428  *
10429  * Originally Released Under LGPL - original licence link has changed is not relivant.
10430  *
10431  * Fork - LGPL
10432  * <script type="text/javascript">
10433  */
10434
10435
10436 /**
10437  * @class Roo.data.SortTypes
10438  * @singleton
10439  * Defines the default sorting (casting?) comparison functions used when sorting data.
10440  */
10441 Roo.data.SortTypes = {
10442     /**
10443      * Default sort that does nothing
10444      * @param {Mixed} s The value being converted
10445      * @return {Mixed} The comparison value
10446      */
10447     none : function(s){
10448         return s;
10449     },
10450     
10451     /**
10452      * The regular expression used to strip tags
10453      * @type {RegExp}
10454      * @property
10455      */
10456     stripTagsRE : /<\/?[^>]+>/gi,
10457     
10458     /**
10459      * Strips all HTML tags to sort on text only
10460      * @param {Mixed} s The value being converted
10461      * @return {String} The comparison value
10462      */
10463     asText : function(s){
10464         return String(s).replace(this.stripTagsRE, "");
10465     },
10466     
10467     /**
10468      * Strips all HTML tags to sort on text only - Case insensitive
10469      * @param {Mixed} s The value being converted
10470      * @return {String} The comparison value
10471      */
10472     asUCText : function(s){
10473         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10474     },
10475     
10476     /**
10477      * Case insensitive string
10478      * @param {Mixed} s The value being converted
10479      * @return {String} The comparison value
10480      */
10481     asUCString : function(s) {
10482         return String(s).toUpperCase();
10483     },
10484     
10485     /**
10486      * Date sorting
10487      * @param {Mixed} s The value being converted
10488      * @return {Number} The comparison value
10489      */
10490     asDate : function(s) {
10491         if(!s){
10492             return 0;
10493         }
10494         if(s instanceof Date){
10495             return s.getTime();
10496         }
10497         return Date.parse(String(s));
10498     },
10499     
10500     /**
10501      * Float sorting
10502      * @param {Mixed} s The value being converted
10503      * @return {Float} The comparison value
10504      */
10505     asFloat : function(s) {
10506         var val = parseFloat(String(s).replace(/,/g, ""));
10507         if(isNaN(val)) {
10508             val = 0;
10509         }
10510         return val;
10511     },
10512     
10513     /**
10514      * Integer sorting
10515      * @param {Mixed} s The value being converted
10516      * @return {Number} The comparison value
10517      */
10518     asInt : function(s) {
10519         var val = parseInt(String(s).replace(/,/g, ""));
10520         if(isNaN(val)) {
10521             val = 0;
10522         }
10523         return val;
10524     }
10525 };/*
10526  * Based on:
10527  * Ext JS Library 1.1.1
10528  * Copyright(c) 2006-2007, Ext JS, LLC.
10529  *
10530  * Originally Released Under LGPL - original licence link has changed is not relivant.
10531  *
10532  * Fork - LGPL
10533  * <script type="text/javascript">
10534  */
10535
10536 /**
10537 * @class Roo.data.Record
10538  * Instances of this class encapsulate both record <em>definition</em> information, and record
10539  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10540  * to access Records cached in an {@link Roo.data.Store} object.<br>
10541  * <p>
10542  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10543  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10544  * objects.<br>
10545  * <p>
10546  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10547  * @constructor
10548  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10549  * {@link #create}. The parameters are the same.
10550  * @param {Array} data An associative Array of data values keyed by the field name.
10551  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10552  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10553  * not specified an integer id is generated.
10554  */
10555 Roo.data.Record = function(data, id){
10556     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10557     this.data = data;
10558 };
10559
10560 /**
10561  * Generate a constructor for a specific record layout.
10562  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10563  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10564  * Each field definition object may contain the following properties: <ul>
10565  * <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,
10566  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10567  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10568  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10569  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10570  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10571  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10572  * this may be omitted.</p></li>
10573  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10574  * <ul><li>auto (Default, implies no conversion)</li>
10575  * <li>string</li>
10576  * <li>int</li>
10577  * <li>float</li>
10578  * <li>boolean</li>
10579  * <li>date</li></ul></p></li>
10580  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10581  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10582  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10583  * by the Reader into an object that will be stored in the Record. It is passed the
10584  * following parameters:<ul>
10585  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10586  * </ul></p></li>
10587  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10588  * </ul>
10589  * <br>usage:<br><pre><code>
10590 var TopicRecord = Roo.data.Record.create(
10591     {name: 'title', mapping: 'topic_title'},
10592     {name: 'author', mapping: 'username'},
10593     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10594     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10595     {name: 'lastPoster', mapping: 'user2'},
10596     {name: 'excerpt', mapping: 'post_text'}
10597 );
10598
10599 var myNewRecord = new TopicRecord({
10600     title: 'Do my job please',
10601     author: 'noobie',
10602     totalPosts: 1,
10603     lastPost: new Date(),
10604     lastPoster: 'Animal',
10605     excerpt: 'No way dude!'
10606 });
10607 myStore.add(myNewRecord);
10608 </code></pre>
10609  * @method create
10610  * @static
10611  */
10612 Roo.data.Record.create = function(o){
10613     var f = function(){
10614         f.superclass.constructor.apply(this, arguments);
10615     };
10616     Roo.extend(f, Roo.data.Record);
10617     var p = f.prototype;
10618     p.fields = new Roo.util.MixedCollection(false, function(field){
10619         return field.name;
10620     });
10621     for(var i = 0, len = o.length; i < len; i++){
10622         p.fields.add(new Roo.data.Field(o[i]));
10623     }
10624     f.getField = function(name){
10625         return p.fields.get(name);  
10626     };
10627     return f;
10628 };
10629
10630 Roo.data.Record.AUTO_ID = 1000;
10631 Roo.data.Record.EDIT = 'edit';
10632 Roo.data.Record.REJECT = 'reject';
10633 Roo.data.Record.COMMIT = 'commit';
10634
10635 Roo.data.Record.prototype = {
10636     /**
10637      * Readonly flag - true if this record has been modified.
10638      * @type Boolean
10639      */
10640     dirty : false,
10641     editing : false,
10642     error: null,
10643     modified: null,
10644
10645     // private
10646     join : function(store){
10647         this.store = store;
10648     },
10649
10650     /**
10651      * Set the named field to the specified value.
10652      * @param {String} name The name of the field to set.
10653      * @param {Object} value The value to set the field to.
10654      */
10655     set : function(name, value){
10656         if(this.data[name] == value){
10657             return;
10658         }
10659         this.dirty = true;
10660         if(!this.modified){
10661             this.modified = {};
10662         }
10663         if(typeof this.modified[name] == 'undefined'){
10664             this.modified[name] = this.data[name];
10665         }
10666         this.data[name] = value;
10667         if(!this.editing && this.store){
10668             this.store.afterEdit(this);
10669         }       
10670     },
10671
10672     /**
10673      * Get the value of the named field.
10674      * @param {String} name The name of the field to get the value of.
10675      * @return {Object} The value of the field.
10676      */
10677     get : function(name){
10678         return this.data[name]; 
10679     },
10680
10681     // private
10682     beginEdit : function(){
10683         this.editing = true;
10684         this.modified = {}; 
10685     },
10686
10687     // private
10688     cancelEdit : function(){
10689         this.editing = false;
10690         delete this.modified;
10691     },
10692
10693     // private
10694     endEdit : function(){
10695         this.editing = false;
10696         if(this.dirty && this.store){
10697             this.store.afterEdit(this);
10698         }
10699     },
10700
10701     /**
10702      * Usually called by the {@link Roo.data.Store} which owns the Record.
10703      * Rejects all changes made to the Record since either creation, or the last commit operation.
10704      * Modified fields are reverted to their original values.
10705      * <p>
10706      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10707      * of reject operations.
10708      */
10709     reject : function(){
10710         var m = this.modified;
10711         for(var n in m){
10712             if(typeof m[n] != "function"){
10713                 this.data[n] = m[n];
10714             }
10715         }
10716         this.dirty = false;
10717         delete this.modified;
10718         this.editing = false;
10719         if(this.store){
10720             this.store.afterReject(this);
10721         }
10722     },
10723
10724     /**
10725      * Usually called by the {@link Roo.data.Store} which owns the Record.
10726      * Commits all changes made to the Record since either creation, or the last commit operation.
10727      * <p>
10728      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10729      * of commit operations.
10730      */
10731     commit : function(){
10732         this.dirty = false;
10733         delete this.modified;
10734         this.editing = false;
10735         if(this.store){
10736             this.store.afterCommit(this);
10737         }
10738     },
10739
10740     // private
10741     hasError : function(){
10742         return this.error != null;
10743     },
10744
10745     // private
10746     clearError : function(){
10747         this.error = null;
10748     },
10749
10750     /**
10751      * Creates a copy of this record.
10752      * @param {String} id (optional) A new record id if you don't want to use this record's id
10753      * @return {Record}
10754      */
10755     copy : function(newId) {
10756         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10757     }
10758 };/*
10759  * Based on:
10760  * Ext JS Library 1.1.1
10761  * Copyright(c) 2006-2007, Ext JS, LLC.
10762  *
10763  * Originally Released Under LGPL - original licence link has changed is not relivant.
10764  *
10765  * Fork - LGPL
10766  * <script type="text/javascript">
10767  */
10768
10769
10770
10771 /**
10772  * @class Roo.data.Store
10773  * @extends Roo.util.Observable
10774  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10775  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10776  * <p>
10777  * 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
10778  * has no knowledge of the format of the data returned by the Proxy.<br>
10779  * <p>
10780  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10781  * instances from the data object. These records are cached and made available through accessor functions.
10782  * @constructor
10783  * Creates a new Store.
10784  * @param {Object} config A config object containing the objects needed for the Store to access data,
10785  * and read the data into Records.
10786  */
10787 Roo.data.Store = function(config){
10788     this.data = new Roo.util.MixedCollection(false);
10789     this.data.getKey = function(o){
10790         return o.id;
10791     };
10792     this.baseParams = {};
10793     // private
10794     this.paramNames = {
10795         "start" : "start",
10796         "limit" : "limit",
10797         "sort" : "sort",
10798         "dir" : "dir",
10799         "multisort" : "_multisort"
10800     };
10801
10802     if(config && config.data){
10803         this.inlineData = config.data;
10804         delete config.data;
10805     }
10806
10807     Roo.apply(this, config);
10808     
10809     if(this.reader){ // reader passed
10810         this.reader = Roo.factory(this.reader, Roo.data);
10811         this.reader.xmodule = this.xmodule || false;
10812         if(!this.recordType){
10813             this.recordType = this.reader.recordType;
10814         }
10815         if(this.reader.onMetaChange){
10816             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10817         }
10818     }
10819
10820     if(this.recordType){
10821         this.fields = this.recordType.prototype.fields;
10822     }
10823     this.modified = [];
10824
10825     this.addEvents({
10826         /**
10827          * @event datachanged
10828          * Fires when the data cache has changed, and a widget which is using this Store
10829          * as a Record cache should refresh its view.
10830          * @param {Store} this
10831          */
10832         datachanged : true,
10833         /**
10834          * @event metachange
10835          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10836          * @param {Store} this
10837          * @param {Object} meta The JSON metadata
10838          */
10839         metachange : true,
10840         /**
10841          * @event add
10842          * Fires when Records have been added to the Store
10843          * @param {Store} this
10844          * @param {Roo.data.Record[]} records The array of Records added
10845          * @param {Number} index The index at which the record(s) were added
10846          */
10847         add : true,
10848         /**
10849          * @event remove
10850          * Fires when a Record has been removed from the Store
10851          * @param {Store} this
10852          * @param {Roo.data.Record} record The Record that was removed
10853          * @param {Number} index The index at which the record was removed
10854          */
10855         remove : true,
10856         /**
10857          * @event update
10858          * Fires when a Record has been updated
10859          * @param {Store} this
10860          * @param {Roo.data.Record} record The Record that was updated
10861          * @param {String} operation The update operation being performed.  Value may be one of:
10862          * <pre><code>
10863  Roo.data.Record.EDIT
10864  Roo.data.Record.REJECT
10865  Roo.data.Record.COMMIT
10866          * </code></pre>
10867          */
10868         update : true,
10869         /**
10870          * @event clear
10871          * Fires when the data cache has been cleared.
10872          * @param {Store} this
10873          */
10874         clear : true,
10875         /**
10876          * @event beforeload
10877          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10878          * the load action will be canceled.
10879          * @param {Store} this
10880          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10881          */
10882         beforeload : true,
10883         /**
10884          * @event beforeloadadd
10885          * Fires after a new set of Records has been loaded.
10886          * @param {Store} this
10887          * @param {Roo.data.Record[]} records The Records that were loaded
10888          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10889          */
10890         beforeloadadd : true,
10891         /**
10892          * @event load
10893          * Fires after a new set of Records has been loaded, before they are added to the store.
10894          * @param {Store} this
10895          * @param {Roo.data.Record[]} records The Records that were loaded
10896          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10897          * @params {Object} return from reader
10898          */
10899         load : true,
10900         /**
10901          * @event loadexception
10902          * Fires if an exception occurs in the Proxy during loading.
10903          * Called with the signature of the Proxy's "loadexception" event.
10904          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10905          * 
10906          * @param {Proxy} 
10907          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10908          * @param {Object} load options 
10909          * @param {Object} jsonData from your request (normally this contains the Exception)
10910          */
10911         loadexception : true
10912     });
10913     
10914     if(this.proxy){
10915         this.proxy = Roo.factory(this.proxy, Roo.data);
10916         this.proxy.xmodule = this.xmodule || false;
10917         this.relayEvents(this.proxy,  ["loadexception"]);
10918     }
10919     this.sortToggle = {};
10920     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10921
10922     Roo.data.Store.superclass.constructor.call(this);
10923
10924     if(this.inlineData){
10925         this.loadData(this.inlineData);
10926         delete this.inlineData;
10927     }
10928 };
10929
10930 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10931      /**
10932     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10933     * without a remote query - used by combo/forms at present.
10934     */
10935     
10936     /**
10937     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10938     */
10939     /**
10940     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10941     */
10942     /**
10943     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10944     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10945     */
10946     /**
10947     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10948     * on any HTTP request
10949     */
10950     /**
10951     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10952     */
10953     /**
10954     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10955     */
10956     multiSort: false,
10957     /**
10958     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10959     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10960     */
10961     remoteSort : false,
10962
10963     /**
10964     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10965      * loaded or when a record is removed. (defaults to false).
10966     */
10967     pruneModifiedRecords : false,
10968
10969     // private
10970     lastOptions : null,
10971
10972     /**
10973      * Add Records to the Store and fires the add event.
10974      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10975      */
10976     add : function(records){
10977         records = [].concat(records);
10978         for(var i = 0, len = records.length; i < len; i++){
10979             records[i].join(this);
10980         }
10981         var index = this.data.length;
10982         this.data.addAll(records);
10983         this.fireEvent("add", this, records, index);
10984     },
10985
10986     /**
10987      * Remove a Record from the Store and fires the remove event.
10988      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10989      */
10990     remove : function(record){
10991         var index = this.data.indexOf(record);
10992         this.data.removeAt(index);
10993         if(this.pruneModifiedRecords){
10994             this.modified.remove(record);
10995         }
10996         this.fireEvent("remove", this, record, index);
10997     },
10998
10999     /**
11000      * Remove all Records from the Store and fires the clear event.
11001      */
11002     removeAll : function(){
11003         this.data.clear();
11004         if(this.pruneModifiedRecords){
11005             this.modified = [];
11006         }
11007         this.fireEvent("clear", this);
11008     },
11009
11010     /**
11011      * Inserts Records to the Store at the given index and fires the add event.
11012      * @param {Number} index The start index at which to insert the passed Records.
11013      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11014      */
11015     insert : function(index, records){
11016         records = [].concat(records);
11017         for(var i = 0, len = records.length; i < len; i++){
11018             this.data.insert(index, records[i]);
11019             records[i].join(this);
11020         }
11021         this.fireEvent("add", this, records, index);
11022     },
11023
11024     /**
11025      * Get the index within the cache of the passed Record.
11026      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11027      * @return {Number} The index of the passed Record. Returns -1 if not found.
11028      */
11029     indexOf : function(record){
11030         return this.data.indexOf(record);
11031     },
11032
11033     /**
11034      * Get the index within the cache of the Record with the passed id.
11035      * @param {String} id The id of the Record to find.
11036      * @return {Number} The index of the Record. Returns -1 if not found.
11037      */
11038     indexOfId : function(id){
11039         return this.data.indexOfKey(id);
11040     },
11041
11042     /**
11043      * Get the Record with the specified id.
11044      * @param {String} id The id of the Record to find.
11045      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11046      */
11047     getById : function(id){
11048         return this.data.key(id);
11049     },
11050
11051     /**
11052      * Get the Record at the specified index.
11053      * @param {Number} index The index of the Record to find.
11054      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11055      */
11056     getAt : function(index){
11057         return this.data.itemAt(index);
11058     },
11059
11060     /**
11061      * Returns a range of Records between specified indices.
11062      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11063      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11064      * @return {Roo.data.Record[]} An array of Records
11065      */
11066     getRange : function(start, end){
11067         return this.data.getRange(start, end);
11068     },
11069
11070     // private
11071     storeOptions : function(o){
11072         o = Roo.apply({}, o);
11073         delete o.callback;
11074         delete o.scope;
11075         this.lastOptions = o;
11076     },
11077
11078     /**
11079      * Loads the Record cache from the configured Proxy using the configured Reader.
11080      * <p>
11081      * If using remote paging, then the first load call must specify the <em>start</em>
11082      * and <em>limit</em> properties in the options.params property to establish the initial
11083      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11084      * <p>
11085      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11086      * and this call will return before the new data has been loaded. Perform any post-processing
11087      * in a callback function, or in a "load" event handler.</strong>
11088      * <p>
11089      * @param {Object} options An object containing properties which control loading options:<ul>
11090      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11091      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11092      * passed the following arguments:<ul>
11093      * <li>r : Roo.data.Record[]</li>
11094      * <li>options: Options object from the load call</li>
11095      * <li>success: Boolean success indicator</li></ul></li>
11096      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11097      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11098      * </ul>
11099      */
11100     load : function(options){
11101         options = options || {};
11102         if(this.fireEvent("beforeload", this, options) !== false){
11103             this.storeOptions(options);
11104             var p = Roo.apply(options.params || {}, this.baseParams);
11105             // if meta was not loaded from remote source.. try requesting it.
11106             if (!this.reader.metaFromRemote) {
11107                 p._requestMeta = 1;
11108             }
11109             if(this.sortInfo && this.remoteSort){
11110                 var pn = this.paramNames;
11111                 p[pn["sort"]] = this.sortInfo.field;
11112                 p[pn["dir"]] = this.sortInfo.direction;
11113             }
11114             if (this.multiSort) {
11115                 var pn = this.paramNames;
11116                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11117             }
11118             
11119             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11120         }
11121     },
11122
11123     /**
11124      * Reloads the Record cache from the configured Proxy using the configured Reader and
11125      * the options from the last load operation performed.
11126      * @param {Object} options (optional) An object containing properties which may override the options
11127      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11128      * the most recently used options are reused).
11129      */
11130     reload : function(options){
11131         this.load(Roo.applyIf(options||{}, this.lastOptions));
11132     },
11133
11134     // private
11135     // Called as a callback by the Reader during a load operation.
11136     loadRecords : function(o, options, success){
11137         if(!o || success === false){
11138             if(success !== false){
11139                 this.fireEvent("load", this, [], options, o);
11140             }
11141             if(options.callback){
11142                 options.callback.call(options.scope || this, [], options, false);
11143             }
11144             return;
11145         }
11146         // if data returned failure - throw an exception.
11147         if (o.success === false) {
11148             // show a message if no listener is registered.
11149             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11150                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11151             }
11152             // loadmask wil be hooked into this..
11153             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11154             return;
11155         }
11156         var r = o.records, t = o.totalRecords || r.length;
11157         
11158         this.fireEvent("beforeloadadd", this, r, options, o);
11159         
11160         if(!options || options.add !== true){
11161             if(this.pruneModifiedRecords){
11162                 this.modified = [];
11163             }
11164             for(var i = 0, len = r.length; i < len; i++){
11165                 r[i].join(this);
11166             }
11167             if(this.snapshot){
11168                 this.data = this.snapshot;
11169                 delete this.snapshot;
11170             }
11171             this.data.clear();
11172             this.data.addAll(r);
11173             this.totalLength = t;
11174             this.applySort();
11175             this.fireEvent("datachanged", this);
11176         }else{
11177             this.totalLength = Math.max(t, this.data.length+r.length);
11178             this.add(r);
11179         }
11180         
11181         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11182                 
11183             var e = new Roo.data.Record({});
11184
11185             e.set(this.parent.displayField, this.parent.emptyTitle);
11186             e.set(this.parent.valueField, '');
11187
11188             this.insert(0, e);
11189         }
11190             
11191         this.fireEvent("load", this, r, options, o);
11192         if(options.callback){
11193             options.callback.call(options.scope || this, r, options, true);
11194         }
11195     },
11196
11197
11198     /**
11199      * Loads data from a passed data block. A Reader which understands the format of the data
11200      * must have been configured in the constructor.
11201      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11202      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11203      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11204      */
11205     loadData : function(o, append){
11206         var r = this.reader.readRecords(o);
11207         this.loadRecords(r, {add: append}, true);
11208     },
11209
11210     /**
11211      * Gets the number of cached records.
11212      * <p>
11213      * <em>If using paging, this may not be the total size of the dataset. If the data object
11214      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11215      * the data set size</em>
11216      */
11217     getCount : function(){
11218         return this.data.length || 0;
11219     },
11220
11221     /**
11222      * Gets the total number of records in the dataset as returned by the server.
11223      * <p>
11224      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11225      * the dataset size</em>
11226      */
11227     getTotalCount : function(){
11228         return this.totalLength || 0;
11229     },
11230
11231     /**
11232      * Returns the sort state of the Store as an object with two properties:
11233      * <pre><code>
11234  field {String} The name of the field by which the Records are sorted
11235  direction {String} The sort order, "ASC" or "DESC"
11236      * </code></pre>
11237      */
11238     getSortState : function(){
11239         return this.sortInfo;
11240     },
11241
11242     // private
11243     applySort : function(){
11244         if(this.sortInfo && !this.remoteSort){
11245             var s = this.sortInfo, f = s.field;
11246             var st = this.fields.get(f).sortType;
11247             var fn = function(r1, r2){
11248                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11249                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11250             };
11251             this.data.sort(s.direction, fn);
11252             if(this.snapshot && this.snapshot != this.data){
11253                 this.snapshot.sort(s.direction, fn);
11254             }
11255         }
11256     },
11257
11258     /**
11259      * Sets the default sort column and order to be used by the next load operation.
11260      * @param {String} fieldName The name of the field to sort by.
11261      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11262      */
11263     setDefaultSort : function(field, dir){
11264         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11265     },
11266
11267     /**
11268      * Sort the Records.
11269      * If remote sorting is used, the sort is performed on the server, and the cache is
11270      * reloaded. If local sorting is used, the cache is sorted internally.
11271      * @param {String} fieldName The name of the field to sort by.
11272      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11273      */
11274     sort : function(fieldName, dir){
11275         var f = this.fields.get(fieldName);
11276         if(!dir){
11277             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11278             
11279             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11280                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11281             }else{
11282                 dir = f.sortDir;
11283             }
11284         }
11285         this.sortToggle[f.name] = dir;
11286         this.sortInfo = {field: f.name, direction: dir};
11287         if(!this.remoteSort){
11288             this.applySort();
11289             this.fireEvent("datachanged", this);
11290         }else{
11291             this.load(this.lastOptions);
11292         }
11293     },
11294
11295     /**
11296      * Calls the specified function for each of the Records in the cache.
11297      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11298      * Returning <em>false</em> aborts and exits the iteration.
11299      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11300      */
11301     each : function(fn, scope){
11302         this.data.each(fn, scope);
11303     },
11304
11305     /**
11306      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11307      * (e.g., during paging).
11308      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11309      */
11310     getModifiedRecords : function(){
11311         return this.modified;
11312     },
11313
11314     // private
11315     createFilterFn : function(property, value, anyMatch){
11316         if(!value.exec){ // not a regex
11317             value = String(value);
11318             if(value.length == 0){
11319                 return false;
11320             }
11321             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11322         }
11323         return function(r){
11324             return value.test(r.data[property]);
11325         };
11326     },
11327
11328     /**
11329      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11330      * @param {String} property A field on your records
11331      * @param {Number} start The record index to start at (defaults to 0)
11332      * @param {Number} end The last record index to include (defaults to length - 1)
11333      * @return {Number} The sum
11334      */
11335     sum : function(property, start, end){
11336         var rs = this.data.items, v = 0;
11337         start = start || 0;
11338         end = (end || end === 0) ? end : rs.length-1;
11339
11340         for(var i = start; i <= end; i++){
11341             v += (rs[i].data[property] || 0);
11342         }
11343         return v;
11344     },
11345
11346     /**
11347      * Filter the records by a specified property.
11348      * @param {String} field A field on your records
11349      * @param {String/RegExp} value Either a string that the field
11350      * should start with or a RegExp to test against the field
11351      * @param {Boolean} anyMatch True to match any part not just the beginning
11352      */
11353     filter : function(property, value, anyMatch){
11354         var fn = this.createFilterFn(property, value, anyMatch);
11355         return fn ? this.filterBy(fn) : this.clearFilter();
11356     },
11357
11358     /**
11359      * Filter by a function. The specified function will be called with each
11360      * record in this data source. If the function returns true the record is included,
11361      * otherwise it is filtered.
11362      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11363      * @param {Object} scope (optional) The scope of the function (defaults to this)
11364      */
11365     filterBy : function(fn, scope){
11366         this.snapshot = this.snapshot || this.data;
11367         this.data = this.queryBy(fn, scope||this);
11368         this.fireEvent("datachanged", this);
11369     },
11370
11371     /**
11372      * Query the records by a specified property.
11373      * @param {String} field A field on your records
11374      * @param {String/RegExp} value Either a string that the field
11375      * should start with or a RegExp to test against the field
11376      * @param {Boolean} anyMatch True to match any part not just the beginning
11377      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11378      */
11379     query : function(property, value, anyMatch){
11380         var fn = this.createFilterFn(property, value, anyMatch);
11381         return fn ? this.queryBy(fn) : this.data.clone();
11382     },
11383
11384     /**
11385      * Query by a function. The specified function will be called with each
11386      * record in this data source. If the function returns true the record is included
11387      * in the results.
11388      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11389      * @param {Object} scope (optional) The scope of the function (defaults to this)
11390       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11391      **/
11392     queryBy : function(fn, scope){
11393         var data = this.snapshot || this.data;
11394         return data.filterBy(fn, scope||this);
11395     },
11396
11397     /**
11398      * Collects unique values for a particular dataIndex from this store.
11399      * @param {String} dataIndex The property to collect
11400      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11401      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11402      * @return {Array} An array of the unique values
11403      **/
11404     collect : function(dataIndex, allowNull, bypassFilter){
11405         var d = (bypassFilter === true && this.snapshot) ?
11406                 this.snapshot.items : this.data.items;
11407         var v, sv, r = [], l = {};
11408         for(var i = 0, len = d.length; i < len; i++){
11409             v = d[i].data[dataIndex];
11410             sv = String(v);
11411             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11412                 l[sv] = true;
11413                 r[r.length] = v;
11414             }
11415         }
11416         return r;
11417     },
11418
11419     /**
11420      * Revert to a view of the Record cache with no filtering applied.
11421      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11422      */
11423     clearFilter : function(suppressEvent){
11424         if(this.snapshot && this.snapshot != this.data){
11425             this.data = this.snapshot;
11426             delete this.snapshot;
11427             if(suppressEvent !== true){
11428                 this.fireEvent("datachanged", this);
11429             }
11430         }
11431     },
11432
11433     // private
11434     afterEdit : function(record){
11435         if(this.modified.indexOf(record) == -1){
11436             this.modified.push(record);
11437         }
11438         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11439     },
11440     
11441     // private
11442     afterReject : function(record){
11443         this.modified.remove(record);
11444         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11445     },
11446
11447     // private
11448     afterCommit : function(record){
11449         this.modified.remove(record);
11450         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11451     },
11452
11453     /**
11454      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11455      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11456      */
11457     commitChanges : function(){
11458         var m = this.modified.slice(0);
11459         this.modified = [];
11460         for(var i = 0, len = m.length; i < len; i++){
11461             m[i].commit();
11462         }
11463     },
11464
11465     /**
11466      * Cancel outstanding changes on all changed records.
11467      */
11468     rejectChanges : function(){
11469         var m = this.modified.slice(0);
11470         this.modified = [];
11471         for(var i = 0, len = m.length; i < len; i++){
11472             m[i].reject();
11473         }
11474     },
11475
11476     onMetaChange : function(meta, rtype, o){
11477         this.recordType = rtype;
11478         this.fields = rtype.prototype.fields;
11479         delete this.snapshot;
11480         this.sortInfo = meta.sortInfo || this.sortInfo;
11481         this.modified = [];
11482         this.fireEvent('metachange', this, this.reader.meta);
11483     },
11484     
11485     moveIndex : function(data, type)
11486     {
11487         var index = this.indexOf(data);
11488         
11489         var newIndex = index + type;
11490         
11491         this.remove(data);
11492         
11493         this.insert(newIndex, data);
11494         
11495     }
11496 });/*
11497  * Based on:
11498  * Ext JS Library 1.1.1
11499  * Copyright(c) 2006-2007, Ext JS, LLC.
11500  *
11501  * Originally Released Under LGPL - original licence link has changed is not relivant.
11502  *
11503  * Fork - LGPL
11504  * <script type="text/javascript">
11505  */
11506
11507 /**
11508  * @class Roo.data.SimpleStore
11509  * @extends Roo.data.Store
11510  * Small helper class to make creating Stores from Array data easier.
11511  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11512  * @cfg {Array} fields An array of field definition objects, or field name strings.
11513  * @cfg {Array} data The multi-dimensional array of data
11514  * @constructor
11515  * @param {Object} config
11516  */
11517 Roo.data.SimpleStore = function(config){
11518     Roo.data.SimpleStore.superclass.constructor.call(this, {
11519         isLocal : true,
11520         reader: new Roo.data.ArrayReader({
11521                 id: config.id
11522             },
11523             Roo.data.Record.create(config.fields)
11524         ),
11525         proxy : new Roo.data.MemoryProxy(config.data)
11526     });
11527     this.load();
11528 };
11529 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11530  * Based on:
11531  * Ext JS Library 1.1.1
11532  * Copyright(c) 2006-2007, Ext JS, LLC.
11533  *
11534  * Originally Released Under LGPL - original licence link has changed is not relivant.
11535  *
11536  * Fork - LGPL
11537  * <script type="text/javascript">
11538  */
11539
11540 /**
11541 /**
11542  * @extends Roo.data.Store
11543  * @class Roo.data.JsonStore
11544  * Small helper class to make creating Stores for JSON data easier. <br/>
11545 <pre><code>
11546 var store = new Roo.data.JsonStore({
11547     url: 'get-images.php',
11548     root: 'images',
11549     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11550 });
11551 </code></pre>
11552  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11553  * JsonReader and HttpProxy (unless inline data is provided).</b>
11554  * @cfg {Array} fields An array of field definition objects, or field name strings.
11555  * @constructor
11556  * @param {Object} config
11557  */
11558 Roo.data.JsonStore = function(c){
11559     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11560         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11561         reader: new Roo.data.JsonReader(c, c.fields)
11562     }));
11563 };
11564 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11565  * Based on:
11566  * Ext JS Library 1.1.1
11567  * Copyright(c) 2006-2007, Ext JS, LLC.
11568  *
11569  * Originally Released Under LGPL - original licence link has changed is not relivant.
11570  *
11571  * Fork - LGPL
11572  * <script type="text/javascript">
11573  */
11574
11575  
11576 Roo.data.Field = function(config){
11577     if(typeof config == "string"){
11578         config = {name: config};
11579     }
11580     Roo.apply(this, config);
11581     
11582     if(!this.type){
11583         this.type = "auto";
11584     }
11585     
11586     var st = Roo.data.SortTypes;
11587     // named sortTypes are supported, here we look them up
11588     if(typeof this.sortType == "string"){
11589         this.sortType = st[this.sortType];
11590     }
11591     
11592     // set default sortType for strings and dates
11593     if(!this.sortType){
11594         switch(this.type){
11595             case "string":
11596                 this.sortType = st.asUCString;
11597                 break;
11598             case "date":
11599                 this.sortType = st.asDate;
11600                 break;
11601             default:
11602                 this.sortType = st.none;
11603         }
11604     }
11605
11606     // define once
11607     var stripRe = /[\$,%]/g;
11608
11609     // prebuilt conversion function for this field, instead of
11610     // switching every time we're reading a value
11611     if(!this.convert){
11612         var cv, dateFormat = this.dateFormat;
11613         switch(this.type){
11614             case "":
11615             case "auto":
11616             case undefined:
11617                 cv = function(v){ return v; };
11618                 break;
11619             case "string":
11620                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11621                 break;
11622             case "int":
11623                 cv = function(v){
11624                     return v !== undefined && v !== null && v !== '' ?
11625                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11626                     };
11627                 break;
11628             case "float":
11629                 cv = function(v){
11630                     return v !== undefined && v !== null && v !== '' ?
11631                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11632                     };
11633                 break;
11634             case "bool":
11635             case "boolean":
11636                 cv = function(v){ return v === true || v === "true" || v == 1; };
11637                 break;
11638             case "date":
11639                 cv = function(v){
11640                     if(!v){
11641                         return '';
11642                     }
11643                     if(v instanceof Date){
11644                         return v;
11645                     }
11646                     if(dateFormat){
11647                         if(dateFormat == "timestamp"){
11648                             return new Date(v*1000);
11649                         }
11650                         return Date.parseDate(v, dateFormat);
11651                     }
11652                     var parsed = Date.parse(v);
11653                     return parsed ? new Date(parsed) : null;
11654                 };
11655              break;
11656             
11657         }
11658         this.convert = cv;
11659     }
11660 };
11661
11662 Roo.data.Field.prototype = {
11663     dateFormat: null,
11664     defaultValue: "",
11665     mapping: null,
11666     sortType : null,
11667     sortDir : "ASC"
11668 };/*
11669  * Based on:
11670  * Ext JS Library 1.1.1
11671  * Copyright(c) 2006-2007, Ext JS, LLC.
11672  *
11673  * Originally Released Under LGPL - original licence link has changed is not relivant.
11674  *
11675  * Fork - LGPL
11676  * <script type="text/javascript">
11677  */
11678  
11679 // Base class for reading structured data from a data source.  This class is intended to be
11680 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11681
11682 /**
11683  * @class Roo.data.DataReader
11684  * Base class for reading structured data from a data source.  This class is intended to be
11685  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11686  */
11687
11688 Roo.data.DataReader = function(meta, recordType){
11689     
11690     this.meta = meta;
11691     
11692     this.recordType = recordType instanceof Array ? 
11693         Roo.data.Record.create(recordType) : recordType;
11694 };
11695
11696 Roo.data.DataReader.prototype = {
11697      /**
11698      * Create an empty record
11699      * @param {Object} data (optional) - overlay some values
11700      * @return {Roo.data.Record} record created.
11701      */
11702     newRow :  function(d) {
11703         var da =  {};
11704         this.recordType.prototype.fields.each(function(c) {
11705             switch( c.type) {
11706                 case 'int' : da[c.name] = 0; break;
11707                 case 'date' : da[c.name] = new Date(); break;
11708                 case 'float' : da[c.name] = 0.0; break;
11709                 case 'boolean' : da[c.name] = false; break;
11710                 default : da[c.name] = ""; break;
11711             }
11712             
11713         });
11714         return new this.recordType(Roo.apply(da, d));
11715     }
11716     
11717 };/*
11718  * Based on:
11719  * Ext JS Library 1.1.1
11720  * Copyright(c) 2006-2007, Ext JS, LLC.
11721  *
11722  * Originally Released Under LGPL - original licence link has changed is not relivant.
11723  *
11724  * Fork - LGPL
11725  * <script type="text/javascript">
11726  */
11727
11728 /**
11729  * @class Roo.data.DataProxy
11730  * @extends Roo.data.Observable
11731  * This class is an abstract base class for implementations which provide retrieval of
11732  * unformatted data objects.<br>
11733  * <p>
11734  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11735  * (of the appropriate type which knows how to parse the data object) to provide a block of
11736  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11737  * <p>
11738  * Custom implementations must implement the load method as described in
11739  * {@link Roo.data.HttpProxy#load}.
11740  */
11741 Roo.data.DataProxy = function(){
11742     this.addEvents({
11743         /**
11744          * @event beforeload
11745          * Fires before a network request is made to retrieve a data object.
11746          * @param {Object} This DataProxy object.
11747          * @param {Object} params The params parameter to the load function.
11748          */
11749         beforeload : true,
11750         /**
11751          * @event load
11752          * Fires before the load method's callback is called.
11753          * @param {Object} This DataProxy object.
11754          * @param {Object} o The data object.
11755          * @param {Object} arg The callback argument object passed to the load function.
11756          */
11757         load : true,
11758         /**
11759          * @event loadexception
11760          * Fires if an Exception occurs during data retrieval.
11761          * @param {Object} This DataProxy object.
11762          * @param {Object} o The data object.
11763          * @param {Object} arg The callback argument object passed to the load function.
11764          * @param {Object} e The Exception.
11765          */
11766         loadexception : true
11767     });
11768     Roo.data.DataProxy.superclass.constructor.call(this);
11769 };
11770
11771 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11772
11773     /**
11774      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11775      */
11776 /*
11777  * Based on:
11778  * Ext JS Library 1.1.1
11779  * Copyright(c) 2006-2007, Ext JS, LLC.
11780  *
11781  * Originally Released Under LGPL - original licence link has changed is not relivant.
11782  *
11783  * Fork - LGPL
11784  * <script type="text/javascript">
11785  */
11786 /**
11787  * @class Roo.data.MemoryProxy
11788  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11789  * to the Reader when its load method is called.
11790  * @constructor
11791  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11792  */
11793 Roo.data.MemoryProxy = function(data){
11794     if (data.data) {
11795         data = data.data;
11796     }
11797     Roo.data.MemoryProxy.superclass.constructor.call(this);
11798     this.data = data;
11799 };
11800
11801 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11802     
11803     /**
11804      * Load data from the requested source (in this case an in-memory
11805      * data object passed to the constructor), read the data object into
11806      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11807      * process that block using the passed callback.
11808      * @param {Object} params This parameter is not used by the MemoryProxy class.
11809      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11810      * object into a block of Roo.data.Records.
11811      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11812      * The function must be passed <ul>
11813      * <li>The Record block object</li>
11814      * <li>The "arg" argument from the load function</li>
11815      * <li>A boolean success indicator</li>
11816      * </ul>
11817      * @param {Object} scope The scope in which to call the callback
11818      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11819      */
11820     load : function(params, reader, callback, scope, arg){
11821         params = params || {};
11822         var result;
11823         try {
11824             result = reader.readRecords(this.data);
11825         }catch(e){
11826             this.fireEvent("loadexception", this, arg, null, e);
11827             callback.call(scope, null, arg, false);
11828             return;
11829         }
11830         callback.call(scope, result, arg, true);
11831     },
11832     
11833     // private
11834     update : function(params, records){
11835         
11836     }
11837 });/*
11838  * Based on:
11839  * Ext JS Library 1.1.1
11840  * Copyright(c) 2006-2007, Ext JS, LLC.
11841  *
11842  * Originally Released Under LGPL - original licence link has changed is not relivant.
11843  *
11844  * Fork - LGPL
11845  * <script type="text/javascript">
11846  */
11847 /**
11848  * @class Roo.data.HttpProxy
11849  * @extends Roo.data.DataProxy
11850  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11851  * configured to reference a certain URL.<br><br>
11852  * <p>
11853  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11854  * from which the running page was served.<br><br>
11855  * <p>
11856  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11857  * <p>
11858  * Be aware that to enable the browser to parse an XML document, the server must set
11859  * the Content-Type header in the HTTP response to "text/xml".
11860  * @constructor
11861  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11862  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11863  * will be used to make the request.
11864  */
11865 Roo.data.HttpProxy = function(conn){
11866     Roo.data.HttpProxy.superclass.constructor.call(this);
11867     // is conn a conn config or a real conn?
11868     this.conn = conn;
11869     this.useAjax = !conn || !conn.events;
11870   
11871 };
11872
11873 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11874     // thse are take from connection...
11875     
11876     /**
11877      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11878      */
11879     /**
11880      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11881      * extra parameters to each request made by this object. (defaults to undefined)
11882      */
11883     /**
11884      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11885      *  to each request made by this object. (defaults to undefined)
11886      */
11887     /**
11888      * @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)
11889      */
11890     /**
11891      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11892      */
11893      /**
11894      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11895      * @type Boolean
11896      */
11897   
11898
11899     /**
11900      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11901      * @type Boolean
11902      */
11903     /**
11904      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11905      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11906      * a finer-grained basis than the DataProxy events.
11907      */
11908     getConnection : function(){
11909         return this.useAjax ? Roo.Ajax : this.conn;
11910     },
11911
11912     /**
11913      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11914      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11915      * process that block using the passed callback.
11916      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11917      * for the request to the remote server.
11918      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11919      * object into a block of Roo.data.Records.
11920      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11921      * The function must be passed <ul>
11922      * <li>The Record block object</li>
11923      * <li>The "arg" argument from the load function</li>
11924      * <li>A boolean success indicator</li>
11925      * </ul>
11926      * @param {Object} scope The scope in which to call the callback
11927      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11928      */
11929     load : function(params, reader, callback, scope, arg){
11930         if(this.fireEvent("beforeload", this, params) !== false){
11931             var  o = {
11932                 params : params || {},
11933                 request: {
11934                     callback : callback,
11935                     scope : scope,
11936                     arg : arg
11937                 },
11938                 reader: reader,
11939                 callback : this.loadResponse,
11940                 scope: this
11941             };
11942             if(this.useAjax){
11943                 Roo.applyIf(o, this.conn);
11944                 if(this.activeRequest){
11945                     Roo.Ajax.abort(this.activeRequest);
11946                 }
11947                 this.activeRequest = Roo.Ajax.request(o);
11948             }else{
11949                 this.conn.request(o);
11950             }
11951         }else{
11952             callback.call(scope||this, null, arg, false);
11953         }
11954     },
11955
11956     // private
11957     loadResponse : function(o, success, response){
11958         delete this.activeRequest;
11959         if(!success){
11960             this.fireEvent("loadexception", this, o, response);
11961             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11962             return;
11963         }
11964         var result;
11965         try {
11966             result = o.reader.read(response);
11967         }catch(e){
11968             this.fireEvent("loadexception", this, o, response, e);
11969             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11970             return;
11971         }
11972         
11973         this.fireEvent("load", this, o, o.request.arg);
11974         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11975     },
11976
11977     // private
11978     update : function(dataSet){
11979
11980     },
11981
11982     // private
11983     updateResponse : function(dataSet){
11984
11985     }
11986 });/*
11987  * Based on:
11988  * Ext JS Library 1.1.1
11989  * Copyright(c) 2006-2007, Ext JS, LLC.
11990  *
11991  * Originally Released Under LGPL - original licence link has changed is not relivant.
11992  *
11993  * Fork - LGPL
11994  * <script type="text/javascript">
11995  */
11996
11997 /**
11998  * @class Roo.data.ScriptTagProxy
11999  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
12000  * other than the originating domain of the running page.<br><br>
12001  * <p>
12002  * <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
12003  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
12004  * <p>
12005  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
12006  * source code that is used as the source inside a &lt;script> tag.<br><br>
12007  * <p>
12008  * In order for the browser to process the returned data, the server must wrap the data object
12009  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
12010  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
12011  * depending on whether the callback name was passed:
12012  * <p>
12013  * <pre><code>
12014 boolean scriptTag = false;
12015 String cb = request.getParameter("callback");
12016 if (cb != null) {
12017     scriptTag = true;
12018     response.setContentType("text/javascript");
12019 } else {
12020     response.setContentType("application/x-json");
12021 }
12022 Writer out = response.getWriter();
12023 if (scriptTag) {
12024     out.write(cb + "(");
12025 }
12026 out.print(dataBlock.toJsonString());
12027 if (scriptTag) {
12028     out.write(");");
12029 }
12030 </pre></code>
12031  *
12032  * @constructor
12033  * @param {Object} config A configuration object.
12034  */
12035 Roo.data.ScriptTagProxy = function(config){
12036     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12037     Roo.apply(this, config);
12038     this.head = document.getElementsByTagName("head")[0];
12039 };
12040
12041 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12042
12043 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12044     /**
12045      * @cfg {String} url The URL from which to request the data object.
12046      */
12047     /**
12048      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12049      */
12050     timeout : 30000,
12051     /**
12052      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12053      * the server the name of the callback function set up by the load call to process the returned data object.
12054      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12055      * javascript output which calls this named function passing the data object as its only parameter.
12056      */
12057     callbackParam : "callback",
12058     /**
12059      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12060      * name to the request.
12061      */
12062     nocache : true,
12063
12064     /**
12065      * Load data from the configured URL, read the data object into
12066      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12067      * process that block using the passed callback.
12068      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12069      * for the request to the remote server.
12070      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12071      * object into a block of Roo.data.Records.
12072      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12073      * The function must be passed <ul>
12074      * <li>The Record block object</li>
12075      * <li>The "arg" argument from the load function</li>
12076      * <li>A boolean success indicator</li>
12077      * </ul>
12078      * @param {Object} scope The scope in which to call the callback
12079      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12080      */
12081     load : function(params, reader, callback, scope, arg){
12082         if(this.fireEvent("beforeload", this, params) !== false){
12083
12084             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12085
12086             var url = this.url;
12087             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12088             if(this.nocache){
12089                 url += "&_dc=" + (new Date().getTime());
12090             }
12091             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12092             var trans = {
12093                 id : transId,
12094                 cb : "stcCallback"+transId,
12095                 scriptId : "stcScript"+transId,
12096                 params : params,
12097                 arg : arg,
12098                 url : url,
12099                 callback : callback,
12100                 scope : scope,
12101                 reader : reader
12102             };
12103             var conn = this;
12104
12105             window[trans.cb] = function(o){
12106                 conn.handleResponse(o, trans);
12107             };
12108
12109             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12110
12111             if(this.autoAbort !== false){
12112                 this.abort();
12113             }
12114
12115             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12116
12117             var script = document.createElement("script");
12118             script.setAttribute("src", url);
12119             script.setAttribute("type", "text/javascript");
12120             script.setAttribute("id", trans.scriptId);
12121             this.head.appendChild(script);
12122
12123             this.trans = trans;
12124         }else{
12125             callback.call(scope||this, null, arg, false);
12126         }
12127     },
12128
12129     // private
12130     isLoading : function(){
12131         return this.trans ? true : false;
12132     },
12133
12134     /**
12135      * Abort the current server request.
12136      */
12137     abort : function(){
12138         if(this.isLoading()){
12139             this.destroyTrans(this.trans);
12140         }
12141     },
12142
12143     // private
12144     destroyTrans : function(trans, isLoaded){
12145         this.head.removeChild(document.getElementById(trans.scriptId));
12146         clearTimeout(trans.timeoutId);
12147         if(isLoaded){
12148             window[trans.cb] = undefined;
12149             try{
12150                 delete window[trans.cb];
12151             }catch(e){}
12152         }else{
12153             // if hasn't been loaded, wait for load to remove it to prevent script error
12154             window[trans.cb] = function(){
12155                 window[trans.cb] = undefined;
12156                 try{
12157                     delete window[trans.cb];
12158                 }catch(e){}
12159             };
12160         }
12161     },
12162
12163     // private
12164     handleResponse : function(o, trans){
12165         this.trans = false;
12166         this.destroyTrans(trans, true);
12167         var result;
12168         try {
12169             result = trans.reader.readRecords(o);
12170         }catch(e){
12171             this.fireEvent("loadexception", this, o, trans.arg, e);
12172             trans.callback.call(trans.scope||window, null, trans.arg, false);
12173             return;
12174         }
12175         this.fireEvent("load", this, o, trans.arg);
12176         trans.callback.call(trans.scope||window, result, trans.arg, true);
12177     },
12178
12179     // private
12180     handleFailure : function(trans){
12181         this.trans = false;
12182         this.destroyTrans(trans, false);
12183         this.fireEvent("loadexception", this, null, trans.arg);
12184         trans.callback.call(trans.scope||window, null, trans.arg, false);
12185     }
12186 });/*
12187  * Based on:
12188  * Ext JS Library 1.1.1
12189  * Copyright(c) 2006-2007, Ext JS, LLC.
12190  *
12191  * Originally Released Under LGPL - original licence link has changed is not relivant.
12192  *
12193  * Fork - LGPL
12194  * <script type="text/javascript">
12195  */
12196
12197 /**
12198  * @class Roo.data.JsonReader
12199  * @extends Roo.data.DataReader
12200  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12201  * based on mappings in a provided Roo.data.Record constructor.
12202  * 
12203  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12204  * in the reply previously. 
12205  * 
12206  * <p>
12207  * Example code:
12208  * <pre><code>
12209 var RecordDef = Roo.data.Record.create([
12210     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12211     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12212 ]);
12213 var myReader = new Roo.data.JsonReader({
12214     totalProperty: "results",    // The property which contains the total dataset size (optional)
12215     root: "rows",                // The property which contains an Array of row objects
12216     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12217 }, RecordDef);
12218 </code></pre>
12219  * <p>
12220  * This would consume a JSON file like this:
12221  * <pre><code>
12222 { 'results': 2, 'rows': [
12223     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12224     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12225 }
12226 </code></pre>
12227  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12228  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12229  * paged from the remote server.
12230  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12231  * @cfg {String} root name of the property which contains the Array of row objects.
12232  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12233  * @cfg {Array} fields Array of field definition objects
12234  * @constructor
12235  * Create a new JsonReader
12236  * @param {Object} meta Metadata configuration options
12237  * @param {Object} recordType Either an Array of field definition objects,
12238  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12239  */
12240 Roo.data.JsonReader = function(meta, recordType){
12241     
12242     meta = meta || {};
12243     // set some defaults:
12244     Roo.applyIf(meta, {
12245         totalProperty: 'total',
12246         successProperty : 'success',
12247         root : 'data',
12248         id : 'id'
12249     });
12250     
12251     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12252 };
12253 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12254     
12255     /**
12256      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12257      * Used by Store query builder to append _requestMeta to params.
12258      * 
12259      */
12260     metaFromRemote : false,
12261     /**
12262      * This method is only used by a DataProxy which has retrieved data from a remote server.
12263      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12264      * @return {Object} data A data block which is used by an Roo.data.Store object as
12265      * a cache of Roo.data.Records.
12266      */
12267     read : function(response){
12268         var json = response.responseText;
12269        
12270         var o = /* eval:var:o */ eval("("+json+")");
12271         if(!o) {
12272             throw {message: "JsonReader.read: Json object not found"};
12273         }
12274         
12275         if(o.metaData){
12276             
12277             delete this.ef;
12278             this.metaFromRemote = true;
12279             this.meta = o.metaData;
12280             this.recordType = Roo.data.Record.create(o.metaData.fields);
12281             this.onMetaChange(this.meta, this.recordType, o);
12282         }
12283         return this.readRecords(o);
12284     },
12285
12286     // private function a store will implement
12287     onMetaChange : function(meta, recordType, o){
12288
12289     },
12290
12291     /**
12292          * @ignore
12293          */
12294     simpleAccess: function(obj, subsc) {
12295         return obj[subsc];
12296     },
12297
12298         /**
12299          * @ignore
12300          */
12301     getJsonAccessor: function(){
12302         var re = /[\[\.]/;
12303         return function(expr) {
12304             try {
12305                 return(re.test(expr))
12306                     ? new Function("obj", "return obj." + expr)
12307                     : function(obj){
12308                         return obj[expr];
12309                     };
12310             } catch(e){}
12311             return Roo.emptyFn;
12312         };
12313     }(),
12314
12315     /**
12316      * Create a data block containing Roo.data.Records from an XML document.
12317      * @param {Object} o An object which contains an Array of row objects in the property specified
12318      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12319      * which contains the total size of the dataset.
12320      * @return {Object} data A data block which is used by an Roo.data.Store object as
12321      * a cache of Roo.data.Records.
12322      */
12323     readRecords : function(o){
12324         /**
12325          * After any data loads, the raw JSON data is available for further custom processing.
12326          * @type Object
12327          */
12328         this.o = o;
12329         var s = this.meta, Record = this.recordType,
12330             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12331
12332 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12333         if (!this.ef) {
12334             if(s.totalProperty) {
12335                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12336                 }
12337                 if(s.successProperty) {
12338                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12339                 }
12340                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12341                 if (s.id) {
12342                         var g = this.getJsonAccessor(s.id);
12343                         this.getId = function(rec) {
12344                                 var r = g(rec);  
12345                                 return (r === undefined || r === "") ? null : r;
12346                         };
12347                 } else {
12348                         this.getId = function(){return null;};
12349                 }
12350             this.ef = [];
12351             for(var jj = 0; jj < fl; jj++){
12352                 f = fi[jj];
12353                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12354                 this.ef[jj] = this.getJsonAccessor(map);
12355             }
12356         }
12357
12358         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12359         if(s.totalProperty){
12360             var vt = parseInt(this.getTotal(o), 10);
12361             if(!isNaN(vt)){
12362                 totalRecords = vt;
12363             }
12364         }
12365         if(s.successProperty){
12366             var vs = this.getSuccess(o);
12367             if(vs === false || vs === 'false'){
12368                 success = false;
12369             }
12370         }
12371         var records = [];
12372         for(var i = 0; i < c; i++){
12373                 var n = root[i];
12374             var values = {};
12375             var id = this.getId(n);
12376             for(var j = 0; j < fl; j++){
12377                 f = fi[j];
12378             var v = this.ef[j](n);
12379             if (!f.convert) {
12380                 Roo.log('missing convert for ' + f.name);
12381                 Roo.log(f);
12382                 continue;
12383             }
12384             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12385             }
12386             var record = new Record(values, id);
12387             record.json = n;
12388             records[i] = record;
12389         }
12390         return {
12391             raw : o,
12392             success : success,
12393             records : records,
12394             totalRecords : totalRecords
12395         };
12396     }
12397 });/*
12398  * Based on:
12399  * Ext JS Library 1.1.1
12400  * Copyright(c) 2006-2007, Ext JS, LLC.
12401  *
12402  * Originally Released Under LGPL - original licence link has changed is not relivant.
12403  *
12404  * Fork - LGPL
12405  * <script type="text/javascript">
12406  */
12407
12408 /**
12409  * @class Roo.data.ArrayReader
12410  * @extends Roo.data.DataReader
12411  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12412  * Each element of that Array represents a row of data fields. The
12413  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12414  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12415  * <p>
12416  * Example code:.
12417  * <pre><code>
12418 var RecordDef = Roo.data.Record.create([
12419     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12420     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12421 ]);
12422 var myReader = new Roo.data.ArrayReader({
12423     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12424 }, RecordDef);
12425 </code></pre>
12426  * <p>
12427  * This would consume an Array like this:
12428  * <pre><code>
12429 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12430   </code></pre>
12431  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12432  * @constructor
12433  * Create a new JsonReader
12434  * @param {Object} meta Metadata configuration options.
12435  * @param {Object} recordType Either an Array of field definition objects
12436  * as specified to {@link Roo.data.Record#create},
12437  * or an {@link Roo.data.Record} object
12438  * created using {@link Roo.data.Record#create}.
12439  */
12440 Roo.data.ArrayReader = function(meta, recordType){
12441     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12442 };
12443
12444 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12445     /**
12446      * Create a data block containing Roo.data.Records from an XML document.
12447      * @param {Object} o An Array of row objects which represents the dataset.
12448      * @return {Object} data A data block which is used by an Roo.data.Store object as
12449      * a cache of Roo.data.Records.
12450      */
12451     readRecords : function(o){
12452         var sid = this.meta ? this.meta.id : null;
12453         var recordType = this.recordType, fields = recordType.prototype.fields;
12454         var records = [];
12455         var root = o;
12456             for(var i = 0; i < root.length; i++){
12457                     var n = root[i];
12458                 var values = {};
12459                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12460                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12461                 var f = fields.items[j];
12462                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12463                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12464                 v = f.convert(v);
12465                 values[f.name] = v;
12466             }
12467                 var record = new recordType(values, id);
12468                 record.json = n;
12469                 records[records.length] = record;
12470             }
12471             return {
12472                 records : records,
12473                 totalRecords : records.length
12474             };
12475     }
12476 });/*
12477  * - LGPL
12478  * * 
12479  */
12480
12481 /**
12482  * @class Roo.bootstrap.ComboBox
12483  * @extends Roo.bootstrap.TriggerField
12484  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12485  * @cfg {Boolean} append (true|false) default false
12486  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12487  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12488  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12489  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12490  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12491  * @cfg {Boolean} animate default true
12492  * @cfg {Boolean} emptyResultText only for touch device
12493  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12494  * @cfg {String} emptyTitle default ''
12495  * @constructor
12496  * Create a new ComboBox.
12497  * @param {Object} config Configuration options
12498  */
12499 Roo.bootstrap.ComboBox = function(config){
12500     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12501     this.addEvents({
12502         /**
12503          * @event expand
12504          * Fires when the dropdown list is expanded
12505         * @param {Roo.bootstrap.ComboBox} combo This combo box
12506         */
12507         'expand' : true,
12508         /**
12509          * @event collapse
12510          * Fires when the dropdown list is collapsed
12511         * @param {Roo.bootstrap.ComboBox} combo This combo box
12512         */
12513         'collapse' : true,
12514         /**
12515          * @event beforeselect
12516          * Fires before a list item is selected. Return false to cancel the selection.
12517         * @param {Roo.bootstrap.ComboBox} combo This combo box
12518         * @param {Roo.data.Record} record The data record returned from the underlying store
12519         * @param {Number} index The index of the selected item in the dropdown list
12520         */
12521         'beforeselect' : true,
12522         /**
12523          * @event select
12524          * Fires when a list item is selected
12525         * @param {Roo.bootstrap.ComboBox} combo This combo box
12526         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12527         * @param {Number} index The index of the selected item in the dropdown list
12528         */
12529         'select' : true,
12530         /**
12531          * @event beforequery
12532          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12533          * The event object passed has these properties:
12534         * @param {Roo.bootstrap.ComboBox} combo This combo box
12535         * @param {String} query The query
12536         * @param {Boolean} forceAll true to force "all" query
12537         * @param {Boolean} cancel true to cancel the query
12538         * @param {Object} e The query event object
12539         */
12540         'beforequery': true,
12541          /**
12542          * @event add
12543          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12544         * @param {Roo.bootstrap.ComboBox} combo This combo box
12545         */
12546         'add' : true,
12547         /**
12548          * @event edit
12549          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12550         * @param {Roo.bootstrap.ComboBox} combo This combo box
12551         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12552         */
12553         'edit' : true,
12554         /**
12555          * @event remove
12556          * Fires when the remove value from the combobox array
12557         * @param {Roo.bootstrap.ComboBox} combo This combo box
12558         */
12559         'remove' : true,
12560         /**
12561          * @event afterremove
12562          * Fires when the remove value from the combobox array
12563         * @param {Roo.bootstrap.ComboBox} combo This combo box
12564         */
12565         'afterremove' : true,
12566         /**
12567          * @event specialfilter
12568          * Fires when specialfilter
12569             * @param {Roo.bootstrap.ComboBox} combo This combo box
12570             */
12571         'specialfilter' : true,
12572         /**
12573          * @event tick
12574          * Fires when tick the element
12575             * @param {Roo.bootstrap.ComboBox} combo This combo box
12576             */
12577         'tick' : true,
12578         /**
12579          * @event touchviewdisplay
12580          * Fires when touch view require special display (default is using displayField)
12581             * @param {Roo.bootstrap.ComboBox} combo This combo box
12582             * @param {Object} cfg set html .
12583             */
12584         'touchviewdisplay' : true
12585         
12586     });
12587     
12588     this.item = [];
12589     this.tickItems = [];
12590     
12591     this.selectedIndex = -1;
12592     if(this.mode == 'local'){
12593         if(config.queryDelay === undefined){
12594             this.queryDelay = 10;
12595         }
12596         if(config.minChars === undefined){
12597             this.minChars = 0;
12598         }
12599     }
12600 };
12601
12602 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12603      
12604     /**
12605      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12606      * rendering into an Roo.Editor, defaults to false)
12607      */
12608     /**
12609      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12610      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12611      */
12612     /**
12613      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12614      */
12615     /**
12616      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12617      * the dropdown list (defaults to undefined, with no header element)
12618      */
12619
12620      /**
12621      * @cfg {String/Roo.Template} tpl The template to use to render the output
12622      */
12623      
12624      /**
12625      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12626      */
12627     listWidth: undefined,
12628     /**
12629      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12630      * mode = 'remote' or 'text' if mode = 'local')
12631      */
12632     displayField: undefined,
12633     
12634     /**
12635      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12636      * mode = 'remote' or 'value' if mode = 'local'). 
12637      * Note: use of a valueField requires the user make a selection
12638      * in order for a value to be mapped.
12639      */
12640     valueField: undefined,
12641     /**
12642      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12643      */
12644     modalTitle : '',
12645     
12646     /**
12647      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12648      * field's data value (defaults to the underlying DOM element's name)
12649      */
12650     hiddenName: undefined,
12651     /**
12652      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12653      */
12654     listClass: '',
12655     /**
12656      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12657      */
12658     selectedClass: 'active',
12659     
12660     /**
12661      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12662      */
12663     shadow:'sides',
12664     /**
12665      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12666      * anchor positions (defaults to 'tl-bl')
12667      */
12668     listAlign: 'tl-bl?',
12669     /**
12670      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12671      */
12672     maxHeight: 300,
12673     /**
12674      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12675      * query specified by the allQuery config option (defaults to 'query')
12676      */
12677     triggerAction: 'query',
12678     /**
12679      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12680      * (defaults to 4, does not apply if editable = false)
12681      */
12682     minChars : 4,
12683     /**
12684      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12685      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12686      */
12687     typeAhead: false,
12688     /**
12689      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12690      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12691      */
12692     queryDelay: 500,
12693     /**
12694      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12695      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12696      */
12697     pageSize: 0,
12698     /**
12699      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12700      * when editable = true (defaults to false)
12701      */
12702     selectOnFocus:false,
12703     /**
12704      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12705      */
12706     queryParam: 'query',
12707     /**
12708      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12709      * when mode = 'remote' (defaults to 'Loading...')
12710      */
12711     loadingText: 'Loading...',
12712     /**
12713      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12714      */
12715     resizable: false,
12716     /**
12717      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12718      */
12719     handleHeight : 8,
12720     /**
12721      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12722      * traditional select (defaults to true)
12723      */
12724     editable: true,
12725     /**
12726      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12727      */
12728     allQuery: '',
12729     /**
12730      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12731      */
12732     mode: 'remote',
12733     /**
12734      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12735      * listWidth has a higher value)
12736      */
12737     minListWidth : 70,
12738     /**
12739      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12740      * allow the user to set arbitrary text into the field (defaults to false)
12741      */
12742     forceSelection:false,
12743     /**
12744      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12745      * if typeAhead = true (defaults to 250)
12746      */
12747     typeAheadDelay : 250,
12748     /**
12749      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12750      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12751      */
12752     valueNotFoundText : undefined,
12753     /**
12754      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12755      */
12756     blockFocus : false,
12757     
12758     /**
12759      * @cfg {Boolean} disableClear Disable showing of clear button.
12760      */
12761     disableClear : false,
12762     /**
12763      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12764      */
12765     alwaysQuery : false,
12766     
12767     /**
12768      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12769      */
12770     multiple : false,
12771     
12772     /**
12773      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12774      */
12775     invalidClass : "has-warning",
12776     
12777     /**
12778      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12779      */
12780     validClass : "has-success",
12781     
12782     /**
12783      * @cfg {Boolean} specialFilter (true|false) special filter default false
12784      */
12785     specialFilter : false,
12786     
12787     /**
12788      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12789      */
12790     mobileTouchView : true,
12791     
12792     /**
12793      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12794      */
12795     useNativeIOS : false,
12796     
12797     ios_options : false,
12798     
12799     //private
12800     addicon : false,
12801     editicon: false,
12802     
12803     page: 0,
12804     hasQuery: false,
12805     append: false,
12806     loadNext: false,
12807     autoFocus : true,
12808     tickable : false,
12809     btnPosition : 'right',
12810     triggerList : true,
12811     showToggleBtn : true,
12812     animate : true,
12813     emptyResultText: 'Empty',
12814     triggerText : 'Select',
12815     emptyTitle : '',
12816     
12817     // element that contains real text value.. (when hidden is used..)
12818     
12819     getAutoCreate : function()
12820     {   
12821         var cfg = false;
12822         //render
12823         /*
12824          * Render classic select for iso
12825          */
12826         
12827         if(Roo.isIOS && this.useNativeIOS){
12828             cfg = this.getAutoCreateNativeIOS();
12829             return cfg;
12830         }
12831         
12832         /*
12833          * Touch Devices
12834          */
12835         
12836         if(Roo.isTouch && this.mobileTouchView){
12837             cfg = this.getAutoCreateTouchView();
12838             return cfg;;
12839         }
12840         
12841         /*
12842          *  Normal ComboBox
12843          */
12844         if(!this.tickable){
12845             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12846             return cfg;
12847         }
12848         
12849         /*
12850          *  ComboBox with tickable selections
12851          */
12852              
12853         var align = this.labelAlign || this.parentLabelAlign();
12854         
12855         cfg = {
12856             cls : 'form-group roo-combobox-tickable' //input-group
12857         };
12858         
12859         var btn_text_select = '';
12860         var btn_text_done = '';
12861         var btn_text_cancel = '';
12862         
12863         if (this.btn_text_show) {
12864             btn_text_select = 'Select';
12865             btn_text_done = 'Done';
12866             btn_text_cancel = 'Cancel'; 
12867         }
12868         
12869         var buttons = {
12870             tag : 'div',
12871             cls : 'tickable-buttons',
12872             cn : [
12873                 {
12874                     tag : 'button',
12875                     type : 'button',
12876                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12877                     //html : this.triggerText
12878                     html: btn_text_select
12879                 },
12880                 {
12881                     tag : 'button',
12882                     type : 'button',
12883                     name : 'ok',
12884                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12885                     //html : 'Done'
12886                     html: btn_text_done
12887                 },
12888                 {
12889                     tag : 'button',
12890                     type : 'button',
12891                     name : 'cancel',
12892                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12893                     //html : 'Cancel'
12894                     html: btn_text_cancel
12895                 }
12896             ]
12897         };
12898         
12899         if(this.editable){
12900             buttons.cn.unshift({
12901                 tag: 'input',
12902                 cls: 'roo-select2-search-field-input'
12903             });
12904         }
12905         
12906         var _this = this;
12907         
12908         Roo.each(buttons.cn, function(c){
12909             if (_this.size) {
12910                 c.cls += ' btn-' + _this.size;
12911             }
12912
12913             if (_this.disabled) {
12914                 c.disabled = true;
12915             }
12916         });
12917         
12918         var box = {
12919             tag: 'div',
12920             cn: [
12921                 {
12922                     tag: 'input',
12923                     type : 'hidden',
12924                     cls: 'form-hidden-field'
12925                 },
12926                 {
12927                     tag: 'ul',
12928                     cls: 'roo-select2-choices',
12929                     cn:[
12930                         {
12931                             tag: 'li',
12932                             cls: 'roo-select2-search-field',
12933                             cn: [
12934                                 buttons
12935                             ]
12936                         }
12937                     ]
12938                 }
12939             ]
12940         };
12941         
12942         var combobox = {
12943             cls: 'roo-select2-container input-group roo-select2-container-multi',
12944             cn: [
12945                 box
12946 //                {
12947 //                    tag: 'ul',
12948 //                    cls: 'typeahead typeahead-long dropdown-menu',
12949 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12950 //                }
12951             ]
12952         };
12953         
12954         if(this.hasFeedback && !this.allowBlank){
12955             
12956             var feedback = {
12957                 tag: 'span',
12958                 cls: 'glyphicon form-control-feedback'
12959             };
12960
12961             combobox.cn.push(feedback);
12962         }
12963         
12964         
12965         if (align ==='left' && this.fieldLabel.length) {
12966             
12967             cfg.cls += ' roo-form-group-label-left';
12968             
12969             cfg.cn = [
12970                 {
12971                     tag : 'i',
12972                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12973                     tooltip : 'This field is required'
12974                 },
12975                 {
12976                     tag: 'label',
12977                     'for' :  id,
12978                     cls : 'control-label',
12979                     html : this.fieldLabel
12980
12981                 },
12982                 {
12983                     cls : "", 
12984                     cn: [
12985                         combobox
12986                     ]
12987                 }
12988
12989             ];
12990             
12991             var labelCfg = cfg.cn[1];
12992             var contentCfg = cfg.cn[2];
12993             
12994
12995             if(this.indicatorpos == 'right'){
12996                 
12997                 cfg.cn = [
12998                     {
12999                         tag: 'label',
13000                         'for' :  id,
13001                         cls : 'control-label',
13002                         cn : [
13003                             {
13004                                 tag : 'span',
13005                                 html : this.fieldLabel
13006                             },
13007                             {
13008                                 tag : 'i',
13009                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13010                                 tooltip : 'This field is required'
13011                             }
13012                         ]
13013                     },
13014                     {
13015                         cls : "",
13016                         cn: [
13017                             combobox
13018                         ]
13019                     }
13020
13021                 ];
13022                 
13023                 
13024                 
13025                 labelCfg = cfg.cn[0];
13026                 contentCfg = cfg.cn[1];
13027             
13028             }
13029             
13030             if(this.labelWidth > 12){
13031                 labelCfg.style = "width: " + this.labelWidth + 'px';
13032             }
13033             
13034             if(this.labelWidth < 13 && this.labelmd == 0){
13035                 this.labelmd = this.labelWidth;
13036             }
13037             
13038             if(this.labellg > 0){
13039                 labelCfg.cls += ' col-lg-' + this.labellg;
13040                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13041             }
13042             
13043             if(this.labelmd > 0){
13044                 labelCfg.cls += ' col-md-' + this.labelmd;
13045                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13046             }
13047             
13048             if(this.labelsm > 0){
13049                 labelCfg.cls += ' col-sm-' + this.labelsm;
13050                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13051             }
13052             
13053             if(this.labelxs > 0){
13054                 labelCfg.cls += ' col-xs-' + this.labelxs;
13055                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13056             }
13057                 
13058                 
13059         } else if ( this.fieldLabel.length) {
13060 //                Roo.log(" label");
13061                  cfg.cn = [
13062                     {
13063                         tag : 'i',
13064                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13065                         tooltip : 'This field is required'
13066                     },
13067                     {
13068                         tag: 'label',
13069                         //cls : 'input-group-addon',
13070                         html : this.fieldLabel
13071                     },
13072                     combobox
13073                 ];
13074                 
13075                 if(this.indicatorpos == 'right'){
13076                     cfg.cn = [
13077                         {
13078                             tag: 'label',
13079                             //cls : 'input-group-addon',
13080                             html : this.fieldLabel
13081                         },
13082                         {
13083                             tag : 'i',
13084                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13085                             tooltip : 'This field is required'
13086                         },
13087                         combobox
13088                     ];
13089                     
13090                 }
13091
13092         } else {
13093             
13094 //                Roo.log(" no label && no align");
13095                 cfg = combobox
13096                      
13097                 
13098         }
13099          
13100         var settings=this;
13101         ['xs','sm','md','lg'].map(function(size){
13102             if (settings[size]) {
13103                 cfg.cls += ' col-' + size + '-' + settings[size];
13104             }
13105         });
13106         
13107         return cfg;
13108         
13109     },
13110     
13111     _initEventsCalled : false,
13112     
13113     // private
13114     initEvents: function()
13115     {   
13116         if (this._initEventsCalled) { // as we call render... prevent looping...
13117             return;
13118         }
13119         this._initEventsCalled = true;
13120         
13121         if (!this.store) {
13122             throw "can not find store for combo";
13123         }
13124         
13125         this.indicator = this.indicatorEl();
13126         
13127         this.store = Roo.factory(this.store, Roo.data);
13128         this.store.parent = this;
13129         
13130         // if we are building from html. then this element is so complex, that we can not really
13131         // use the rendered HTML.
13132         // so we have to trash and replace the previous code.
13133         if (Roo.XComponent.build_from_html) {
13134             // remove this element....
13135             var e = this.el.dom, k=0;
13136             while (e ) { e = e.previousSibling;  ++k;}
13137
13138             this.el.remove();
13139             
13140             this.el=false;
13141             this.rendered = false;
13142             
13143             this.render(this.parent().getChildContainer(true), k);
13144         }
13145         
13146         if(Roo.isIOS && this.useNativeIOS){
13147             this.initIOSView();
13148             return;
13149         }
13150         
13151         /*
13152          * Touch Devices
13153          */
13154         
13155         if(Roo.isTouch && this.mobileTouchView){
13156             this.initTouchView();
13157             return;
13158         }
13159         
13160         if(this.tickable){
13161             this.initTickableEvents();
13162             return;
13163         }
13164         
13165         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13166         
13167         if(this.hiddenName){
13168             
13169             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13170             
13171             this.hiddenField.dom.value =
13172                 this.hiddenValue !== undefined ? this.hiddenValue :
13173                 this.value !== undefined ? this.value : '';
13174
13175             // prevent input submission
13176             this.el.dom.removeAttribute('name');
13177             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13178              
13179              
13180         }
13181         //if(Roo.isGecko){
13182         //    this.el.dom.setAttribute('autocomplete', 'off');
13183         //}
13184         
13185         var cls = 'x-combo-list';
13186         
13187         //this.list = new Roo.Layer({
13188         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13189         //});
13190         
13191         var _this = this;
13192         
13193         (function(){
13194             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13195             _this.list.setWidth(lw);
13196         }).defer(100);
13197         
13198         this.list.on('mouseover', this.onViewOver, this);
13199         this.list.on('mousemove', this.onViewMove, this);
13200         this.list.on('scroll', this.onViewScroll, this);
13201         
13202         /*
13203         this.list.swallowEvent('mousewheel');
13204         this.assetHeight = 0;
13205
13206         if(this.title){
13207             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13208             this.assetHeight += this.header.getHeight();
13209         }
13210
13211         this.innerList = this.list.createChild({cls:cls+'-inner'});
13212         this.innerList.on('mouseover', this.onViewOver, this);
13213         this.innerList.on('mousemove', this.onViewMove, this);
13214         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13215         
13216         if(this.allowBlank && !this.pageSize && !this.disableClear){
13217             this.footer = this.list.createChild({cls:cls+'-ft'});
13218             this.pageTb = new Roo.Toolbar(this.footer);
13219            
13220         }
13221         if(this.pageSize){
13222             this.footer = this.list.createChild({cls:cls+'-ft'});
13223             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13224                     {pageSize: this.pageSize});
13225             
13226         }
13227         
13228         if (this.pageTb && this.allowBlank && !this.disableClear) {
13229             var _this = this;
13230             this.pageTb.add(new Roo.Toolbar.Fill(), {
13231                 cls: 'x-btn-icon x-btn-clear',
13232                 text: '&#160;',
13233                 handler: function()
13234                 {
13235                     _this.collapse();
13236                     _this.clearValue();
13237                     _this.onSelect(false, -1);
13238                 }
13239             });
13240         }
13241         if (this.footer) {
13242             this.assetHeight += this.footer.getHeight();
13243         }
13244         */
13245             
13246         if(!this.tpl){
13247             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13248         }
13249
13250         this.view = new Roo.View(this.list, this.tpl, {
13251             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13252         });
13253         //this.view.wrapEl.setDisplayed(false);
13254         this.view.on('click', this.onViewClick, this);
13255         
13256         
13257         this.store.on('beforeload', this.onBeforeLoad, this);
13258         this.store.on('load', this.onLoad, this);
13259         this.store.on('loadexception', this.onLoadException, this);
13260         /*
13261         if(this.resizable){
13262             this.resizer = new Roo.Resizable(this.list,  {
13263                pinned:true, handles:'se'
13264             });
13265             this.resizer.on('resize', function(r, w, h){
13266                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13267                 this.listWidth = w;
13268                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13269                 this.restrictHeight();
13270             }, this);
13271             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13272         }
13273         */
13274         if(!this.editable){
13275             this.editable = true;
13276             this.setEditable(false);
13277         }
13278         
13279         /*
13280         
13281         if (typeof(this.events.add.listeners) != 'undefined') {
13282             
13283             this.addicon = this.wrap.createChild(
13284                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13285        
13286             this.addicon.on('click', function(e) {
13287                 this.fireEvent('add', this);
13288             }, this);
13289         }
13290         if (typeof(this.events.edit.listeners) != 'undefined') {
13291             
13292             this.editicon = this.wrap.createChild(
13293                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13294             if (this.addicon) {
13295                 this.editicon.setStyle('margin-left', '40px');
13296             }
13297             this.editicon.on('click', function(e) {
13298                 
13299                 // we fire even  if inothing is selected..
13300                 this.fireEvent('edit', this, this.lastData );
13301                 
13302             }, this);
13303         }
13304         */
13305         
13306         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13307             "up" : function(e){
13308                 this.inKeyMode = true;
13309                 this.selectPrev();
13310             },
13311
13312             "down" : function(e){
13313                 if(!this.isExpanded()){
13314                     this.onTriggerClick();
13315                 }else{
13316                     this.inKeyMode = true;
13317                     this.selectNext();
13318                 }
13319             },
13320
13321             "enter" : function(e){
13322 //                this.onViewClick();
13323                 //return true;
13324                 this.collapse();
13325                 
13326                 if(this.fireEvent("specialkey", this, e)){
13327                     this.onViewClick(false);
13328                 }
13329                 
13330                 return true;
13331             },
13332
13333             "esc" : function(e){
13334                 this.collapse();
13335             },
13336
13337             "tab" : function(e){
13338                 this.collapse();
13339                 
13340                 if(this.fireEvent("specialkey", this, e)){
13341                     this.onViewClick(false);
13342                 }
13343                 
13344                 return true;
13345             },
13346
13347             scope : this,
13348
13349             doRelay : function(foo, bar, hname){
13350                 if(hname == 'down' || this.scope.isExpanded()){
13351                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13352                 }
13353                 return true;
13354             },
13355
13356             forceKeyDown: true
13357         });
13358         
13359         
13360         this.queryDelay = Math.max(this.queryDelay || 10,
13361                 this.mode == 'local' ? 10 : 250);
13362         
13363         
13364         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13365         
13366         if(this.typeAhead){
13367             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13368         }
13369         if(this.editable !== false){
13370             this.inputEl().on("keyup", this.onKeyUp, this);
13371         }
13372         if(this.forceSelection){
13373             this.inputEl().on('blur', this.doForce, this);
13374         }
13375         
13376         if(this.multiple){
13377             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13378             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13379         }
13380     },
13381     
13382     initTickableEvents: function()
13383     {   
13384         this.createList();
13385         
13386         if(this.hiddenName){
13387             
13388             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13389             
13390             this.hiddenField.dom.value =
13391                 this.hiddenValue !== undefined ? this.hiddenValue :
13392                 this.value !== undefined ? this.value : '';
13393
13394             // prevent input submission
13395             this.el.dom.removeAttribute('name');
13396             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13397              
13398              
13399         }
13400         
13401 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13402         
13403         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13404         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13405         if(this.triggerList){
13406             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13407         }
13408          
13409         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13410         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13411         
13412         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13413         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13414         
13415         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13416         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13417         
13418         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13419         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13420         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13421         
13422         this.okBtn.hide();
13423         this.cancelBtn.hide();
13424         
13425         var _this = this;
13426         
13427         (function(){
13428             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13429             _this.list.setWidth(lw);
13430         }).defer(100);
13431         
13432         this.list.on('mouseover', this.onViewOver, this);
13433         this.list.on('mousemove', this.onViewMove, this);
13434         
13435         this.list.on('scroll', this.onViewScroll, this);
13436         
13437         if(!this.tpl){
13438             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>';
13439         }
13440
13441         this.view = new Roo.View(this.list, this.tpl, {
13442             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13443         });
13444         
13445         //this.view.wrapEl.setDisplayed(false);
13446         this.view.on('click', this.onViewClick, this);
13447         
13448         
13449         
13450         this.store.on('beforeload', this.onBeforeLoad, this);
13451         this.store.on('load', this.onLoad, this);
13452         this.store.on('loadexception', this.onLoadException, this);
13453         
13454         if(this.editable){
13455             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13456                 "up" : function(e){
13457                     this.inKeyMode = true;
13458                     this.selectPrev();
13459                 },
13460
13461                 "down" : function(e){
13462                     this.inKeyMode = true;
13463                     this.selectNext();
13464                 },
13465
13466                 "enter" : function(e){
13467                     if(this.fireEvent("specialkey", this, e)){
13468                         this.onViewClick(false);
13469                     }
13470                     
13471                     return true;
13472                 },
13473
13474                 "esc" : function(e){
13475                     this.onTickableFooterButtonClick(e, false, false);
13476                 },
13477
13478                 "tab" : function(e){
13479                     this.fireEvent("specialkey", this, e);
13480                     
13481                     this.onTickableFooterButtonClick(e, false, false);
13482                     
13483                     return true;
13484                 },
13485
13486                 scope : this,
13487
13488                 doRelay : function(e, fn, key){
13489                     if(this.scope.isExpanded()){
13490                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13491                     }
13492                     return true;
13493                 },
13494
13495                 forceKeyDown: true
13496             });
13497         }
13498         
13499         this.queryDelay = Math.max(this.queryDelay || 10,
13500                 this.mode == 'local' ? 10 : 250);
13501         
13502         
13503         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13504         
13505         if(this.typeAhead){
13506             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13507         }
13508         
13509         if(this.editable !== false){
13510             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13511         }
13512         
13513         this.indicator = this.indicatorEl();
13514         
13515         if(this.indicator){
13516             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13517             this.indicator.hide();
13518         }
13519         
13520     },
13521
13522     onDestroy : function(){
13523         if(this.view){
13524             this.view.setStore(null);
13525             this.view.el.removeAllListeners();
13526             this.view.el.remove();
13527             this.view.purgeListeners();
13528         }
13529         if(this.list){
13530             this.list.dom.innerHTML  = '';
13531         }
13532         
13533         if(this.store){
13534             this.store.un('beforeload', this.onBeforeLoad, this);
13535             this.store.un('load', this.onLoad, this);
13536             this.store.un('loadexception', this.onLoadException, this);
13537         }
13538         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13539     },
13540
13541     // private
13542     fireKey : function(e){
13543         if(e.isNavKeyPress() && !this.list.isVisible()){
13544             this.fireEvent("specialkey", this, e);
13545         }
13546     },
13547
13548     // private
13549     onResize: function(w, h){
13550 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13551 //        
13552 //        if(typeof w != 'number'){
13553 //            // we do not handle it!?!?
13554 //            return;
13555 //        }
13556 //        var tw = this.trigger.getWidth();
13557 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13558 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13559 //        var x = w - tw;
13560 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13561 //            
13562 //        //this.trigger.setStyle('left', x+'px');
13563 //        
13564 //        if(this.list && this.listWidth === undefined){
13565 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13566 //            this.list.setWidth(lw);
13567 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13568 //        }
13569         
13570     
13571         
13572     },
13573
13574     /**
13575      * Allow or prevent the user from directly editing the field text.  If false is passed,
13576      * the user will only be able to select from the items defined in the dropdown list.  This method
13577      * is the runtime equivalent of setting the 'editable' config option at config time.
13578      * @param {Boolean} value True to allow the user to directly edit the field text
13579      */
13580     setEditable : function(value){
13581         if(value == this.editable){
13582             return;
13583         }
13584         this.editable = value;
13585         if(!value){
13586             this.inputEl().dom.setAttribute('readOnly', true);
13587             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13588             this.inputEl().addClass('x-combo-noedit');
13589         }else{
13590             this.inputEl().dom.setAttribute('readOnly', false);
13591             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13592             this.inputEl().removeClass('x-combo-noedit');
13593         }
13594     },
13595
13596     // private
13597     
13598     onBeforeLoad : function(combo,opts){
13599         if(!this.hasFocus){
13600             return;
13601         }
13602          if (!opts.add) {
13603             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13604          }
13605         this.restrictHeight();
13606         this.selectedIndex = -1;
13607     },
13608
13609     // private
13610     onLoad : function(){
13611         
13612         this.hasQuery = false;
13613         
13614         if(!this.hasFocus){
13615             return;
13616         }
13617         
13618         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13619             this.loading.hide();
13620         }
13621         
13622         if(this.store.getCount() > 0){
13623             
13624             this.expand();
13625             this.restrictHeight();
13626             if(this.lastQuery == this.allQuery){
13627                 if(this.editable && !this.tickable){
13628                     this.inputEl().dom.select();
13629                 }
13630                 
13631                 if(
13632                     !this.selectByValue(this.value, true) &&
13633                     this.autoFocus && 
13634                     (
13635                         !this.store.lastOptions ||
13636                         typeof(this.store.lastOptions.add) == 'undefined' || 
13637                         this.store.lastOptions.add != true
13638                     )
13639                 ){
13640                     this.select(0, true);
13641                 }
13642             }else{
13643                 if(this.autoFocus){
13644                     this.selectNext();
13645                 }
13646                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13647                     this.taTask.delay(this.typeAheadDelay);
13648                 }
13649             }
13650         }else{
13651             this.onEmptyResults();
13652         }
13653         
13654         //this.el.focus();
13655     },
13656     // private
13657     onLoadException : function()
13658     {
13659         this.hasQuery = false;
13660         
13661         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13662             this.loading.hide();
13663         }
13664         
13665         if(this.tickable && this.editable){
13666             return;
13667         }
13668         
13669         this.collapse();
13670         // only causes errors at present
13671         //Roo.log(this.store.reader.jsonData);
13672         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13673             // fixme
13674             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13675         //}
13676         
13677         
13678     },
13679     // private
13680     onTypeAhead : function(){
13681         if(this.store.getCount() > 0){
13682             var r = this.store.getAt(0);
13683             var newValue = r.data[this.displayField];
13684             var len = newValue.length;
13685             var selStart = this.getRawValue().length;
13686             
13687             if(selStart != len){
13688                 this.setRawValue(newValue);
13689                 this.selectText(selStart, newValue.length);
13690             }
13691         }
13692     },
13693
13694     // private
13695     onSelect : function(record, index){
13696         
13697         if(this.fireEvent('beforeselect', this, record, index) !== false){
13698         
13699             this.setFromData(index > -1 ? record.data : false);
13700             
13701             this.collapse();
13702             this.fireEvent('select', this, record, index);
13703         }
13704     },
13705
13706     /**
13707      * Returns the currently selected field value or empty string if no value is set.
13708      * @return {String} value The selected value
13709      */
13710     getValue : function()
13711     {
13712         if(Roo.isIOS && this.useNativeIOS){
13713             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13714         }
13715         
13716         if(this.multiple){
13717             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13718         }
13719         
13720         if(this.valueField){
13721             return typeof this.value != 'undefined' ? this.value : '';
13722         }else{
13723             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13724         }
13725     },
13726     
13727     getRawValue : function()
13728     {
13729         if(Roo.isIOS && this.useNativeIOS){
13730             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13731         }
13732         
13733         var v = this.inputEl().getValue();
13734         
13735         return v;
13736     },
13737
13738     /**
13739      * Clears any text/value currently set in the field
13740      */
13741     clearValue : function(){
13742         
13743         if(this.hiddenField){
13744             this.hiddenField.dom.value = '';
13745         }
13746         this.value = '';
13747         this.setRawValue('');
13748         this.lastSelectionText = '';
13749         this.lastData = false;
13750         
13751         var close = this.closeTriggerEl();
13752         
13753         if(close){
13754             close.hide();
13755         }
13756         
13757         this.validate();
13758         
13759     },
13760
13761     /**
13762      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13763      * will be displayed in the field.  If the value does not match the data value of an existing item,
13764      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13765      * Otherwise the field will be blank (although the value will still be set).
13766      * @param {String} value The value to match
13767      */
13768     setValue : function(v)
13769     {
13770         if(Roo.isIOS && this.useNativeIOS){
13771             this.setIOSValue(v);
13772             return;
13773         }
13774         
13775         if(this.multiple){
13776             this.syncValue();
13777             return;
13778         }
13779         
13780         var text = v;
13781         if(this.valueField){
13782             var r = this.findRecord(this.valueField, v);
13783             if(r){
13784                 text = r.data[this.displayField];
13785             }else if(this.valueNotFoundText !== undefined){
13786                 text = this.valueNotFoundText;
13787             }
13788         }
13789         this.lastSelectionText = text;
13790         if(this.hiddenField){
13791             this.hiddenField.dom.value = v;
13792         }
13793         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13794         this.value = v;
13795         
13796         var close = this.closeTriggerEl();
13797         
13798         if(close){
13799             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13800         }
13801         
13802         this.validate();
13803     },
13804     /**
13805      * @property {Object} the last set data for the element
13806      */
13807     
13808     lastData : false,
13809     /**
13810      * Sets the value of the field based on a object which is related to the record format for the store.
13811      * @param {Object} value the value to set as. or false on reset?
13812      */
13813     setFromData : function(o){
13814         
13815         if(this.multiple){
13816             this.addItem(o);
13817             return;
13818         }
13819             
13820         var dv = ''; // display value
13821         var vv = ''; // value value..
13822         this.lastData = o;
13823         if (this.displayField) {
13824             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13825         } else {
13826             // this is an error condition!!!
13827             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13828         }
13829         
13830         if(this.valueField){
13831             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13832         }
13833         
13834         var close = this.closeTriggerEl();
13835         
13836         if(close){
13837             if(dv.length || vv * 1 > 0){
13838                 close.show() ;
13839                 this.blockFocus=true;
13840             } else {
13841                 close.hide();
13842             }             
13843         }
13844         
13845         if(this.hiddenField){
13846             this.hiddenField.dom.value = vv;
13847             
13848             this.lastSelectionText = dv;
13849             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13850             this.value = vv;
13851             return;
13852         }
13853         // no hidden field.. - we store the value in 'value', but still display
13854         // display field!!!!
13855         this.lastSelectionText = dv;
13856         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13857         this.value = vv;
13858         
13859         
13860         
13861     },
13862     // private
13863     reset : function(){
13864         // overridden so that last data is reset..
13865         
13866         if(this.multiple){
13867             this.clearItem();
13868             return;
13869         }
13870         
13871         this.setValue(this.originalValue);
13872         //this.clearInvalid();
13873         this.lastData = false;
13874         if (this.view) {
13875             this.view.clearSelections();
13876         }
13877         
13878         this.validate();
13879     },
13880     // private
13881     findRecord : function(prop, value){
13882         var record;
13883         if(this.store.getCount() > 0){
13884             this.store.each(function(r){
13885                 if(r.data[prop] == value){
13886                     record = r;
13887                     return false;
13888                 }
13889                 return true;
13890             });
13891         }
13892         return record;
13893     },
13894     
13895     getName: function()
13896     {
13897         // returns hidden if it's set..
13898         if (!this.rendered) {return ''};
13899         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13900         
13901     },
13902     // private
13903     onViewMove : function(e, t){
13904         this.inKeyMode = false;
13905     },
13906
13907     // private
13908     onViewOver : function(e, t){
13909         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13910             return;
13911         }
13912         var item = this.view.findItemFromChild(t);
13913         
13914         if(item){
13915             var index = this.view.indexOf(item);
13916             this.select(index, false);
13917         }
13918     },
13919
13920     // private
13921     onViewClick : function(view, doFocus, el, e)
13922     {
13923         var index = this.view.getSelectedIndexes()[0];
13924         
13925         var r = this.store.getAt(index);
13926         
13927         if(this.tickable){
13928             
13929             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13930                 return;
13931             }
13932             
13933             var rm = false;
13934             var _this = this;
13935             
13936             Roo.each(this.tickItems, function(v,k){
13937                 
13938                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13939                     Roo.log(v);
13940                     _this.tickItems.splice(k, 1);
13941                     
13942                     if(typeof(e) == 'undefined' && view == false){
13943                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13944                     }
13945                     
13946                     rm = true;
13947                     return;
13948                 }
13949             });
13950             
13951             if(rm){
13952                 return;
13953             }
13954             
13955             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13956                 this.tickItems.push(r.data);
13957             }
13958             
13959             if(typeof(e) == 'undefined' && view == false){
13960                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13961             }
13962                     
13963             return;
13964         }
13965         
13966         if(r){
13967             this.onSelect(r, index);
13968         }
13969         if(doFocus !== false && !this.blockFocus){
13970             this.inputEl().focus();
13971         }
13972     },
13973
13974     // private
13975     restrictHeight : function(){
13976         //this.innerList.dom.style.height = '';
13977         //var inner = this.innerList.dom;
13978         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13979         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13980         //this.list.beginUpdate();
13981         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13982         this.list.alignTo(this.inputEl(), this.listAlign);
13983         this.list.alignTo(this.inputEl(), this.listAlign);
13984         //this.list.endUpdate();
13985     },
13986
13987     // private
13988     onEmptyResults : function(){
13989         
13990         if(this.tickable && this.editable){
13991             this.hasFocus = false;
13992             this.restrictHeight();
13993             return;
13994         }
13995         
13996         this.collapse();
13997     },
13998
13999     /**
14000      * Returns true if the dropdown list is expanded, else false.
14001      */
14002     isExpanded : function(){
14003         return this.list.isVisible();
14004     },
14005
14006     /**
14007      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
14008      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14009      * @param {String} value The data value of the item to select
14010      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14011      * selected item if it is not currently in view (defaults to true)
14012      * @return {Boolean} True if the value matched an item in the list, else false
14013      */
14014     selectByValue : function(v, scrollIntoView){
14015         if(v !== undefined && v !== null){
14016             var r = this.findRecord(this.valueField || this.displayField, v);
14017             if(r){
14018                 this.select(this.store.indexOf(r), scrollIntoView);
14019                 return true;
14020             }
14021         }
14022         return false;
14023     },
14024
14025     /**
14026      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14027      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14028      * @param {Number} index The zero-based index of the list item to select
14029      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14030      * selected item if it is not currently in view (defaults to true)
14031      */
14032     select : function(index, scrollIntoView){
14033         this.selectedIndex = index;
14034         this.view.select(index);
14035         if(scrollIntoView !== false){
14036             var el = this.view.getNode(index);
14037             /*
14038              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14039              */
14040             if(el){
14041                 this.list.scrollChildIntoView(el, false);
14042             }
14043         }
14044     },
14045
14046     // private
14047     selectNext : function(){
14048         var ct = this.store.getCount();
14049         if(ct > 0){
14050             if(this.selectedIndex == -1){
14051                 this.select(0);
14052             }else if(this.selectedIndex < ct-1){
14053                 this.select(this.selectedIndex+1);
14054             }
14055         }
14056     },
14057
14058     // private
14059     selectPrev : function(){
14060         var ct = this.store.getCount();
14061         if(ct > 0){
14062             if(this.selectedIndex == -1){
14063                 this.select(0);
14064             }else if(this.selectedIndex != 0){
14065                 this.select(this.selectedIndex-1);
14066             }
14067         }
14068     },
14069
14070     // private
14071     onKeyUp : function(e){
14072         if(this.editable !== false && !e.isSpecialKey()){
14073             this.lastKey = e.getKey();
14074             this.dqTask.delay(this.queryDelay);
14075         }
14076     },
14077
14078     // private
14079     validateBlur : function(){
14080         return !this.list || !this.list.isVisible();   
14081     },
14082
14083     // private
14084     initQuery : function(){
14085         
14086         var v = this.getRawValue();
14087         
14088         if(this.tickable && this.editable){
14089             v = this.tickableInputEl().getValue();
14090         }
14091         
14092         this.doQuery(v);
14093     },
14094
14095     // private
14096     doForce : function(){
14097         if(this.inputEl().dom.value.length > 0){
14098             this.inputEl().dom.value =
14099                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14100              
14101         }
14102     },
14103
14104     /**
14105      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14106      * query allowing the query action to be canceled if needed.
14107      * @param {String} query The SQL query to execute
14108      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14109      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14110      * saved in the current store (defaults to false)
14111      */
14112     doQuery : function(q, forceAll){
14113         
14114         if(q === undefined || q === null){
14115             q = '';
14116         }
14117         var qe = {
14118             query: q,
14119             forceAll: forceAll,
14120             combo: this,
14121             cancel:false
14122         };
14123         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14124             return false;
14125         }
14126         q = qe.query;
14127         
14128         forceAll = qe.forceAll;
14129         if(forceAll === true || (q.length >= this.minChars)){
14130             
14131             this.hasQuery = true;
14132             
14133             if(this.lastQuery != q || this.alwaysQuery){
14134                 this.lastQuery = q;
14135                 if(this.mode == 'local'){
14136                     this.selectedIndex = -1;
14137                     if(forceAll){
14138                         this.store.clearFilter();
14139                     }else{
14140                         
14141                         if(this.specialFilter){
14142                             this.fireEvent('specialfilter', this);
14143                             this.onLoad();
14144                             return;
14145                         }
14146                         
14147                         this.store.filter(this.displayField, q);
14148                     }
14149                     
14150                     this.store.fireEvent("datachanged", this.store);
14151                     
14152                     this.onLoad();
14153                     
14154                     
14155                 }else{
14156                     
14157                     this.store.baseParams[this.queryParam] = q;
14158                     
14159                     var options = {params : this.getParams(q)};
14160                     
14161                     if(this.loadNext){
14162                         options.add = true;
14163                         options.params.start = this.page * this.pageSize;
14164                     }
14165                     
14166                     this.store.load(options);
14167                     
14168                     /*
14169                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14170                      *  we should expand the list on onLoad
14171                      *  so command out it
14172                      */
14173 //                    this.expand();
14174                 }
14175             }else{
14176                 this.selectedIndex = -1;
14177                 this.onLoad();   
14178             }
14179         }
14180         
14181         this.loadNext = false;
14182     },
14183     
14184     // private
14185     getParams : function(q){
14186         var p = {};
14187         //p[this.queryParam] = q;
14188         
14189         if(this.pageSize){
14190             p.start = 0;
14191             p.limit = this.pageSize;
14192         }
14193         return p;
14194     },
14195
14196     /**
14197      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14198      */
14199     collapse : function(){
14200         if(!this.isExpanded()){
14201             return;
14202         }
14203         
14204         this.list.hide();
14205         
14206         this.hasFocus = false;
14207         
14208         if(this.tickable){
14209             this.okBtn.hide();
14210             this.cancelBtn.hide();
14211             this.trigger.show();
14212             
14213             if(this.editable){
14214                 this.tickableInputEl().dom.value = '';
14215                 this.tickableInputEl().blur();
14216             }
14217             
14218         }
14219         
14220         Roo.get(document).un('mousedown', this.collapseIf, this);
14221         Roo.get(document).un('mousewheel', this.collapseIf, this);
14222         if (!this.editable) {
14223             Roo.get(document).un('keydown', this.listKeyPress, this);
14224         }
14225         this.fireEvent('collapse', this);
14226         
14227         this.validate();
14228     },
14229
14230     // private
14231     collapseIf : function(e){
14232         var in_combo  = e.within(this.el);
14233         var in_list =  e.within(this.list);
14234         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14235         
14236         if (in_combo || in_list || is_list) {
14237             //e.stopPropagation();
14238             return;
14239         }
14240         
14241         if(this.tickable){
14242             this.onTickableFooterButtonClick(e, false, false);
14243         }
14244
14245         this.collapse();
14246         
14247     },
14248
14249     /**
14250      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14251      */
14252     expand : function(){
14253        
14254         if(this.isExpanded() || !this.hasFocus){
14255             return;
14256         }
14257         
14258         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14259         this.list.setWidth(lw);
14260         
14261         Roo.log('expand');
14262         
14263         this.list.show();
14264         
14265         this.restrictHeight();
14266         
14267         if(this.tickable){
14268             
14269             this.tickItems = Roo.apply([], this.item);
14270             
14271             this.okBtn.show();
14272             this.cancelBtn.show();
14273             this.trigger.hide();
14274             
14275             if(this.editable){
14276                 this.tickableInputEl().focus();
14277             }
14278             
14279         }
14280         
14281         Roo.get(document).on('mousedown', this.collapseIf, this);
14282         Roo.get(document).on('mousewheel', this.collapseIf, this);
14283         if (!this.editable) {
14284             Roo.get(document).on('keydown', this.listKeyPress, this);
14285         }
14286         
14287         this.fireEvent('expand', this);
14288     },
14289
14290     // private
14291     // Implements the default empty TriggerField.onTriggerClick function
14292     onTriggerClick : function(e)
14293     {
14294         Roo.log('trigger click');
14295         
14296         if(this.disabled || !this.triggerList){
14297             return;
14298         }
14299         
14300         this.page = 0;
14301         this.loadNext = false;
14302         
14303         if(this.isExpanded()){
14304             this.collapse();
14305             if (!this.blockFocus) {
14306                 this.inputEl().focus();
14307             }
14308             
14309         }else {
14310             this.hasFocus = true;
14311             if(this.triggerAction == 'all') {
14312                 this.doQuery(this.allQuery, true);
14313             } else {
14314                 this.doQuery(this.getRawValue());
14315             }
14316             if (!this.blockFocus) {
14317                 this.inputEl().focus();
14318             }
14319         }
14320     },
14321     
14322     onTickableTriggerClick : function(e)
14323     {
14324         if(this.disabled){
14325             return;
14326         }
14327         
14328         this.page = 0;
14329         this.loadNext = false;
14330         this.hasFocus = true;
14331         
14332         if(this.triggerAction == 'all') {
14333             this.doQuery(this.allQuery, true);
14334         } else {
14335             this.doQuery(this.getRawValue());
14336         }
14337     },
14338     
14339     onSearchFieldClick : function(e)
14340     {
14341         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14342             this.onTickableFooterButtonClick(e, false, false);
14343             return;
14344         }
14345         
14346         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14347             return;
14348         }
14349         
14350         this.page = 0;
14351         this.loadNext = false;
14352         this.hasFocus = true;
14353         
14354         if(this.triggerAction == 'all') {
14355             this.doQuery(this.allQuery, true);
14356         } else {
14357             this.doQuery(this.getRawValue());
14358         }
14359     },
14360     
14361     listKeyPress : function(e)
14362     {
14363         //Roo.log('listkeypress');
14364         // scroll to first matching element based on key pres..
14365         if (e.isSpecialKey()) {
14366             return false;
14367         }
14368         var k = String.fromCharCode(e.getKey()).toUpperCase();
14369         //Roo.log(k);
14370         var match  = false;
14371         var csel = this.view.getSelectedNodes();
14372         var cselitem = false;
14373         if (csel.length) {
14374             var ix = this.view.indexOf(csel[0]);
14375             cselitem  = this.store.getAt(ix);
14376             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14377                 cselitem = false;
14378             }
14379             
14380         }
14381         
14382         this.store.each(function(v) { 
14383             if (cselitem) {
14384                 // start at existing selection.
14385                 if (cselitem.id == v.id) {
14386                     cselitem = false;
14387                 }
14388                 return true;
14389             }
14390                 
14391             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14392                 match = this.store.indexOf(v);
14393                 return false;
14394             }
14395             return true;
14396         }, this);
14397         
14398         if (match === false) {
14399             return true; // no more action?
14400         }
14401         // scroll to?
14402         this.view.select(match);
14403         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14404         sn.scrollIntoView(sn.dom.parentNode, false);
14405     },
14406     
14407     onViewScroll : function(e, t){
14408         
14409         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){
14410             return;
14411         }
14412         
14413         this.hasQuery = true;
14414         
14415         this.loading = this.list.select('.loading', true).first();
14416         
14417         if(this.loading === null){
14418             this.list.createChild({
14419                 tag: 'div',
14420                 cls: 'loading roo-select2-more-results roo-select2-active',
14421                 html: 'Loading more results...'
14422             });
14423             
14424             this.loading = this.list.select('.loading', true).first();
14425             
14426             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14427             
14428             this.loading.hide();
14429         }
14430         
14431         this.loading.show();
14432         
14433         var _combo = this;
14434         
14435         this.page++;
14436         this.loadNext = true;
14437         
14438         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14439         
14440         return;
14441     },
14442     
14443     addItem : function(o)
14444     {   
14445         var dv = ''; // display value
14446         
14447         if (this.displayField) {
14448             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14449         } else {
14450             // this is an error condition!!!
14451             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14452         }
14453         
14454         if(!dv.length){
14455             return;
14456         }
14457         
14458         var choice = this.choices.createChild({
14459             tag: 'li',
14460             cls: 'roo-select2-search-choice',
14461             cn: [
14462                 {
14463                     tag: 'div',
14464                     html: dv
14465                 },
14466                 {
14467                     tag: 'a',
14468                     href: '#',
14469                     cls: 'roo-select2-search-choice-close fa fa-times',
14470                     tabindex: '-1'
14471                 }
14472             ]
14473             
14474         }, this.searchField);
14475         
14476         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14477         
14478         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14479         
14480         this.item.push(o);
14481         
14482         this.lastData = o;
14483         
14484         this.syncValue();
14485         
14486         this.inputEl().dom.value = '';
14487         
14488         this.validate();
14489     },
14490     
14491     onRemoveItem : function(e, _self, o)
14492     {
14493         e.preventDefault();
14494         
14495         this.lastItem = Roo.apply([], this.item);
14496         
14497         var index = this.item.indexOf(o.data) * 1;
14498         
14499         if( index < 0){
14500             Roo.log('not this item?!');
14501             return;
14502         }
14503         
14504         this.item.splice(index, 1);
14505         o.item.remove();
14506         
14507         this.syncValue();
14508         
14509         this.fireEvent('remove', this, e);
14510         
14511         this.validate();
14512         
14513     },
14514     
14515     syncValue : function()
14516     {
14517         if(!this.item.length){
14518             this.clearValue();
14519             return;
14520         }
14521             
14522         var value = [];
14523         var _this = this;
14524         Roo.each(this.item, function(i){
14525             if(_this.valueField){
14526                 value.push(i[_this.valueField]);
14527                 return;
14528             }
14529
14530             value.push(i);
14531         });
14532
14533         this.value = value.join(',');
14534
14535         if(this.hiddenField){
14536             this.hiddenField.dom.value = this.value;
14537         }
14538         
14539         this.store.fireEvent("datachanged", this.store);
14540         
14541         this.validate();
14542     },
14543     
14544     clearItem : function()
14545     {
14546         if(!this.multiple){
14547             return;
14548         }
14549         
14550         this.item = [];
14551         
14552         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14553            c.remove();
14554         });
14555         
14556         this.syncValue();
14557         
14558         this.validate();
14559         
14560         if(this.tickable && !Roo.isTouch){
14561             this.view.refresh();
14562         }
14563     },
14564     
14565     inputEl: function ()
14566     {
14567         if(Roo.isIOS && this.useNativeIOS){
14568             return this.el.select('select.roo-ios-select', true).first();
14569         }
14570         
14571         if(Roo.isTouch && this.mobileTouchView){
14572             return this.el.select('input.form-control',true).first();
14573         }
14574         
14575         if(this.tickable){
14576             return this.searchField;
14577         }
14578         
14579         return this.el.select('input.form-control',true).first();
14580     },
14581     
14582     onTickableFooterButtonClick : function(e, btn, el)
14583     {
14584         e.preventDefault();
14585         
14586         this.lastItem = Roo.apply([], this.item);
14587         
14588         if(btn && btn.name == 'cancel'){
14589             this.tickItems = Roo.apply([], this.item);
14590             this.collapse();
14591             return;
14592         }
14593         
14594         this.clearItem();
14595         
14596         var _this = this;
14597         
14598         Roo.each(this.tickItems, function(o){
14599             _this.addItem(o);
14600         });
14601         
14602         this.collapse();
14603         
14604     },
14605     
14606     validate : function()
14607     {
14608         var v = this.getRawValue();
14609         
14610         if(this.multiple){
14611             v = this.getValue();
14612         }
14613         
14614         if(this.disabled || this.allowBlank || v.length){
14615             this.markValid();
14616             return true;
14617         }
14618         
14619         this.markInvalid();
14620         return false;
14621     },
14622     
14623     tickableInputEl : function()
14624     {
14625         if(!this.tickable || !this.editable){
14626             return this.inputEl();
14627         }
14628         
14629         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14630     },
14631     
14632     
14633     getAutoCreateTouchView : function()
14634     {
14635         var id = Roo.id();
14636         
14637         var cfg = {
14638             cls: 'form-group' //input-group
14639         };
14640         
14641         var input =  {
14642             tag: 'input',
14643             id : id,
14644             type : this.inputType,
14645             cls : 'form-control x-combo-noedit',
14646             autocomplete: 'new-password',
14647             placeholder : this.placeholder || '',
14648             readonly : true
14649         };
14650         
14651         if (this.name) {
14652             input.name = this.name;
14653         }
14654         
14655         if (this.size) {
14656             input.cls += ' input-' + this.size;
14657         }
14658         
14659         if (this.disabled) {
14660             input.disabled = true;
14661         }
14662         
14663         var inputblock = {
14664             cls : '',
14665             cn : [
14666                 input
14667             ]
14668         };
14669         
14670         if(this.before){
14671             inputblock.cls += ' input-group';
14672             
14673             inputblock.cn.unshift({
14674                 tag :'span',
14675                 cls : 'input-group-addon',
14676                 html : this.before
14677             });
14678         }
14679         
14680         if(this.removable && !this.multiple){
14681             inputblock.cls += ' roo-removable';
14682             
14683             inputblock.cn.push({
14684                 tag: 'button',
14685                 html : 'x',
14686                 cls : 'roo-combo-removable-btn close'
14687             });
14688         }
14689
14690         if(this.hasFeedback && !this.allowBlank){
14691             
14692             inputblock.cls += ' has-feedback';
14693             
14694             inputblock.cn.push({
14695                 tag: 'span',
14696                 cls: 'glyphicon form-control-feedback'
14697             });
14698             
14699         }
14700         
14701         if (this.after) {
14702             
14703             inputblock.cls += (this.before) ? '' : ' input-group';
14704             
14705             inputblock.cn.push({
14706                 tag :'span',
14707                 cls : 'input-group-addon',
14708                 html : this.after
14709             });
14710         }
14711
14712         var box = {
14713             tag: 'div',
14714             cn: [
14715                 {
14716                     tag: 'input',
14717                     type : 'hidden',
14718                     cls: 'form-hidden-field'
14719                 },
14720                 inputblock
14721             ]
14722             
14723         };
14724         
14725         if(this.multiple){
14726             box = {
14727                 tag: 'div',
14728                 cn: [
14729                     {
14730                         tag: 'input',
14731                         type : 'hidden',
14732                         cls: 'form-hidden-field'
14733                     },
14734                     {
14735                         tag: 'ul',
14736                         cls: 'roo-select2-choices',
14737                         cn:[
14738                             {
14739                                 tag: 'li',
14740                                 cls: 'roo-select2-search-field',
14741                                 cn: [
14742
14743                                     inputblock
14744                                 ]
14745                             }
14746                         ]
14747                     }
14748                 ]
14749             }
14750         };
14751         
14752         var combobox = {
14753             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14754             cn: [
14755                 box
14756             ]
14757         };
14758         
14759         if(!this.multiple && this.showToggleBtn){
14760             
14761             var caret = {
14762                         tag: 'span',
14763                         cls: 'caret'
14764             };
14765             
14766             if (this.caret != false) {
14767                 caret = {
14768                      tag: 'i',
14769                      cls: 'fa fa-' + this.caret
14770                 };
14771                 
14772             }
14773             
14774             combobox.cn.push({
14775                 tag :'span',
14776                 cls : 'input-group-addon btn dropdown-toggle',
14777                 cn : [
14778                     caret,
14779                     {
14780                         tag: 'span',
14781                         cls: 'combobox-clear',
14782                         cn  : [
14783                             {
14784                                 tag : 'i',
14785                                 cls: 'icon-remove'
14786                             }
14787                         ]
14788                     }
14789                 ]
14790
14791             })
14792         }
14793         
14794         if(this.multiple){
14795             combobox.cls += ' roo-select2-container-multi';
14796         }
14797         
14798         var align = this.labelAlign || this.parentLabelAlign();
14799         
14800         if (align ==='left' && this.fieldLabel.length) {
14801
14802             cfg.cn = [
14803                 {
14804                    tag : 'i',
14805                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14806                    tooltip : 'This field is required'
14807                 },
14808                 {
14809                     tag: 'label',
14810                     cls : 'control-label',
14811                     html : this.fieldLabel
14812
14813                 },
14814                 {
14815                     cls : '', 
14816                     cn: [
14817                         combobox
14818                     ]
14819                 }
14820             ];
14821             
14822             var labelCfg = cfg.cn[1];
14823             var contentCfg = cfg.cn[2];
14824             
14825
14826             if(this.indicatorpos == 'right'){
14827                 cfg.cn = [
14828                     {
14829                         tag: 'label',
14830                         'for' :  id,
14831                         cls : 'control-label',
14832                         cn : [
14833                             {
14834                                 tag : 'span',
14835                                 html : this.fieldLabel
14836                             },
14837                             {
14838                                 tag : 'i',
14839                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14840                                 tooltip : 'This field is required'
14841                             }
14842                         ]
14843                     },
14844                     {
14845                         cls : "",
14846                         cn: [
14847                             combobox
14848                         ]
14849                     }
14850
14851                 ];
14852                 
14853                 labelCfg = cfg.cn[0];
14854                 contentCfg = cfg.cn[1];
14855             }
14856             
14857            
14858             
14859             if(this.labelWidth > 12){
14860                 labelCfg.style = "width: " + this.labelWidth + 'px';
14861             }
14862             
14863             if(this.labelWidth < 13 && this.labelmd == 0){
14864                 this.labelmd = this.labelWidth;
14865             }
14866             
14867             if(this.labellg > 0){
14868                 labelCfg.cls += ' col-lg-' + this.labellg;
14869                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14870             }
14871             
14872             if(this.labelmd > 0){
14873                 labelCfg.cls += ' col-md-' + this.labelmd;
14874                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14875             }
14876             
14877             if(this.labelsm > 0){
14878                 labelCfg.cls += ' col-sm-' + this.labelsm;
14879                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14880             }
14881             
14882             if(this.labelxs > 0){
14883                 labelCfg.cls += ' col-xs-' + this.labelxs;
14884                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14885             }
14886                 
14887                 
14888         } else if ( this.fieldLabel.length) {
14889             cfg.cn = [
14890                 {
14891                    tag : 'i',
14892                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14893                    tooltip : 'This field is required'
14894                 },
14895                 {
14896                     tag: 'label',
14897                     cls : 'control-label',
14898                     html : this.fieldLabel
14899
14900                 },
14901                 {
14902                     cls : '', 
14903                     cn: [
14904                         combobox
14905                     ]
14906                 }
14907             ];
14908             
14909             if(this.indicatorpos == 'right'){
14910                 cfg.cn = [
14911                     {
14912                         tag: 'label',
14913                         cls : 'control-label',
14914                         html : this.fieldLabel,
14915                         cn : [
14916                             {
14917                                tag : 'i',
14918                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14919                                tooltip : 'This field is required'
14920                             }
14921                         ]
14922                     },
14923                     {
14924                         cls : '', 
14925                         cn: [
14926                             combobox
14927                         ]
14928                     }
14929                 ];
14930             }
14931         } else {
14932             cfg.cn = combobox;    
14933         }
14934         
14935         
14936         var settings = this;
14937         
14938         ['xs','sm','md','lg'].map(function(size){
14939             if (settings[size]) {
14940                 cfg.cls += ' col-' + size + '-' + settings[size];
14941             }
14942         });
14943         
14944         return cfg;
14945     },
14946     
14947     initTouchView : function()
14948     {
14949         this.renderTouchView();
14950         
14951         this.touchViewEl.on('scroll', function(){
14952             this.el.dom.scrollTop = 0;
14953         }, this);
14954         
14955         this.originalValue = this.getValue();
14956         
14957         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14958         
14959         this.inputEl().on("click", this.showTouchView, this);
14960         if (this.triggerEl) {
14961             this.triggerEl.on("click", this.showTouchView, this);
14962         }
14963         
14964         
14965         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14966         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14967         
14968         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14969         
14970         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14971         this.store.on('load', this.onTouchViewLoad, this);
14972         this.store.on('loadexception', this.onTouchViewLoadException, this);
14973         
14974         if(this.hiddenName){
14975             
14976             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14977             
14978             this.hiddenField.dom.value =
14979                 this.hiddenValue !== undefined ? this.hiddenValue :
14980                 this.value !== undefined ? this.value : '';
14981         
14982             this.el.dom.removeAttribute('name');
14983             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14984         }
14985         
14986         if(this.multiple){
14987             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14988             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14989         }
14990         
14991         if(this.removable && !this.multiple){
14992             var close = this.closeTriggerEl();
14993             if(close){
14994                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14995                 close.on('click', this.removeBtnClick, this, close);
14996             }
14997         }
14998         /*
14999          * fix the bug in Safari iOS8
15000          */
15001         this.inputEl().on("focus", function(e){
15002             document.activeElement.blur();
15003         }, this);
15004         
15005         return;
15006         
15007         
15008     },
15009     
15010     renderTouchView : function()
15011     {
15012         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
15013         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15014         
15015         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15016         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15017         
15018         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15019         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15020         this.touchViewBodyEl.setStyle('overflow', 'auto');
15021         
15022         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15023         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15024         
15025         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15026         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15027         
15028     },
15029     
15030     showTouchView : function()
15031     {
15032         if(this.disabled){
15033             return;
15034         }
15035         
15036         this.touchViewHeaderEl.hide();
15037
15038         if(this.modalTitle.length){
15039             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15040             this.touchViewHeaderEl.show();
15041         }
15042
15043         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15044         this.touchViewEl.show();
15045
15046         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15047         
15048         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15049         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15050
15051         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15052
15053         if(this.modalTitle.length){
15054             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15055         }
15056         
15057         this.touchViewBodyEl.setHeight(bodyHeight);
15058
15059         if(this.animate){
15060             var _this = this;
15061             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15062         }else{
15063             this.touchViewEl.addClass('in');
15064         }
15065
15066         this.doTouchViewQuery();
15067         
15068     },
15069     
15070     hideTouchView : function()
15071     {
15072         this.touchViewEl.removeClass('in');
15073
15074         if(this.animate){
15075             var _this = this;
15076             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15077         }else{
15078             this.touchViewEl.setStyle('display', 'none');
15079         }
15080         
15081     },
15082     
15083     setTouchViewValue : function()
15084     {
15085         if(this.multiple){
15086             this.clearItem();
15087         
15088             var _this = this;
15089
15090             Roo.each(this.tickItems, function(o){
15091                 this.addItem(o);
15092             }, this);
15093         }
15094         
15095         this.hideTouchView();
15096     },
15097     
15098     doTouchViewQuery : function()
15099     {
15100         var qe = {
15101             query: '',
15102             forceAll: true,
15103             combo: this,
15104             cancel:false
15105         };
15106         
15107         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15108             return false;
15109         }
15110         
15111         if(!this.alwaysQuery || this.mode == 'local'){
15112             this.onTouchViewLoad();
15113             return;
15114         }
15115         
15116         this.store.load();
15117     },
15118     
15119     onTouchViewBeforeLoad : function(combo,opts)
15120     {
15121         return;
15122     },
15123
15124     // private
15125     onTouchViewLoad : function()
15126     {
15127         if(this.store.getCount() < 1){
15128             this.onTouchViewEmptyResults();
15129             return;
15130         }
15131         
15132         this.clearTouchView();
15133         
15134         var rawValue = this.getRawValue();
15135         
15136         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15137         
15138         this.tickItems = [];
15139         
15140         this.store.data.each(function(d, rowIndex){
15141             var row = this.touchViewListGroup.createChild(template);
15142             
15143             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15144                 row.addClass(d.data.cls);
15145             }
15146             
15147             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15148                 var cfg = {
15149                     data : d.data,
15150                     html : d.data[this.displayField]
15151                 };
15152                 
15153                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15154                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15155                 }
15156             }
15157             row.removeClass('selected');
15158             if(!this.multiple && this.valueField &&
15159                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15160             {
15161                 // radio buttons..
15162                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15163                 row.addClass('selected');
15164             }
15165             
15166             if(this.multiple && this.valueField &&
15167                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15168             {
15169                 
15170                 // checkboxes...
15171                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15172                 this.tickItems.push(d.data);
15173             }
15174             
15175             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15176             
15177         }, this);
15178         
15179         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15180         
15181         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15182
15183         if(this.modalTitle.length){
15184             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15185         }
15186
15187         var listHeight = this.touchViewListGroup.getHeight();
15188         
15189         var _this = this;
15190         
15191         if(firstChecked && listHeight > bodyHeight){
15192             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15193         }
15194         
15195     },
15196     
15197     onTouchViewLoadException : function()
15198     {
15199         this.hideTouchView();
15200     },
15201     
15202     onTouchViewEmptyResults : function()
15203     {
15204         this.clearTouchView();
15205         
15206         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15207         
15208         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15209         
15210     },
15211     
15212     clearTouchView : function()
15213     {
15214         this.touchViewListGroup.dom.innerHTML = '';
15215     },
15216     
15217     onTouchViewClick : function(e, el, o)
15218     {
15219         e.preventDefault();
15220         
15221         var row = o.row;
15222         var rowIndex = o.rowIndex;
15223         
15224         var r = this.store.getAt(rowIndex);
15225         
15226         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15227             
15228             if(!this.multiple){
15229                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15230                     c.dom.removeAttribute('checked');
15231                 }, this);
15232
15233                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15234
15235                 this.setFromData(r.data);
15236
15237                 var close = this.closeTriggerEl();
15238
15239                 if(close){
15240                     close.show();
15241                 }
15242
15243                 this.hideTouchView();
15244
15245                 this.fireEvent('select', this, r, rowIndex);
15246
15247                 return;
15248             }
15249
15250             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15251                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15252                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15253                 return;
15254             }
15255
15256             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15257             this.addItem(r.data);
15258             this.tickItems.push(r.data);
15259         }
15260     },
15261     
15262     getAutoCreateNativeIOS : function()
15263     {
15264         var cfg = {
15265             cls: 'form-group' //input-group,
15266         };
15267         
15268         var combobox =  {
15269             tag: 'select',
15270             cls : 'roo-ios-select'
15271         };
15272         
15273         if (this.name) {
15274             combobox.name = this.name;
15275         }
15276         
15277         if (this.disabled) {
15278             combobox.disabled = true;
15279         }
15280         
15281         var settings = this;
15282         
15283         ['xs','sm','md','lg'].map(function(size){
15284             if (settings[size]) {
15285                 cfg.cls += ' col-' + size + '-' + settings[size];
15286             }
15287         });
15288         
15289         cfg.cn = combobox;
15290         
15291         return cfg;
15292         
15293     },
15294     
15295     initIOSView : function()
15296     {
15297         this.store.on('load', this.onIOSViewLoad, this);
15298         
15299         return;
15300     },
15301     
15302     onIOSViewLoad : function()
15303     {
15304         if(this.store.getCount() < 1){
15305             return;
15306         }
15307         
15308         this.clearIOSView();
15309         
15310         if(this.allowBlank) {
15311             
15312             var default_text = '-- SELECT --';
15313             
15314             if(this.placeholder.length){
15315                 default_text = this.placeholder;
15316             }
15317             
15318             if(this.emptyTitle.length){
15319                 default_text += ' - ' + this.emptyTitle + ' -';
15320             }
15321             
15322             var opt = this.inputEl().createChild({
15323                 tag: 'option',
15324                 value : 0,
15325                 html : default_text
15326             });
15327             
15328             var o = {};
15329             o[this.valueField] = 0;
15330             o[this.displayField] = default_text;
15331             
15332             this.ios_options.push({
15333                 data : o,
15334                 el : opt
15335             });
15336             
15337         }
15338         
15339         this.store.data.each(function(d, rowIndex){
15340             
15341             var html = '';
15342             
15343             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15344                 html = d.data[this.displayField];
15345             }
15346             
15347             var value = '';
15348             
15349             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15350                 value = d.data[this.valueField];
15351             }
15352             
15353             var option = {
15354                 tag: 'option',
15355                 value : value,
15356                 html : html
15357             };
15358             
15359             if(this.value == d.data[this.valueField]){
15360                 option['selected'] = true;
15361             }
15362             
15363             var opt = this.inputEl().createChild(option);
15364             
15365             this.ios_options.push({
15366                 data : d.data,
15367                 el : opt
15368             });
15369             
15370         }, this);
15371         
15372         this.inputEl().on('change', function(){
15373            this.fireEvent('select', this);
15374         }, this);
15375         
15376     },
15377     
15378     clearIOSView: function()
15379     {
15380         this.inputEl().dom.innerHTML = '';
15381         
15382         this.ios_options = [];
15383     },
15384     
15385     setIOSValue: function(v)
15386     {
15387         this.value = v;
15388         
15389         if(!this.ios_options){
15390             return;
15391         }
15392         
15393         Roo.each(this.ios_options, function(opts){
15394            
15395            opts.el.dom.removeAttribute('selected');
15396            
15397            if(opts.data[this.valueField] != v){
15398                return;
15399            }
15400            
15401            opts.el.dom.setAttribute('selected', true);
15402            
15403         }, this);
15404     }
15405
15406     /** 
15407     * @cfg {Boolean} grow 
15408     * @hide 
15409     */
15410     /** 
15411     * @cfg {Number} growMin 
15412     * @hide 
15413     */
15414     /** 
15415     * @cfg {Number} growMax 
15416     * @hide 
15417     */
15418     /**
15419      * @hide
15420      * @method autoSize
15421      */
15422 });
15423
15424 Roo.apply(Roo.bootstrap.ComboBox,  {
15425     
15426     header : {
15427         tag: 'div',
15428         cls: 'modal-header',
15429         cn: [
15430             {
15431                 tag: 'h4',
15432                 cls: 'modal-title'
15433             }
15434         ]
15435     },
15436     
15437     body : {
15438         tag: 'div',
15439         cls: 'modal-body',
15440         cn: [
15441             {
15442                 tag: 'ul',
15443                 cls: 'list-group'
15444             }
15445         ]
15446     },
15447     
15448     listItemRadio : {
15449         tag: 'li',
15450         cls: 'list-group-item',
15451         cn: [
15452             {
15453                 tag: 'span',
15454                 cls: 'roo-combobox-list-group-item-value'
15455             },
15456             {
15457                 tag: 'div',
15458                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15459                 cn: [
15460                     {
15461                         tag: 'input',
15462                         type: 'radio'
15463                     },
15464                     {
15465                         tag: 'label'
15466                     }
15467                 ]
15468             }
15469         ]
15470     },
15471     
15472     listItemCheckbox : {
15473         tag: 'li',
15474         cls: 'list-group-item',
15475         cn: [
15476             {
15477                 tag: 'span',
15478                 cls: 'roo-combobox-list-group-item-value'
15479             },
15480             {
15481                 tag: 'div',
15482                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15483                 cn: [
15484                     {
15485                         tag: 'input',
15486                         type: 'checkbox'
15487                     },
15488                     {
15489                         tag: 'label'
15490                     }
15491                 ]
15492             }
15493         ]
15494     },
15495     
15496     emptyResult : {
15497         tag: 'div',
15498         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15499     },
15500     
15501     footer : {
15502         tag: 'div',
15503         cls: 'modal-footer',
15504         cn: [
15505             {
15506                 tag: 'div',
15507                 cls: 'row',
15508                 cn: [
15509                     {
15510                         tag: 'div',
15511                         cls: 'col-xs-6 text-left',
15512                         cn: {
15513                             tag: 'button',
15514                             cls: 'btn btn-danger roo-touch-view-cancel',
15515                             html: 'Cancel'
15516                         }
15517                     },
15518                     {
15519                         tag: 'div',
15520                         cls: 'col-xs-6 text-right',
15521                         cn: {
15522                             tag: 'button',
15523                             cls: 'btn btn-success roo-touch-view-ok',
15524                             html: 'OK'
15525                         }
15526                     }
15527                 ]
15528             }
15529         ]
15530         
15531     }
15532 });
15533
15534 Roo.apply(Roo.bootstrap.ComboBox,  {
15535     
15536     touchViewTemplate : {
15537         tag: 'div',
15538         cls: 'modal fade roo-combobox-touch-view',
15539         cn: [
15540             {
15541                 tag: 'div',
15542                 cls: 'modal-dialog',
15543                 style : 'position:fixed', // we have to fix position....
15544                 cn: [
15545                     {
15546                         tag: 'div',
15547                         cls: 'modal-content',
15548                         cn: [
15549                             Roo.bootstrap.ComboBox.header,
15550                             Roo.bootstrap.ComboBox.body,
15551                             Roo.bootstrap.ComboBox.footer
15552                         ]
15553                     }
15554                 ]
15555             }
15556         ]
15557     }
15558 });/*
15559  * Based on:
15560  * Ext JS Library 1.1.1
15561  * Copyright(c) 2006-2007, Ext JS, LLC.
15562  *
15563  * Originally Released Under LGPL - original licence link has changed is not relivant.
15564  *
15565  * Fork - LGPL
15566  * <script type="text/javascript">
15567  */
15568
15569 /**
15570  * @class Roo.View
15571  * @extends Roo.util.Observable
15572  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15573  * This class also supports single and multi selection modes. <br>
15574  * Create a data model bound view:
15575  <pre><code>
15576  var store = new Roo.data.Store(...);
15577
15578  var view = new Roo.View({
15579     el : "my-element",
15580     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15581  
15582     singleSelect: true,
15583     selectedClass: "ydataview-selected",
15584     store: store
15585  });
15586
15587  // listen for node click?
15588  view.on("click", function(vw, index, node, e){
15589  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15590  });
15591
15592  // load XML data
15593  dataModel.load("foobar.xml");
15594  </code></pre>
15595  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15596  * <br><br>
15597  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15598  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15599  * 
15600  * Note: old style constructor is still suported (container, template, config)
15601  * 
15602  * @constructor
15603  * Create a new View
15604  * @param {Object} config The config object
15605  * 
15606  */
15607 Roo.View = function(config, depreciated_tpl, depreciated_config){
15608     
15609     this.parent = false;
15610     
15611     if (typeof(depreciated_tpl) == 'undefined') {
15612         // new way.. - universal constructor.
15613         Roo.apply(this, config);
15614         this.el  = Roo.get(this.el);
15615     } else {
15616         // old format..
15617         this.el  = Roo.get(config);
15618         this.tpl = depreciated_tpl;
15619         Roo.apply(this, depreciated_config);
15620     }
15621     this.wrapEl  = this.el.wrap().wrap();
15622     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15623     
15624     
15625     if(typeof(this.tpl) == "string"){
15626         this.tpl = new Roo.Template(this.tpl);
15627     } else {
15628         // support xtype ctors..
15629         this.tpl = new Roo.factory(this.tpl, Roo);
15630     }
15631     
15632     
15633     this.tpl.compile();
15634     
15635     /** @private */
15636     this.addEvents({
15637         /**
15638          * @event beforeclick
15639          * Fires before a click is processed. Returns false to cancel the default action.
15640          * @param {Roo.View} this
15641          * @param {Number} index The index of the target node
15642          * @param {HTMLElement} node The target node
15643          * @param {Roo.EventObject} e The raw event object
15644          */
15645             "beforeclick" : true,
15646         /**
15647          * @event click
15648          * Fires when a template node is clicked.
15649          * @param {Roo.View} this
15650          * @param {Number} index The index of the target node
15651          * @param {HTMLElement} node The target node
15652          * @param {Roo.EventObject} e The raw event object
15653          */
15654             "click" : true,
15655         /**
15656          * @event dblclick
15657          * Fires when a template node is double clicked.
15658          * @param {Roo.View} this
15659          * @param {Number} index The index of the target node
15660          * @param {HTMLElement} node The target node
15661          * @param {Roo.EventObject} e The raw event object
15662          */
15663             "dblclick" : true,
15664         /**
15665          * @event contextmenu
15666          * Fires when a template node is right clicked.
15667          * @param {Roo.View} this
15668          * @param {Number} index The index of the target node
15669          * @param {HTMLElement} node The target node
15670          * @param {Roo.EventObject} e The raw event object
15671          */
15672             "contextmenu" : true,
15673         /**
15674          * @event selectionchange
15675          * Fires when the selected nodes change.
15676          * @param {Roo.View} this
15677          * @param {Array} selections Array of the selected nodes
15678          */
15679             "selectionchange" : true,
15680     
15681         /**
15682          * @event beforeselect
15683          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15684          * @param {Roo.View} this
15685          * @param {HTMLElement} node The node to be selected
15686          * @param {Array} selections Array of currently selected nodes
15687          */
15688             "beforeselect" : true,
15689         /**
15690          * @event preparedata
15691          * Fires on every row to render, to allow you to change the data.
15692          * @param {Roo.View} this
15693          * @param {Object} data to be rendered (change this)
15694          */
15695           "preparedata" : true
15696           
15697           
15698         });
15699
15700
15701
15702     this.el.on({
15703         "click": this.onClick,
15704         "dblclick": this.onDblClick,
15705         "contextmenu": this.onContextMenu,
15706         scope:this
15707     });
15708
15709     this.selections = [];
15710     this.nodes = [];
15711     this.cmp = new Roo.CompositeElementLite([]);
15712     if(this.store){
15713         this.store = Roo.factory(this.store, Roo.data);
15714         this.setStore(this.store, true);
15715     }
15716     
15717     if ( this.footer && this.footer.xtype) {
15718            
15719          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15720         
15721         this.footer.dataSource = this.store;
15722         this.footer.container = fctr;
15723         this.footer = Roo.factory(this.footer, Roo);
15724         fctr.insertFirst(this.el);
15725         
15726         // this is a bit insane - as the paging toolbar seems to detach the el..
15727 //        dom.parentNode.parentNode.parentNode
15728          // they get detached?
15729     }
15730     
15731     
15732     Roo.View.superclass.constructor.call(this);
15733     
15734     
15735 };
15736
15737 Roo.extend(Roo.View, Roo.util.Observable, {
15738     
15739      /**
15740      * @cfg {Roo.data.Store} store Data store to load data from.
15741      */
15742     store : false,
15743     
15744     /**
15745      * @cfg {String|Roo.Element} el The container element.
15746      */
15747     el : '',
15748     
15749     /**
15750      * @cfg {String|Roo.Template} tpl The template used by this View 
15751      */
15752     tpl : false,
15753     /**
15754      * @cfg {String} dataName the named area of the template to use as the data area
15755      *                          Works with domtemplates roo-name="name"
15756      */
15757     dataName: false,
15758     /**
15759      * @cfg {String} selectedClass The css class to add to selected nodes
15760      */
15761     selectedClass : "x-view-selected",
15762      /**
15763      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15764      */
15765     emptyText : "",
15766     
15767     /**
15768      * @cfg {String} text to display on mask (default Loading)
15769      */
15770     mask : false,
15771     /**
15772      * @cfg {Boolean} multiSelect Allow multiple selection
15773      */
15774     multiSelect : false,
15775     /**
15776      * @cfg {Boolean} singleSelect Allow single selection
15777      */
15778     singleSelect:  false,
15779     
15780     /**
15781      * @cfg {Boolean} toggleSelect - selecting 
15782      */
15783     toggleSelect : false,
15784     
15785     /**
15786      * @cfg {Boolean} tickable - selecting 
15787      */
15788     tickable : false,
15789     
15790     /**
15791      * Returns the element this view is bound to.
15792      * @return {Roo.Element}
15793      */
15794     getEl : function(){
15795         return this.wrapEl;
15796     },
15797     
15798     
15799
15800     /**
15801      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15802      */
15803     refresh : function(){
15804         //Roo.log('refresh');
15805         var t = this.tpl;
15806         
15807         // if we are using something like 'domtemplate', then
15808         // the what gets used is:
15809         // t.applySubtemplate(NAME, data, wrapping data..)
15810         // the outer template then get' applied with
15811         //     the store 'extra data'
15812         // and the body get's added to the
15813         //      roo-name="data" node?
15814         //      <span class='roo-tpl-{name}'></span> ?????
15815         
15816         
15817         
15818         this.clearSelections();
15819         this.el.update("");
15820         var html = [];
15821         var records = this.store.getRange();
15822         if(records.length < 1) {
15823             
15824             // is this valid??  = should it render a template??
15825             
15826             this.el.update(this.emptyText);
15827             return;
15828         }
15829         var el = this.el;
15830         if (this.dataName) {
15831             this.el.update(t.apply(this.store.meta)); //????
15832             el = this.el.child('.roo-tpl-' + this.dataName);
15833         }
15834         
15835         for(var i = 0, len = records.length; i < len; i++){
15836             var data = this.prepareData(records[i].data, i, records[i]);
15837             this.fireEvent("preparedata", this, data, i, records[i]);
15838             
15839             var d = Roo.apply({}, data);
15840             
15841             if(this.tickable){
15842                 Roo.apply(d, {'roo-id' : Roo.id()});
15843                 
15844                 var _this = this;
15845             
15846                 Roo.each(this.parent.item, function(item){
15847                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15848                         return;
15849                     }
15850                     Roo.apply(d, {'roo-data-checked' : 'checked'});
15851                 });
15852             }
15853             
15854             html[html.length] = Roo.util.Format.trim(
15855                 this.dataName ?
15856                     t.applySubtemplate(this.dataName, d, this.store.meta) :
15857                     t.apply(d)
15858             );
15859         }
15860         
15861         
15862         
15863         el.update(html.join(""));
15864         this.nodes = el.dom.childNodes;
15865         this.updateIndexes(0);
15866     },
15867     
15868
15869     /**
15870      * Function to override to reformat the data that is sent to
15871      * the template for each node.
15872      * DEPRICATED - use the preparedata event handler.
15873      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15874      * a JSON object for an UpdateManager bound view).
15875      */
15876     prepareData : function(data, index, record)
15877     {
15878         this.fireEvent("preparedata", this, data, index, record);
15879         return data;
15880     },
15881
15882     onUpdate : function(ds, record){
15883         // Roo.log('on update');   
15884         this.clearSelections();
15885         var index = this.store.indexOf(record);
15886         var n = this.nodes[index];
15887         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15888         n.parentNode.removeChild(n);
15889         this.updateIndexes(index, index);
15890     },
15891
15892     
15893     
15894 // --------- FIXME     
15895     onAdd : function(ds, records, index)
15896     {
15897         //Roo.log(['on Add', ds, records, index] );        
15898         this.clearSelections();
15899         if(this.nodes.length == 0){
15900             this.refresh();
15901             return;
15902         }
15903         var n = this.nodes[index];
15904         for(var i = 0, len = records.length; i < len; i++){
15905             var d = this.prepareData(records[i].data, i, records[i]);
15906             if(n){
15907                 this.tpl.insertBefore(n, d);
15908             }else{
15909                 
15910                 this.tpl.append(this.el, d);
15911             }
15912         }
15913         this.updateIndexes(index);
15914     },
15915
15916     onRemove : function(ds, record, index){
15917        // Roo.log('onRemove');
15918         this.clearSelections();
15919         var el = this.dataName  ?
15920             this.el.child('.roo-tpl-' + this.dataName) :
15921             this.el; 
15922         
15923         el.dom.removeChild(this.nodes[index]);
15924         this.updateIndexes(index);
15925     },
15926
15927     /**
15928      * Refresh an individual node.
15929      * @param {Number} index
15930      */
15931     refreshNode : function(index){
15932         this.onUpdate(this.store, this.store.getAt(index));
15933     },
15934
15935     updateIndexes : function(startIndex, endIndex){
15936         var ns = this.nodes;
15937         startIndex = startIndex || 0;
15938         endIndex = endIndex || ns.length - 1;
15939         for(var i = startIndex; i <= endIndex; i++){
15940             ns[i].nodeIndex = i;
15941         }
15942     },
15943
15944     /**
15945      * Changes the data store this view uses and refresh the view.
15946      * @param {Store} store
15947      */
15948     setStore : function(store, initial){
15949         if(!initial && this.store){
15950             this.store.un("datachanged", this.refresh);
15951             this.store.un("add", this.onAdd);
15952             this.store.un("remove", this.onRemove);
15953             this.store.un("update", this.onUpdate);
15954             this.store.un("clear", this.refresh);
15955             this.store.un("beforeload", this.onBeforeLoad);
15956             this.store.un("load", this.onLoad);
15957             this.store.un("loadexception", this.onLoad);
15958         }
15959         if(store){
15960           
15961             store.on("datachanged", this.refresh, this);
15962             store.on("add", this.onAdd, this);
15963             store.on("remove", this.onRemove, this);
15964             store.on("update", this.onUpdate, this);
15965             store.on("clear", this.refresh, this);
15966             store.on("beforeload", this.onBeforeLoad, this);
15967             store.on("load", this.onLoad, this);
15968             store.on("loadexception", this.onLoad, this);
15969         }
15970         
15971         if(store){
15972             this.refresh();
15973         }
15974     },
15975     /**
15976      * onbeforeLoad - masks the loading area.
15977      *
15978      */
15979     onBeforeLoad : function(store,opts)
15980     {
15981          //Roo.log('onBeforeLoad');   
15982         if (!opts.add) {
15983             this.el.update("");
15984         }
15985         this.el.mask(this.mask ? this.mask : "Loading" ); 
15986     },
15987     onLoad : function ()
15988     {
15989         this.el.unmask();
15990     },
15991     
15992
15993     /**
15994      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15995      * @param {HTMLElement} node
15996      * @return {HTMLElement} The template node
15997      */
15998     findItemFromChild : function(node){
15999         var el = this.dataName  ?
16000             this.el.child('.roo-tpl-' + this.dataName,true) :
16001             this.el.dom; 
16002         
16003         if(!node || node.parentNode == el){
16004                     return node;
16005             }
16006             var p = node.parentNode;
16007             while(p && p != el){
16008             if(p.parentNode == el){
16009                 return p;
16010             }
16011             p = p.parentNode;
16012         }
16013             return null;
16014     },
16015
16016     /** @ignore */
16017     onClick : function(e){
16018         var item = this.findItemFromChild(e.getTarget());
16019         if(item){
16020             var index = this.indexOf(item);
16021             if(this.onItemClick(item, index, e) !== false){
16022                 this.fireEvent("click", this, index, item, e);
16023             }
16024         }else{
16025             this.clearSelections();
16026         }
16027     },
16028
16029     /** @ignore */
16030     onContextMenu : function(e){
16031         var item = this.findItemFromChild(e.getTarget());
16032         if(item){
16033             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16034         }
16035     },
16036
16037     /** @ignore */
16038     onDblClick : function(e){
16039         var item = this.findItemFromChild(e.getTarget());
16040         if(item){
16041             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16042         }
16043     },
16044
16045     onItemClick : function(item, index, e)
16046     {
16047         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16048             return false;
16049         }
16050         if (this.toggleSelect) {
16051             var m = this.isSelected(item) ? 'unselect' : 'select';
16052             //Roo.log(m);
16053             var _t = this;
16054             _t[m](item, true, false);
16055             return true;
16056         }
16057         if(this.multiSelect || this.singleSelect){
16058             if(this.multiSelect && e.shiftKey && this.lastSelection){
16059                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16060             }else{
16061                 this.select(item, this.multiSelect && e.ctrlKey);
16062                 this.lastSelection = item;
16063             }
16064             
16065             if(!this.tickable){
16066                 e.preventDefault();
16067             }
16068             
16069         }
16070         return true;
16071     },
16072
16073     /**
16074      * Get the number of selected nodes.
16075      * @return {Number}
16076      */
16077     getSelectionCount : function(){
16078         return this.selections.length;
16079     },
16080
16081     /**
16082      * Get the currently selected nodes.
16083      * @return {Array} An array of HTMLElements
16084      */
16085     getSelectedNodes : function(){
16086         return this.selections;
16087     },
16088
16089     /**
16090      * Get the indexes of the selected nodes.
16091      * @return {Array}
16092      */
16093     getSelectedIndexes : function(){
16094         var indexes = [], s = this.selections;
16095         for(var i = 0, len = s.length; i < len; i++){
16096             indexes.push(s[i].nodeIndex);
16097         }
16098         return indexes;
16099     },
16100
16101     /**
16102      * Clear all selections
16103      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16104      */
16105     clearSelections : function(suppressEvent){
16106         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16107             this.cmp.elements = this.selections;
16108             this.cmp.removeClass(this.selectedClass);
16109             this.selections = [];
16110             if(!suppressEvent){
16111                 this.fireEvent("selectionchange", this, this.selections);
16112             }
16113         }
16114     },
16115
16116     /**
16117      * Returns true if the passed node is selected
16118      * @param {HTMLElement/Number} node The node or node index
16119      * @return {Boolean}
16120      */
16121     isSelected : function(node){
16122         var s = this.selections;
16123         if(s.length < 1){
16124             return false;
16125         }
16126         node = this.getNode(node);
16127         return s.indexOf(node) !== -1;
16128     },
16129
16130     /**
16131      * Selects nodes.
16132      * @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
16133      * @param {Boolean} keepExisting (optional) true to keep existing selections
16134      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16135      */
16136     select : function(nodeInfo, keepExisting, suppressEvent){
16137         if(nodeInfo instanceof Array){
16138             if(!keepExisting){
16139                 this.clearSelections(true);
16140             }
16141             for(var i = 0, len = nodeInfo.length; i < len; i++){
16142                 this.select(nodeInfo[i], true, true);
16143             }
16144             return;
16145         } 
16146         var node = this.getNode(nodeInfo);
16147         if(!node || this.isSelected(node)){
16148             return; // already selected.
16149         }
16150         if(!keepExisting){
16151             this.clearSelections(true);
16152         }
16153         
16154         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16155             Roo.fly(node).addClass(this.selectedClass);
16156             this.selections.push(node);
16157             if(!suppressEvent){
16158                 this.fireEvent("selectionchange", this, this.selections);
16159             }
16160         }
16161         
16162         
16163     },
16164       /**
16165      * Unselects nodes.
16166      * @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
16167      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16168      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16169      */
16170     unselect : function(nodeInfo, keepExisting, suppressEvent)
16171     {
16172         if(nodeInfo instanceof Array){
16173             Roo.each(this.selections, function(s) {
16174                 this.unselect(s, nodeInfo);
16175             }, this);
16176             return;
16177         }
16178         var node = this.getNode(nodeInfo);
16179         if(!node || !this.isSelected(node)){
16180             //Roo.log("not selected");
16181             return; // not selected.
16182         }
16183         // fireevent???
16184         var ns = [];
16185         Roo.each(this.selections, function(s) {
16186             if (s == node ) {
16187                 Roo.fly(node).removeClass(this.selectedClass);
16188
16189                 return;
16190             }
16191             ns.push(s);
16192         },this);
16193         
16194         this.selections= ns;
16195         this.fireEvent("selectionchange", this, this.selections);
16196     },
16197
16198     /**
16199      * Gets a template node.
16200      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16201      * @return {HTMLElement} The node or null if it wasn't found
16202      */
16203     getNode : function(nodeInfo){
16204         if(typeof nodeInfo == "string"){
16205             return document.getElementById(nodeInfo);
16206         }else if(typeof nodeInfo == "number"){
16207             return this.nodes[nodeInfo];
16208         }
16209         return nodeInfo;
16210     },
16211
16212     /**
16213      * Gets a range template nodes.
16214      * @param {Number} startIndex
16215      * @param {Number} endIndex
16216      * @return {Array} An array of nodes
16217      */
16218     getNodes : function(start, end){
16219         var ns = this.nodes;
16220         start = start || 0;
16221         end = typeof end == "undefined" ? ns.length - 1 : end;
16222         var nodes = [];
16223         if(start <= end){
16224             for(var i = start; i <= end; i++){
16225                 nodes.push(ns[i]);
16226             }
16227         } else{
16228             for(var i = start; i >= end; i--){
16229                 nodes.push(ns[i]);
16230             }
16231         }
16232         return nodes;
16233     },
16234
16235     /**
16236      * Finds the index of the passed node
16237      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16238      * @return {Number} The index of the node or -1
16239      */
16240     indexOf : function(node){
16241         node = this.getNode(node);
16242         if(typeof node.nodeIndex == "number"){
16243             return node.nodeIndex;
16244         }
16245         var ns = this.nodes;
16246         for(var i = 0, len = ns.length; i < len; i++){
16247             if(ns[i] == node){
16248                 return i;
16249             }
16250         }
16251         return -1;
16252     }
16253 });
16254 /*
16255  * - LGPL
16256  *
16257  * based on jquery fullcalendar
16258  * 
16259  */
16260
16261 Roo.bootstrap = Roo.bootstrap || {};
16262 /**
16263  * @class Roo.bootstrap.Calendar
16264  * @extends Roo.bootstrap.Component
16265  * Bootstrap Calendar class
16266  * @cfg {Boolean} loadMask (true|false) default false
16267  * @cfg {Object} header generate the user specific header of the calendar, default false
16268
16269  * @constructor
16270  * Create a new Container
16271  * @param {Object} config The config object
16272  */
16273
16274
16275
16276 Roo.bootstrap.Calendar = function(config){
16277     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16278      this.addEvents({
16279         /**
16280              * @event select
16281              * Fires when a date is selected
16282              * @param {DatePicker} this
16283              * @param {Date} date The selected date
16284              */
16285         'select': true,
16286         /**
16287              * @event monthchange
16288              * Fires when the displayed month changes 
16289              * @param {DatePicker} this
16290              * @param {Date} date The selected month
16291              */
16292         'monthchange': true,
16293         /**
16294              * @event evententer
16295              * Fires when mouse over an event
16296              * @param {Calendar} this
16297              * @param {event} Event
16298              */
16299         'evententer': true,
16300         /**
16301              * @event eventleave
16302              * Fires when the mouse leaves an
16303              * @param {Calendar} this
16304              * @param {event}
16305              */
16306         'eventleave': true,
16307         /**
16308              * @event eventclick
16309              * Fires when the mouse click an
16310              * @param {Calendar} this
16311              * @param {event}
16312              */
16313         'eventclick': true
16314         
16315     });
16316
16317 };
16318
16319 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16320     
16321      /**
16322      * @cfg {Number} startDay
16323      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16324      */
16325     startDay : 0,
16326     
16327     loadMask : false,
16328     
16329     header : false,
16330       
16331     getAutoCreate : function(){
16332         
16333         
16334         var fc_button = function(name, corner, style, content ) {
16335             return Roo.apply({},{
16336                 tag : 'span',
16337                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16338                          (corner.length ?
16339                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16340                             ''
16341                         ),
16342                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16343                 unselectable: 'on'
16344             });
16345         };
16346         
16347         var header = {};
16348         
16349         if(!this.header){
16350             header = {
16351                 tag : 'table',
16352                 cls : 'fc-header',
16353                 style : 'width:100%',
16354                 cn : [
16355                     {
16356                         tag: 'tr',
16357                         cn : [
16358                             {
16359                                 tag : 'td',
16360                                 cls : 'fc-header-left',
16361                                 cn : [
16362                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16363                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16364                                     { tag: 'span', cls: 'fc-header-space' },
16365                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16366
16367
16368                                 ]
16369                             },
16370
16371                             {
16372                                 tag : 'td',
16373                                 cls : 'fc-header-center',
16374                                 cn : [
16375                                     {
16376                                         tag: 'span',
16377                                         cls: 'fc-header-title',
16378                                         cn : {
16379                                             tag: 'H2',
16380                                             html : 'month / year'
16381                                         }
16382                                     }
16383
16384                                 ]
16385                             },
16386                             {
16387                                 tag : 'td',
16388                                 cls : 'fc-header-right',
16389                                 cn : [
16390                               /*      fc_button('month', 'left', '', 'month' ),
16391                                     fc_button('week', '', '', 'week' ),
16392                                     fc_button('day', 'right', '', 'day' )
16393                                 */    
16394
16395                                 ]
16396                             }
16397
16398                         ]
16399                     }
16400                 ]
16401             };
16402         }
16403         
16404         header = this.header;
16405         
16406        
16407         var cal_heads = function() {
16408             var ret = [];
16409             // fixme - handle this.
16410             
16411             for (var i =0; i < Date.dayNames.length; i++) {
16412                 var d = Date.dayNames[i];
16413                 ret.push({
16414                     tag: 'th',
16415                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16416                     html : d.substring(0,3)
16417                 });
16418                 
16419             }
16420             ret[0].cls += ' fc-first';
16421             ret[6].cls += ' fc-last';
16422             return ret;
16423         };
16424         var cal_cell = function(n) {
16425             return  {
16426                 tag: 'td',
16427                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16428                 cn : [
16429                     {
16430                         cn : [
16431                             {
16432                                 cls: 'fc-day-number',
16433                                 html: 'D'
16434                             },
16435                             {
16436                                 cls: 'fc-day-content',
16437                              
16438                                 cn : [
16439                                      {
16440                                         style: 'position: relative;' // height: 17px;
16441                                     }
16442                                 ]
16443                             }
16444                             
16445                             
16446                         ]
16447                     }
16448                 ]
16449                 
16450             }
16451         };
16452         var cal_rows = function() {
16453             
16454             var ret = [];
16455             for (var r = 0; r < 6; r++) {
16456                 var row= {
16457                     tag : 'tr',
16458                     cls : 'fc-week',
16459                     cn : []
16460                 };
16461                 
16462                 for (var i =0; i < Date.dayNames.length; i++) {
16463                     var d = Date.dayNames[i];
16464                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16465
16466                 }
16467                 row.cn[0].cls+=' fc-first';
16468                 row.cn[0].cn[0].style = 'min-height:90px';
16469                 row.cn[6].cls+=' fc-last';
16470                 ret.push(row);
16471                 
16472             }
16473             ret[0].cls += ' fc-first';
16474             ret[4].cls += ' fc-prev-last';
16475             ret[5].cls += ' fc-last';
16476             return ret;
16477             
16478         };
16479         
16480         var cal_table = {
16481             tag: 'table',
16482             cls: 'fc-border-separate',
16483             style : 'width:100%',
16484             cellspacing  : 0,
16485             cn : [
16486                 { 
16487                     tag: 'thead',
16488                     cn : [
16489                         { 
16490                             tag: 'tr',
16491                             cls : 'fc-first fc-last',
16492                             cn : cal_heads()
16493                         }
16494                     ]
16495                 },
16496                 { 
16497                     tag: 'tbody',
16498                     cn : cal_rows()
16499                 }
16500                   
16501             ]
16502         };
16503          
16504          var cfg = {
16505             cls : 'fc fc-ltr',
16506             cn : [
16507                 header,
16508                 {
16509                     cls : 'fc-content',
16510                     style : "position: relative;",
16511                     cn : [
16512                         {
16513                             cls : 'fc-view fc-view-month fc-grid',
16514                             style : 'position: relative',
16515                             unselectable : 'on',
16516                             cn : [
16517                                 {
16518                                     cls : 'fc-event-container',
16519                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16520                                 },
16521                                 cal_table
16522                             ]
16523                         }
16524                     ]
16525     
16526                 }
16527            ] 
16528             
16529         };
16530         
16531          
16532         
16533         return cfg;
16534     },
16535     
16536     
16537     initEvents : function()
16538     {
16539         if(!this.store){
16540             throw "can not find store for calendar";
16541         }
16542         
16543         var mark = {
16544             tag: "div",
16545             cls:"x-dlg-mask",
16546             style: "text-align:center",
16547             cn: [
16548                 {
16549                     tag: "div",
16550                     style: "background-color:white;width:50%;margin:250 auto",
16551                     cn: [
16552                         {
16553                             tag: "img",
16554                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16555                         },
16556                         {
16557                             tag: "span",
16558                             html: "Loading"
16559                         }
16560                         
16561                     ]
16562                 }
16563             ]
16564         };
16565         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16566         
16567         var size = this.el.select('.fc-content', true).first().getSize();
16568         this.maskEl.setSize(size.width, size.height);
16569         this.maskEl.enableDisplayMode("block");
16570         if(!this.loadMask){
16571             this.maskEl.hide();
16572         }
16573         
16574         this.store = Roo.factory(this.store, Roo.data);
16575         this.store.on('load', this.onLoad, this);
16576         this.store.on('beforeload', this.onBeforeLoad, this);
16577         
16578         this.resize();
16579         
16580         this.cells = this.el.select('.fc-day',true);
16581         //Roo.log(this.cells);
16582         this.textNodes = this.el.query('.fc-day-number');
16583         this.cells.addClassOnOver('fc-state-hover');
16584         
16585         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16586         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16587         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16588         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16589         
16590         this.on('monthchange', this.onMonthChange, this);
16591         
16592         this.update(new Date().clearTime());
16593     },
16594     
16595     resize : function() {
16596         var sz  = this.el.getSize();
16597         
16598         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16599         this.el.select('.fc-day-content div',true).setHeight(34);
16600     },
16601     
16602     
16603     // private
16604     showPrevMonth : function(e){
16605         this.update(this.activeDate.add("mo", -1));
16606     },
16607     showToday : function(e){
16608         this.update(new Date().clearTime());
16609     },
16610     // private
16611     showNextMonth : function(e){
16612         this.update(this.activeDate.add("mo", 1));
16613     },
16614
16615     // private
16616     showPrevYear : function(){
16617         this.update(this.activeDate.add("y", -1));
16618     },
16619
16620     // private
16621     showNextYear : function(){
16622         this.update(this.activeDate.add("y", 1));
16623     },
16624
16625     
16626    // private
16627     update : function(date)
16628     {
16629         var vd = this.activeDate;
16630         this.activeDate = date;
16631 //        if(vd && this.el){
16632 //            var t = date.getTime();
16633 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16634 //                Roo.log('using add remove');
16635 //                
16636 //                this.fireEvent('monthchange', this, date);
16637 //                
16638 //                this.cells.removeClass("fc-state-highlight");
16639 //                this.cells.each(function(c){
16640 //                   if(c.dateValue == t){
16641 //                       c.addClass("fc-state-highlight");
16642 //                       setTimeout(function(){
16643 //                            try{c.dom.firstChild.focus();}catch(e){}
16644 //                       }, 50);
16645 //                       return false;
16646 //                   }
16647 //                   return true;
16648 //                });
16649 //                return;
16650 //            }
16651 //        }
16652         
16653         var days = date.getDaysInMonth();
16654         
16655         var firstOfMonth = date.getFirstDateOfMonth();
16656         var startingPos = firstOfMonth.getDay()-this.startDay;
16657         
16658         if(startingPos < this.startDay){
16659             startingPos += 7;
16660         }
16661         
16662         var pm = date.add(Date.MONTH, -1);
16663         var prevStart = pm.getDaysInMonth()-startingPos;
16664 //        
16665         this.cells = this.el.select('.fc-day',true);
16666         this.textNodes = this.el.query('.fc-day-number');
16667         this.cells.addClassOnOver('fc-state-hover');
16668         
16669         var cells = this.cells.elements;
16670         var textEls = this.textNodes;
16671         
16672         Roo.each(cells, function(cell){
16673             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16674         });
16675         
16676         days += startingPos;
16677
16678         // convert everything to numbers so it's fast
16679         var day = 86400000;
16680         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16681         //Roo.log(d);
16682         //Roo.log(pm);
16683         //Roo.log(prevStart);
16684         
16685         var today = new Date().clearTime().getTime();
16686         var sel = date.clearTime().getTime();
16687         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16688         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16689         var ddMatch = this.disabledDatesRE;
16690         var ddText = this.disabledDatesText;
16691         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16692         var ddaysText = this.disabledDaysText;
16693         var format = this.format;
16694         
16695         var setCellClass = function(cal, cell){
16696             cell.row = 0;
16697             cell.events = [];
16698             cell.more = [];
16699             //Roo.log('set Cell Class');
16700             cell.title = "";
16701             var t = d.getTime();
16702             
16703             //Roo.log(d);
16704             
16705             cell.dateValue = t;
16706             if(t == today){
16707                 cell.className += " fc-today";
16708                 cell.className += " fc-state-highlight";
16709                 cell.title = cal.todayText;
16710             }
16711             if(t == sel){
16712                 // disable highlight in other month..
16713                 //cell.className += " fc-state-highlight";
16714                 
16715             }
16716             // disabling
16717             if(t < min) {
16718                 cell.className = " fc-state-disabled";
16719                 cell.title = cal.minText;
16720                 return;
16721             }
16722             if(t > max) {
16723                 cell.className = " fc-state-disabled";
16724                 cell.title = cal.maxText;
16725                 return;
16726             }
16727             if(ddays){
16728                 if(ddays.indexOf(d.getDay()) != -1){
16729                     cell.title = ddaysText;
16730                     cell.className = " fc-state-disabled";
16731                 }
16732             }
16733             if(ddMatch && format){
16734                 var fvalue = d.dateFormat(format);
16735                 if(ddMatch.test(fvalue)){
16736                     cell.title = ddText.replace("%0", fvalue);
16737                     cell.className = " fc-state-disabled";
16738                 }
16739             }
16740             
16741             if (!cell.initialClassName) {
16742                 cell.initialClassName = cell.dom.className;
16743             }
16744             
16745             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16746         };
16747
16748         var i = 0;
16749         
16750         for(; i < startingPos; i++) {
16751             textEls[i].innerHTML = (++prevStart);
16752             d.setDate(d.getDate()+1);
16753             
16754             cells[i].className = "fc-past fc-other-month";
16755             setCellClass(this, cells[i]);
16756         }
16757         
16758         var intDay = 0;
16759         
16760         for(; i < days; i++){
16761             intDay = i - startingPos + 1;
16762             textEls[i].innerHTML = (intDay);
16763             d.setDate(d.getDate()+1);
16764             
16765             cells[i].className = ''; // "x-date-active";
16766             setCellClass(this, cells[i]);
16767         }
16768         var extraDays = 0;
16769         
16770         for(; i < 42; i++) {
16771             textEls[i].innerHTML = (++extraDays);
16772             d.setDate(d.getDate()+1);
16773             
16774             cells[i].className = "fc-future fc-other-month";
16775             setCellClass(this, cells[i]);
16776         }
16777         
16778         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16779         
16780         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16781         
16782         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16783         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16784         
16785         if(totalRows != 6){
16786             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16787             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16788         }
16789         
16790         this.fireEvent('monthchange', this, date);
16791         
16792         
16793         /*
16794         if(!this.internalRender){
16795             var main = this.el.dom.firstChild;
16796             var w = main.offsetWidth;
16797             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16798             Roo.fly(main).setWidth(w);
16799             this.internalRender = true;
16800             // opera does not respect the auto grow header center column
16801             // then, after it gets a width opera refuses to recalculate
16802             // without a second pass
16803             if(Roo.isOpera && !this.secondPass){
16804                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16805                 this.secondPass = true;
16806                 this.update.defer(10, this, [date]);
16807             }
16808         }
16809         */
16810         
16811     },
16812     
16813     findCell : function(dt) {
16814         dt = dt.clearTime().getTime();
16815         var ret = false;
16816         this.cells.each(function(c){
16817             //Roo.log("check " +c.dateValue + '?=' + dt);
16818             if(c.dateValue == dt){
16819                 ret = c;
16820                 return false;
16821             }
16822             return true;
16823         });
16824         
16825         return ret;
16826     },
16827     
16828     findCells : function(ev) {
16829         var s = ev.start.clone().clearTime().getTime();
16830        // Roo.log(s);
16831         var e= ev.end.clone().clearTime().getTime();
16832        // Roo.log(e);
16833         var ret = [];
16834         this.cells.each(function(c){
16835              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16836             
16837             if(c.dateValue > e){
16838                 return ;
16839             }
16840             if(c.dateValue < s){
16841                 return ;
16842             }
16843             ret.push(c);
16844         });
16845         
16846         return ret;    
16847     },
16848     
16849 //    findBestRow: function(cells)
16850 //    {
16851 //        var ret = 0;
16852 //        
16853 //        for (var i =0 ; i < cells.length;i++) {
16854 //            ret  = Math.max(cells[i].rows || 0,ret);
16855 //        }
16856 //        return ret;
16857 //        
16858 //    },
16859     
16860     
16861     addItem : function(ev)
16862     {
16863         // look for vertical location slot in
16864         var cells = this.findCells(ev);
16865         
16866 //        ev.row = this.findBestRow(cells);
16867         
16868         // work out the location.
16869         
16870         var crow = false;
16871         var rows = [];
16872         for(var i =0; i < cells.length; i++) {
16873             
16874             cells[i].row = cells[0].row;
16875             
16876             if(i == 0){
16877                 cells[i].row = cells[i].row + 1;
16878             }
16879             
16880             if (!crow) {
16881                 crow = {
16882                     start : cells[i],
16883                     end :  cells[i]
16884                 };
16885                 continue;
16886             }
16887             if (crow.start.getY() == cells[i].getY()) {
16888                 // on same row.
16889                 crow.end = cells[i];
16890                 continue;
16891             }
16892             // different row.
16893             rows.push(crow);
16894             crow = {
16895                 start: cells[i],
16896                 end : cells[i]
16897             };
16898             
16899         }
16900         
16901         rows.push(crow);
16902         ev.els = [];
16903         ev.rows = rows;
16904         ev.cells = cells;
16905         
16906         cells[0].events.push(ev);
16907         
16908         this.calevents.push(ev);
16909     },
16910     
16911     clearEvents: function() {
16912         
16913         if(!this.calevents){
16914             return;
16915         }
16916         
16917         Roo.each(this.cells.elements, function(c){
16918             c.row = 0;
16919             c.events = [];
16920             c.more = [];
16921         });
16922         
16923         Roo.each(this.calevents, function(e) {
16924             Roo.each(e.els, function(el) {
16925                 el.un('mouseenter' ,this.onEventEnter, this);
16926                 el.un('mouseleave' ,this.onEventLeave, this);
16927                 el.remove();
16928             },this);
16929         },this);
16930         
16931         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16932             e.remove();
16933         });
16934         
16935     },
16936     
16937     renderEvents: function()
16938     {   
16939         var _this = this;
16940         
16941         this.cells.each(function(c) {
16942             
16943             if(c.row < 5){
16944                 return;
16945             }
16946             
16947             var ev = c.events;
16948             
16949             var r = 4;
16950             if(c.row != c.events.length){
16951                 r = 4 - (4 - (c.row - c.events.length));
16952             }
16953             
16954             c.events = ev.slice(0, r);
16955             c.more = ev.slice(r);
16956             
16957             if(c.more.length && c.more.length == 1){
16958                 c.events.push(c.more.pop());
16959             }
16960             
16961             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16962             
16963         });
16964             
16965         this.cells.each(function(c) {
16966             
16967             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16968             
16969             
16970             for (var e = 0; e < c.events.length; e++){
16971                 var ev = c.events[e];
16972                 var rows = ev.rows;
16973                 
16974                 for(var i = 0; i < rows.length; i++) {
16975                 
16976                     // how many rows should it span..
16977
16978                     var  cfg = {
16979                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16980                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16981
16982                         unselectable : "on",
16983                         cn : [
16984                             {
16985                                 cls: 'fc-event-inner',
16986                                 cn : [
16987     //                                {
16988     //                                  tag:'span',
16989     //                                  cls: 'fc-event-time',
16990     //                                  html : cells.length > 1 ? '' : ev.time
16991     //                                },
16992                                     {
16993                                       tag:'span',
16994                                       cls: 'fc-event-title',
16995                                       html : String.format('{0}', ev.title)
16996                                     }
16997
16998
16999                                 ]
17000                             },
17001                             {
17002                                 cls: 'ui-resizable-handle ui-resizable-e',
17003                                 html : '&nbsp;&nbsp;&nbsp'
17004                             }
17005
17006                         ]
17007                     };
17008
17009                     if (i == 0) {
17010                         cfg.cls += ' fc-event-start';
17011                     }
17012                     if ((i+1) == rows.length) {
17013                         cfg.cls += ' fc-event-end';
17014                     }
17015
17016                     var ctr = _this.el.select('.fc-event-container',true).first();
17017                     var cg = ctr.createChild(cfg);
17018
17019                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17020                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17021
17022                     var r = (c.more.length) ? 1 : 0;
17023                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17024                     cg.setWidth(ebox.right - sbox.x -2);
17025
17026                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17027                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17028                     cg.on('click', _this.onEventClick, _this, ev);
17029
17030                     ev.els.push(cg);
17031                     
17032                 }
17033                 
17034             }
17035             
17036             
17037             if(c.more.length){
17038                 var  cfg = {
17039                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17040                     style : 'position: absolute',
17041                     unselectable : "on",
17042                     cn : [
17043                         {
17044                             cls: 'fc-event-inner',
17045                             cn : [
17046                                 {
17047                                   tag:'span',
17048                                   cls: 'fc-event-title',
17049                                   html : 'More'
17050                                 }
17051
17052
17053                             ]
17054                         },
17055                         {
17056                             cls: 'ui-resizable-handle ui-resizable-e',
17057                             html : '&nbsp;&nbsp;&nbsp'
17058                         }
17059
17060                     ]
17061                 };
17062
17063                 var ctr = _this.el.select('.fc-event-container',true).first();
17064                 var cg = ctr.createChild(cfg);
17065
17066                 var sbox = c.select('.fc-day-content',true).first().getBox();
17067                 var ebox = c.select('.fc-day-content',true).first().getBox();
17068                 //Roo.log(cg);
17069                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17070                 cg.setWidth(ebox.right - sbox.x -2);
17071
17072                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17073                 
17074             }
17075             
17076         });
17077         
17078         
17079         
17080     },
17081     
17082     onEventEnter: function (e, el,event,d) {
17083         this.fireEvent('evententer', this, el, event);
17084     },
17085     
17086     onEventLeave: function (e, el,event,d) {
17087         this.fireEvent('eventleave', this, el, event);
17088     },
17089     
17090     onEventClick: function (e, el,event,d) {
17091         this.fireEvent('eventclick', this, el, event);
17092     },
17093     
17094     onMonthChange: function () {
17095         this.store.load();
17096     },
17097     
17098     onMoreEventClick: function(e, el, more)
17099     {
17100         var _this = this;
17101         
17102         this.calpopover.placement = 'right';
17103         this.calpopover.setTitle('More');
17104         
17105         this.calpopover.setContent('');
17106         
17107         var ctr = this.calpopover.el.select('.popover-content', true).first();
17108         
17109         Roo.each(more, function(m){
17110             var cfg = {
17111                 cls : 'fc-event-hori fc-event-draggable',
17112                 html : m.title
17113             };
17114             var cg = ctr.createChild(cfg);
17115             
17116             cg.on('click', _this.onEventClick, _this, m);
17117         });
17118         
17119         this.calpopover.show(el);
17120         
17121         
17122     },
17123     
17124     onLoad: function () 
17125     {   
17126         this.calevents = [];
17127         var cal = this;
17128         
17129         if(this.store.getCount() > 0){
17130             this.store.data.each(function(d){
17131                cal.addItem({
17132                     id : d.data.id,
17133                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17134                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17135                     time : d.data.start_time,
17136                     title : d.data.title,
17137                     description : d.data.description,
17138                     venue : d.data.venue
17139                 });
17140             });
17141         }
17142         
17143         this.renderEvents();
17144         
17145         if(this.calevents.length && this.loadMask){
17146             this.maskEl.hide();
17147         }
17148     },
17149     
17150     onBeforeLoad: function()
17151     {
17152         this.clearEvents();
17153         if(this.loadMask){
17154             this.maskEl.show();
17155         }
17156     }
17157 });
17158
17159  
17160  /*
17161  * - LGPL
17162  *
17163  * element
17164  * 
17165  */
17166
17167 /**
17168  * @class Roo.bootstrap.Popover
17169  * @extends Roo.bootstrap.Component
17170  * Bootstrap Popover class
17171  * @cfg {String} html contents of the popover   (or false to use children..)
17172  * @cfg {String} title of popover (or false to hide)
17173  * @cfg {String} placement how it is placed
17174  * @cfg {String} trigger click || hover (or false to trigger manually)
17175  * @cfg {String} over what (parent or false to trigger manually.)
17176  * @cfg {Number} delay - delay before showing
17177  
17178  * @constructor
17179  * Create a new Popover
17180  * @param {Object} config The config object
17181  */
17182
17183 Roo.bootstrap.Popover = function(config){
17184     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17185     
17186     this.addEvents({
17187         // raw events
17188          /**
17189          * @event show
17190          * After the popover show
17191          * 
17192          * @param {Roo.bootstrap.Popover} this
17193          */
17194         "show" : true,
17195         /**
17196          * @event hide
17197          * After the popover hide
17198          * 
17199          * @param {Roo.bootstrap.Popover} this
17200          */
17201         "hide" : true
17202     });
17203 };
17204
17205 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17206     
17207     title: 'Fill in a title',
17208     html: false,
17209     
17210     placement : 'right',
17211     trigger : 'hover', // hover
17212     
17213     delay : 0,
17214     
17215     over: 'parent',
17216     
17217     can_build_overlaid : false,
17218     
17219     getChildContainer : function()
17220     {
17221         return this.el.select('.popover-content',true).first();
17222     },
17223     
17224     getAutoCreate : function(){
17225          
17226         var cfg = {
17227            cls : 'popover roo-dynamic',
17228            style: 'display:block',
17229            cn : [
17230                 {
17231                     cls : 'arrow'
17232                 },
17233                 {
17234                     cls : 'popover-inner',
17235                     cn : [
17236                         {
17237                             tag: 'h3',
17238                             cls: 'popover-title',
17239                             html : this.title
17240                         },
17241                         {
17242                             cls : 'popover-content',
17243                             html : this.html
17244                         }
17245                     ]
17246                     
17247                 }
17248            ]
17249         };
17250         
17251         return cfg;
17252     },
17253     setTitle: function(str)
17254     {
17255         this.title = str;
17256         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17257     },
17258     setContent: function(str)
17259     {
17260         this.html = str;
17261         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17262     },
17263     // as it get's added to the bottom of the page.
17264     onRender : function(ct, position)
17265     {
17266         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17267         if(!this.el){
17268             var cfg = Roo.apply({},  this.getAutoCreate());
17269             cfg.id = Roo.id();
17270             
17271             if (this.cls) {
17272                 cfg.cls += ' ' + this.cls;
17273             }
17274             if (this.style) {
17275                 cfg.style = this.style;
17276             }
17277             //Roo.log("adding to ");
17278             this.el = Roo.get(document.body).createChild(cfg, position);
17279 //            Roo.log(this.el);
17280         }
17281         this.initEvents();
17282     },
17283     
17284     initEvents : function()
17285     {
17286         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17287         this.el.enableDisplayMode('block');
17288         this.el.hide();
17289         if (this.over === false) {
17290             return; 
17291         }
17292         if (this.triggers === false) {
17293             return;
17294         }
17295         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17296         var triggers = this.trigger ? this.trigger.split(' ') : [];
17297         Roo.each(triggers, function(trigger) {
17298         
17299             if (trigger == 'click') {
17300                 on_el.on('click', this.toggle, this);
17301             } else if (trigger != 'manual') {
17302                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17303                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17304       
17305                 on_el.on(eventIn  ,this.enter, this);
17306                 on_el.on(eventOut, this.leave, this);
17307             }
17308         }, this);
17309         
17310     },
17311     
17312     
17313     // private
17314     timeout : null,
17315     hoverState : null,
17316     
17317     toggle : function () {
17318         this.hoverState == 'in' ? this.leave() : this.enter();
17319     },
17320     
17321     enter : function () {
17322         
17323         clearTimeout(this.timeout);
17324     
17325         this.hoverState = 'in';
17326     
17327         if (!this.delay || !this.delay.show) {
17328             this.show();
17329             return;
17330         }
17331         var _t = this;
17332         this.timeout = setTimeout(function () {
17333             if (_t.hoverState == 'in') {
17334                 _t.show();
17335             }
17336         }, this.delay.show)
17337     },
17338     
17339     leave : function() {
17340         clearTimeout(this.timeout);
17341     
17342         this.hoverState = 'out';
17343     
17344         if (!this.delay || !this.delay.hide) {
17345             this.hide();
17346             return;
17347         }
17348         var _t = this;
17349         this.timeout = setTimeout(function () {
17350             if (_t.hoverState == 'out') {
17351                 _t.hide();
17352             }
17353         }, this.delay.hide)
17354     },
17355     
17356     show : function (on_el)
17357     {
17358         if (!on_el) {
17359             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17360         }
17361         
17362         // set content.
17363         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17364         if (this.html !== false) {
17365             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17366         }
17367         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17368         if (!this.title.length) {
17369             this.el.select('.popover-title',true).hide();
17370         }
17371         
17372         var placement = typeof this.placement == 'function' ?
17373             this.placement.call(this, this.el, on_el) :
17374             this.placement;
17375             
17376         var autoToken = /\s?auto?\s?/i;
17377         var autoPlace = autoToken.test(placement);
17378         if (autoPlace) {
17379             placement = placement.replace(autoToken, '') || 'top';
17380         }
17381         
17382         //this.el.detach()
17383         //this.el.setXY([0,0]);
17384         this.el.show();
17385         this.el.dom.style.display='block';
17386         this.el.addClass(placement);
17387         
17388         //this.el.appendTo(on_el);
17389         
17390         var p = this.getPosition();
17391         var box = this.el.getBox();
17392         
17393         if (autoPlace) {
17394             // fixme..
17395         }
17396         var align = Roo.bootstrap.Popover.alignment[placement];
17397         
17398 //        Roo.log(align);
17399         this.el.alignTo(on_el, align[0],align[1]);
17400         //var arrow = this.el.select('.arrow',true).first();
17401         //arrow.set(align[2], 
17402         
17403         this.el.addClass('in');
17404         
17405         
17406         if (this.el.hasClass('fade')) {
17407             // fade it?
17408         }
17409         
17410         this.hoverState = 'in';
17411         
17412         this.fireEvent('show', this);
17413         
17414     },
17415     hide : function()
17416     {
17417         this.el.setXY([0,0]);
17418         this.el.removeClass('in');
17419         this.el.hide();
17420         this.hoverState = null;
17421         
17422         this.fireEvent('hide', this);
17423     }
17424     
17425 });
17426
17427 Roo.bootstrap.Popover.alignment = {
17428     'left' : ['r-l', [-10,0], 'right'],
17429     'right' : ['l-r', [10,0], 'left'],
17430     'bottom' : ['t-b', [0,10], 'top'],
17431     'top' : [ 'b-t', [0,-10], 'bottom']
17432 };
17433
17434  /*
17435  * - LGPL
17436  *
17437  * Progress
17438  * 
17439  */
17440
17441 /**
17442  * @class Roo.bootstrap.Progress
17443  * @extends Roo.bootstrap.Component
17444  * Bootstrap Progress class
17445  * @cfg {Boolean} striped striped of the progress bar
17446  * @cfg {Boolean} active animated of the progress bar
17447  * 
17448  * 
17449  * @constructor
17450  * Create a new Progress
17451  * @param {Object} config The config object
17452  */
17453
17454 Roo.bootstrap.Progress = function(config){
17455     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17456 };
17457
17458 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17459     
17460     striped : false,
17461     active: false,
17462     
17463     getAutoCreate : function(){
17464         var cfg = {
17465             tag: 'div',
17466             cls: 'progress'
17467         };
17468         
17469         
17470         if(this.striped){
17471             cfg.cls += ' progress-striped';
17472         }
17473       
17474         if(this.active){
17475             cfg.cls += ' active';
17476         }
17477         
17478         
17479         return cfg;
17480     }
17481    
17482 });
17483
17484  
17485
17486  /*
17487  * - LGPL
17488  *
17489  * ProgressBar
17490  * 
17491  */
17492
17493 /**
17494  * @class Roo.bootstrap.ProgressBar
17495  * @extends Roo.bootstrap.Component
17496  * Bootstrap ProgressBar class
17497  * @cfg {Number} aria_valuenow aria-value now
17498  * @cfg {Number} aria_valuemin aria-value min
17499  * @cfg {Number} aria_valuemax aria-value max
17500  * @cfg {String} label label for the progress bar
17501  * @cfg {String} panel (success | info | warning | danger )
17502  * @cfg {String} role role of the progress bar
17503  * @cfg {String} sr_only text
17504  * 
17505  * 
17506  * @constructor
17507  * Create a new ProgressBar
17508  * @param {Object} config The config object
17509  */
17510
17511 Roo.bootstrap.ProgressBar = function(config){
17512     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17513 };
17514
17515 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17516     
17517     aria_valuenow : 0,
17518     aria_valuemin : 0,
17519     aria_valuemax : 100,
17520     label : false,
17521     panel : false,
17522     role : false,
17523     sr_only: false,
17524     
17525     getAutoCreate : function()
17526     {
17527         
17528         var cfg = {
17529             tag: 'div',
17530             cls: 'progress-bar',
17531             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17532         };
17533         
17534         if(this.sr_only){
17535             cfg.cn = {
17536                 tag: 'span',
17537                 cls: 'sr-only',
17538                 html: this.sr_only
17539             }
17540         }
17541         
17542         if(this.role){
17543             cfg.role = this.role;
17544         }
17545         
17546         if(this.aria_valuenow){
17547             cfg['aria-valuenow'] = this.aria_valuenow;
17548         }
17549         
17550         if(this.aria_valuemin){
17551             cfg['aria-valuemin'] = this.aria_valuemin;
17552         }
17553         
17554         if(this.aria_valuemax){
17555             cfg['aria-valuemax'] = this.aria_valuemax;
17556         }
17557         
17558         if(this.label && !this.sr_only){
17559             cfg.html = this.label;
17560         }
17561         
17562         if(this.panel){
17563             cfg.cls += ' progress-bar-' + this.panel;
17564         }
17565         
17566         return cfg;
17567     },
17568     
17569     update : function(aria_valuenow)
17570     {
17571         this.aria_valuenow = aria_valuenow;
17572         
17573         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17574     }
17575    
17576 });
17577
17578  
17579
17580  /*
17581  * - LGPL
17582  *
17583  * column
17584  * 
17585  */
17586
17587 /**
17588  * @class Roo.bootstrap.TabGroup
17589  * @extends Roo.bootstrap.Column
17590  * Bootstrap Column class
17591  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17592  * @cfg {Boolean} carousel true to make the group behave like a carousel
17593  * @cfg {Boolean} bullets show bullets for the panels
17594  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17595  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17596  * @cfg {Boolean} showarrow (true|false) show arrow default true
17597  * 
17598  * @constructor
17599  * Create a new TabGroup
17600  * @param {Object} config The config object
17601  */
17602
17603 Roo.bootstrap.TabGroup = function(config){
17604     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17605     if (!this.navId) {
17606         this.navId = Roo.id();
17607     }
17608     this.tabs = [];
17609     Roo.bootstrap.TabGroup.register(this);
17610     
17611 };
17612
17613 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17614     
17615     carousel : false,
17616     transition : false,
17617     bullets : 0,
17618     timer : 0,
17619     autoslide : false,
17620     slideFn : false,
17621     slideOnTouch : false,
17622     showarrow : true,
17623     
17624     getAutoCreate : function()
17625     {
17626         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17627         
17628         cfg.cls += ' tab-content';
17629         
17630         if (this.carousel) {
17631             cfg.cls += ' carousel slide';
17632             
17633             cfg.cn = [{
17634                cls : 'carousel-inner',
17635                cn : []
17636             }];
17637         
17638             if(this.bullets  && !Roo.isTouch){
17639                 
17640                 var bullets = {
17641                     cls : 'carousel-bullets',
17642                     cn : []
17643                 };
17644                
17645                 if(this.bullets_cls){
17646                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17647                 }
17648                 
17649                 bullets.cn.push({
17650                     cls : 'clear'
17651                 });
17652                 
17653                 cfg.cn[0].cn.push(bullets);
17654             }
17655             
17656             if(this.showarrow){
17657                 cfg.cn[0].cn.push({
17658                     tag : 'div',
17659                     class : 'carousel-arrow',
17660                     cn : [
17661                         {
17662                             tag : 'div',
17663                             class : 'carousel-prev',
17664                             cn : [
17665                                 {
17666                                     tag : 'i',
17667                                     class : 'fa fa-chevron-left'
17668                                 }
17669                             ]
17670                         },
17671                         {
17672                             tag : 'div',
17673                             class : 'carousel-next',
17674                             cn : [
17675                                 {
17676                                     tag : 'i',
17677                                     class : 'fa fa-chevron-right'
17678                                 }
17679                             ]
17680                         }
17681                     ]
17682                 });
17683             }
17684             
17685         }
17686         
17687         return cfg;
17688     },
17689     
17690     initEvents:  function()
17691     {
17692 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17693 //            this.el.on("touchstart", this.onTouchStart, this);
17694 //        }
17695         
17696         if(this.autoslide){
17697             var _this = this;
17698             
17699             this.slideFn = window.setInterval(function() {
17700                 _this.showPanelNext();
17701             }, this.timer);
17702         }
17703         
17704         if(this.showarrow){
17705             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17706             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17707         }
17708         
17709         
17710     },
17711     
17712 //    onTouchStart : function(e, el, o)
17713 //    {
17714 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17715 //            return;
17716 //        }
17717 //        
17718 //        this.showPanelNext();
17719 //    },
17720     
17721     
17722     getChildContainer : function()
17723     {
17724         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17725     },
17726     
17727     /**
17728     * register a Navigation item
17729     * @param {Roo.bootstrap.NavItem} the navitem to add
17730     */
17731     register : function(item)
17732     {
17733         this.tabs.push( item);
17734         item.navId = this.navId; // not really needed..
17735         this.addBullet();
17736     
17737     },
17738     
17739     getActivePanel : function()
17740     {
17741         var r = false;
17742         Roo.each(this.tabs, function(t) {
17743             if (t.active) {
17744                 r = t;
17745                 return false;
17746             }
17747             return null;
17748         });
17749         return r;
17750         
17751     },
17752     getPanelByName : function(n)
17753     {
17754         var r = false;
17755         Roo.each(this.tabs, function(t) {
17756             if (t.tabId == n) {
17757                 r = t;
17758                 return false;
17759             }
17760             return null;
17761         });
17762         return r;
17763     },
17764     indexOfPanel : function(p)
17765     {
17766         var r = false;
17767         Roo.each(this.tabs, function(t,i) {
17768             if (t.tabId == p.tabId) {
17769                 r = i;
17770                 return false;
17771             }
17772             return null;
17773         });
17774         return r;
17775     },
17776     /**
17777      * show a specific panel
17778      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17779      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17780      */
17781     showPanel : function (pan)
17782     {
17783         if(this.transition || typeof(pan) == 'undefined'){
17784             Roo.log("waiting for the transitionend");
17785             return;
17786         }
17787         
17788         if (typeof(pan) == 'number') {
17789             pan = this.tabs[pan];
17790         }
17791         
17792         if (typeof(pan) == 'string') {
17793             pan = this.getPanelByName(pan);
17794         }
17795         
17796         var cur = this.getActivePanel();
17797         
17798         if(!pan || !cur){
17799             Roo.log('pan or acitve pan is undefined');
17800             return false;
17801         }
17802         
17803         if (pan.tabId == this.getActivePanel().tabId) {
17804             return true;
17805         }
17806         
17807         if (false === cur.fireEvent('beforedeactivate')) {
17808             return false;
17809         }
17810         
17811         if(this.bullets > 0 && !Roo.isTouch){
17812             this.setActiveBullet(this.indexOfPanel(pan));
17813         }
17814         
17815         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17816             
17817             this.transition = true;
17818             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17819             var lr = dir == 'next' ? 'left' : 'right';
17820             pan.el.addClass(dir); // or prev
17821             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17822             cur.el.addClass(lr); // or right
17823             pan.el.addClass(lr);
17824             
17825             var _this = this;
17826             cur.el.on('transitionend', function() {
17827                 Roo.log("trans end?");
17828                 
17829                 pan.el.removeClass([lr,dir]);
17830                 pan.setActive(true);
17831                 
17832                 cur.el.removeClass([lr]);
17833                 cur.setActive(false);
17834                 
17835                 _this.transition = false;
17836                 
17837             }, this, { single:  true } );
17838             
17839             return true;
17840         }
17841         
17842         cur.setActive(false);
17843         pan.setActive(true);
17844         
17845         return true;
17846         
17847     },
17848     showPanelNext : function()
17849     {
17850         var i = this.indexOfPanel(this.getActivePanel());
17851         
17852         if (i >= this.tabs.length - 1 && !this.autoslide) {
17853             return;
17854         }
17855         
17856         if (i >= this.tabs.length - 1 && this.autoslide) {
17857             i = -1;
17858         }
17859         
17860         this.showPanel(this.tabs[i+1]);
17861     },
17862     
17863     showPanelPrev : function()
17864     {
17865         var i = this.indexOfPanel(this.getActivePanel());
17866         
17867         if (i  < 1 && !this.autoslide) {
17868             return;
17869         }
17870         
17871         if (i < 1 && this.autoslide) {
17872             i = this.tabs.length;
17873         }
17874         
17875         this.showPanel(this.tabs[i-1]);
17876     },
17877     
17878     
17879     addBullet: function()
17880     {
17881         if(!this.bullets || Roo.isTouch){
17882             return;
17883         }
17884         var ctr = this.el.select('.carousel-bullets',true).first();
17885         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17886         var bullet = ctr.createChild({
17887             cls : 'bullet bullet-' + i
17888         },ctr.dom.lastChild);
17889         
17890         
17891         var _this = this;
17892         
17893         bullet.on('click', (function(e, el, o, ii, t){
17894
17895             e.preventDefault();
17896
17897             this.showPanel(ii);
17898
17899             if(this.autoslide && this.slideFn){
17900                 clearInterval(this.slideFn);
17901                 this.slideFn = window.setInterval(function() {
17902                     _this.showPanelNext();
17903                 }, this.timer);
17904             }
17905
17906         }).createDelegate(this, [i, bullet], true));
17907                 
17908         
17909     },
17910      
17911     setActiveBullet : function(i)
17912     {
17913         if(Roo.isTouch){
17914             return;
17915         }
17916         
17917         Roo.each(this.el.select('.bullet', true).elements, function(el){
17918             el.removeClass('selected');
17919         });
17920
17921         var bullet = this.el.select('.bullet-' + i, true).first();
17922         
17923         if(!bullet){
17924             return;
17925         }
17926         
17927         bullet.addClass('selected');
17928     }
17929     
17930     
17931   
17932 });
17933
17934  
17935
17936  
17937  
17938 Roo.apply(Roo.bootstrap.TabGroup, {
17939     
17940     groups: {},
17941      /**
17942     * register a Navigation Group
17943     * @param {Roo.bootstrap.NavGroup} the navgroup to add
17944     */
17945     register : function(navgrp)
17946     {
17947         this.groups[navgrp.navId] = navgrp;
17948         
17949     },
17950     /**
17951     * fetch a Navigation Group based on the navigation ID
17952     * if one does not exist , it will get created.
17953     * @param {string} the navgroup to add
17954     * @returns {Roo.bootstrap.NavGroup} the navgroup 
17955     */
17956     get: function(navId) {
17957         if (typeof(this.groups[navId]) == 'undefined') {
17958             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17959         }
17960         return this.groups[navId] ;
17961     }
17962     
17963     
17964     
17965 });
17966
17967  /*
17968  * - LGPL
17969  *
17970  * TabPanel
17971  * 
17972  */
17973
17974 /**
17975  * @class Roo.bootstrap.TabPanel
17976  * @extends Roo.bootstrap.Component
17977  * Bootstrap TabPanel class
17978  * @cfg {Boolean} active panel active
17979  * @cfg {String} html panel content
17980  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17981  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17982  * @cfg {String} href click to link..
17983  * 
17984  * 
17985  * @constructor
17986  * Create a new TabPanel
17987  * @param {Object} config The config object
17988  */
17989
17990 Roo.bootstrap.TabPanel = function(config){
17991     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17992     this.addEvents({
17993         /**
17994              * @event changed
17995              * Fires when the active status changes
17996              * @param {Roo.bootstrap.TabPanel} this
17997              * @param {Boolean} state the new state
17998             
17999          */
18000         'changed': true,
18001         /**
18002              * @event beforedeactivate
18003              * Fires before a tab is de-activated - can be used to do validation on a form.
18004              * @param {Roo.bootstrap.TabPanel} this
18005              * @return {Boolean} false if there is an error
18006             
18007          */
18008         'beforedeactivate': true
18009      });
18010     
18011     this.tabId = this.tabId || Roo.id();
18012   
18013 };
18014
18015 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18016     
18017     active: false,
18018     html: false,
18019     tabId: false,
18020     navId : false,
18021     href : '',
18022     
18023     getAutoCreate : function(){
18024         var cfg = {
18025             tag: 'div',
18026             // item is needed for carousel - not sure if it has any effect otherwise
18027             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18028             html: this.html || ''
18029         };
18030         
18031         if(this.active){
18032             cfg.cls += ' active';
18033         }
18034         
18035         if(this.tabId){
18036             cfg.tabId = this.tabId;
18037         }
18038         
18039         
18040         return cfg;
18041     },
18042     
18043     initEvents:  function()
18044     {
18045         var p = this.parent();
18046         
18047         this.navId = this.navId || p.navId;
18048         
18049         if (typeof(this.navId) != 'undefined') {
18050             // not really needed.. but just in case.. parent should be a NavGroup.
18051             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18052             
18053             tg.register(this);
18054             
18055             var i = tg.tabs.length - 1;
18056             
18057             if(this.active && tg.bullets > 0 && i < tg.bullets){
18058                 tg.setActiveBullet(i);
18059             }
18060         }
18061         
18062         this.el.on('click', this.onClick, this);
18063         
18064         if(Roo.isTouch){
18065             this.el.on("touchstart", this.onTouchStart, this);
18066             this.el.on("touchmove", this.onTouchMove, this);
18067             this.el.on("touchend", this.onTouchEnd, this);
18068         }
18069         
18070     },
18071     
18072     onRender : function(ct, position)
18073     {
18074         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18075     },
18076     
18077     setActive : function(state)
18078     {
18079         Roo.log("panel - set active " + this.tabId + "=" + state);
18080         
18081         this.active = state;
18082         if (!state) {
18083             this.el.removeClass('active');
18084             
18085         } else  if (!this.el.hasClass('active')) {
18086             this.el.addClass('active');
18087         }
18088         
18089         this.fireEvent('changed', this, state);
18090     },
18091     
18092     onClick : function(e)
18093     {
18094         e.preventDefault();
18095         
18096         if(!this.href.length){
18097             return;
18098         }
18099         
18100         window.location.href = this.href;
18101     },
18102     
18103     startX : 0,
18104     startY : 0,
18105     endX : 0,
18106     endY : 0,
18107     swiping : false,
18108     
18109     onTouchStart : function(e)
18110     {
18111         this.swiping = false;
18112         
18113         this.startX = e.browserEvent.touches[0].clientX;
18114         this.startY = e.browserEvent.touches[0].clientY;
18115     },
18116     
18117     onTouchMove : function(e)
18118     {
18119         this.swiping = true;
18120         
18121         this.endX = e.browserEvent.touches[0].clientX;
18122         this.endY = e.browserEvent.touches[0].clientY;
18123     },
18124     
18125     onTouchEnd : function(e)
18126     {
18127         if(!this.swiping){
18128             this.onClick(e);
18129             return;
18130         }
18131         
18132         var tabGroup = this.parent();
18133         
18134         if(this.endX > this.startX){ // swiping right
18135             tabGroup.showPanelPrev();
18136             return;
18137         }
18138         
18139         if(this.startX > this.endX){ // swiping left
18140             tabGroup.showPanelNext();
18141             return;
18142         }
18143     }
18144     
18145     
18146 });
18147  
18148
18149  
18150
18151  /*
18152  * - LGPL
18153  *
18154  * DateField
18155  * 
18156  */
18157
18158 /**
18159  * @class Roo.bootstrap.DateField
18160  * @extends Roo.bootstrap.Input
18161  * Bootstrap DateField class
18162  * @cfg {Number} weekStart default 0
18163  * @cfg {String} viewMode default empty, (months|years)
18164  * @cfg {String} minViewMode default empty, (months|years)
18165  * @cfg {Number} startDate default -Infinity
18166  * @cfg {Number} endDate default Infinity
18167  * @cfg {Boolean} todayHighlight default false
18168  * @cfg {Boolean} todayBtn default false
18169  * @cfg {Boolean} calendarWeeks default false
18170  * @cfg {Object} daysOfWeekDisabled default empty
18171  * @cfg {Boolean} singleMode default false (true | false)
18172  * 
18173  * @cfg {Boolean} keyboardNavigation default true
18174  * @cfg {String} language default en
18175  * 
18176  * @constructor
18177  * Create a new DateField
18178  * @param {Object} config The config object
18179  */
18180
18181 Roo.bootstrap.DateField = function(config){
18182     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18183      this.addEvents({
18184             /**
18185              * @event show
18186              * Fires when this field show.
18187              * @param {Roo.bootstrap.DateField} this
18188              * @param {Mixed} date The date value
18189              */
18190             show : true,
18191             /**
18192              * @event show
18193              * Fires when this field hide.
18194              * @param {Roo.bootstrap.DateField} this
18195              * @param {Mixed} date The date value
18196              */
18197             hide : true,
18198             /**
18199              * @event select
18200              * Fires when select a date.
18201              * @param {Roo.bootstrap.DateField} this
18202              * @param {Mixed} date The date value
18203              */
18204             select : true,
18205             /**
18206              * @event beforeselect
18207              * Fires when before select a date.
18208              * @param {Roo.bootstrap.DateField} this
18209              * @param {Mixed} date The date value
18210              */
18211             beforeselect : true
18212         });
18213 };
18214
18215 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18216     
18217     /**
18218      * @cfg {String} format
18219      * The default date format string which can be overriden for localization support.  The format must be
18220      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18221      */
18222     format : "m/d/y",
18223     /**
18224      * @cfg {String} altFormats
18225      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18226      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18227      */
18228     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18229     
18230     weekStart : 0,
18231     
18232     viewMode : '',
18233     
18234     minViewMode : '',
18235     
18236     todayHighlight : false,
18237     
18238     todayBtn: false,
18239     
18240     language: 'en',
18241     
18242     keyboardNavigation: true,
18243     
18244     calendarWeeks: false,
18245     
18246     startDate: -Infinity,
18247     
18248     endDate: Infinity,
18249     
18250     daysOfWeekDisabled: [],
18251     
18252     _events: [],
18253     
18254     singleMode : false,
18255     
18256     UTCDate: function()
18257     {
18258         return new Date(Date.UTC.apply(Date, arguments));
18259     },
18260     
18261     UTCToday: function()
18262     {
18263         var today = new Date();
18264         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18265     },
18266     
18267     getDate: function() {
18268             var d = this.getUTCDate();
18269             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18270     },
18271     
18272     getUTCDate: function() {
18273             return this.date;
18274     },
18275     
18276     setDate: function(d) {
18277             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18278     },
18279     
18280     setUTCDate: function(d) {
18281             this.date = d;
18282             this.setValue(this.formatDate(this.date));
18283     },
18284         
18285     onRender: function(ct, position)
18286     {
18287         
18288         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18289         
18290         this.language = this.language || 'en';
18291         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18292         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18293         
18294         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18295         this.format = this.format || 'm/d/y';
18296         this.isInline = false;
18297         this.isInput = true;
18298         this.component = this.el.select('.add-on', true).first() || false;
18299         this.component = (this.component && this.component.length === 0) ? false : this.component;
18300         this.hasInput = this.component && this.inputEl().length;
18301         
18302         if (typeof(this.minViewMode === 'string')) {
18303             switch (this.minViewMode) {
18304                 case 'months':
18305                     this.minViewMode = 1;
18306                     break;
18307                 case 'years':
18308                     this.minViewMode = 2;
18309                     break;
18310                 default:
18311                     this.minViewMode = 0;
18312                     break;
18313             }
18314         }
18315         
18316         if (typeof(this.viewMode === 'string')) {
18317             switch (this.viewMode) {
18318                 case 'months':
18319                     this.viewMode = 1;
18320                     break;
18321                 case 'years':
18322                     this.viewMode = 2;
18323                     break;
18324                 default:
18325                     this.viewMode = 0;
18326                     break;
18327             }
18328         }
18329                 
18330         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18331         
18332 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18333         
18334         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18335         
18336         this.picker().on('mousedown', this.onMousedown, this);
18337         this.picker().on('click', this.onClick, this);
18338         
18339         this.picker().addClass('datepicker-dropdown');
18340         
18341         this.startViewMode = this.viewMode;
18342         
18343         if(this.singleMode){
18344             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18345                 v.setVisibilityMode(Roo.Element.DISPLAY);
18346                 v.hide();
18347             });
18348             
18349             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18350                 v.setStyle('width', '189px');
18351             });
18352         }
18353         
18354         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18355             if(!this.calendarWeeks){
18356                 v.remove();
18357                 return;
18358             }
18359             
18360             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18361             v.attr('colspan', function(i, val){
18362                 return parseInt(val) + 1;
18363             });
18364         });
18365                         
18366         
18367         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18368         
18369         this.setStartDate(this.startDate);
18370         this.setEndDate(this.endDate);
18371         
18372         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18373         
18374         this.fillDow();
18375         this.fillMonths();
18376         this.update();
18377         this.showMode();
18378         
18379         if(this.isInline) {
18380             this.show();
18381         }
18382     },
18383     
18384     picker : function()
18385     {
18386         return this.pickerEl;
18387 //        return this.el.select('.datepicker', true).first();
18388     },
18389     
18390     fillDow: function()
18391     {
18392         var dowCnt = this.weekStart;
18393         
18394         var dow = {
18395             tag: 'tr',
18396             cn: [
18397                 
18398             ]
18399         };
18400         
18401         if(this.calendarWeeks){
18402             dow.cn.push({
18403                 tag: 'th',
18404                 cls: 'cw',
18405                 html: '&nbsp;'
18406             })
18407         }
18408         
18409         while (dowCnt < this.weekStart + 7) {
18410             dow.cn.push({
18411                 tag: 'th',
18412                 cls: 'dow',
18413                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18414             });
18415         }
18416         
18417         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18418     },
18419     
18420     fillMonths: function()
18421     {    
18422         var i = 0;
18423         var months = this.picker().select('>.datepicker-months td', true).first();
18424         
18425         months.dom.innerHTML = '';
18426         
18427         while (i < 12) {
18428             var month = {
18429                 tag: 'span',
18430                 cls: 'month',
18431                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18432             };
18433             
18434             months.createChild(month);
18435         }
18436         
18437     },
18438     
18439     update: function()
18440     {
18441         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;
18442         
18443         if (this.date < this.startDate) {
18444             this.viewDate = new Date(this.startDate);
18445         } else if (this.date > this.endDate) {
18446             this.viewDate = new Date(this.endDate);
18447         } else {
18448             this.viewDate = new Date(this.date);
18449         }
18450         
18451         this.fill();
18452     },
18453     
18454     fill: function() 
18455     {
18456         var d = new Date(this.viewDate),
18457                 year = d.getUTCFullYear(),
18458                 month = d.getUTCMonth(),
18459                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18460                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18461                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18462                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18463                 currentDate = this.date && this.date.valueOf(),
18464                 today = this.UTCToday();
18465         
18466         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18467         
18468 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18469         
18470 //        this.picker.select('>tfoot th.today').
18471 //                                              .text(dates[this.language].today)
18472 //                                              .toggle(this.todayBtn !== false);
18473     
18474         this.updateNavArrows();
18475         this.fillMonths();
18476                                                 
18477         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18478         
18479         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18480          
18481         prevMonth.setUTCDate(day);
18482         
18483         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18484         
18485         var nextMonth = new Date(prevMonth);
18486         
18487         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18488         
18489         nextMonth = nextMonth.valueOf();
18490         
18491         var fillMonths = false;
18492         
18493         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18494         
18495         while(prevMonth.valueOf() < nextMonth) {
18496             var clsName = '';
18497             
18498             if (prevMonth.getUTCDay() === this.weekStart) {
18499                 if(fillMonths){
18500                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18501                 }
18502                     
18503                 fillMonths = {
18504                     tag: 'tr',
18505                     cn: []
18506                 };
18507                 
18508                 if(this.calendarWeeks){
18509                     // ISO 8601: First week contains first thursday.
18510                     // ISO also states week starts on Monday, but we can be more abstract here.
18511                     var
18512                     // Start of current week: based on weekstart/current date
18513                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18514                     // Thursday of this week
18515                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18516                     // First Thursday of year, year from thursday
18517                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18518                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18519                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18520                     
18521                     fillMonths.cn.push({
18522                         tag: 'td',
18523                         cls: 'cw',
18524                         html: calWeek
18525                     });
18526                 }
18527             }
18528             
18529             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18530                 clsName += ' old';
18531             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18532                 clsName += ' new';
18533             }
18534             if (this.todayHighlight &&
18535                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18536                 prevMonth.getUTCMonth() == today.getMonth() &&
18537                 prevMonth.getUTCDate() == today.getDate()) {
18538                 clsName += ' today';
18539             }
18540             
18541             if (currentDate && prevMonth.valueOf() === currentDate) {
18542                 clsName += ' active';
18543             }
18544             
18545             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18546                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18547                     clsName += ' disabled';
18548             }
18549             
18550             fillMonths.cn.push({
18551                 tag: 'td',
18552                 cls: 'day ' + clsName,
18553                 html: prevMonth.getDate()
18554             });
18555             
18556             prevMonth.setDate(prevMonth.getDate()+1);
18557         }
18558           
18559         var currentYear = this.date && this.date.getUTCFullYear();
18560         var currentMonth = this.date && this.date.getUTCMonth();
18561         
18562         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18563         
18564         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18565             v.removeClass('active');
18566             
18567             if(currentYear === year && k === currentMonth){
18568                 v.addClass('active');
18569             }
18570             
18571             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18572                 v.addClass('disabled');
18573             }
18574             
18575         });
18576         
18577         
18578         year = parseInt(year/10, 10) * 10;
18579         
18580         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18581         
18582         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18583         
18584         year -= 1;
18585         for (var i = -1; i < 11; i++) {
18586             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18587                 tag: 'span',
18588                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18589                 html: year
18590             });
18591             
18592             year += 1;
18593         }
18594     },
18595     
18596     showMode: function(dir) 
18597     {
18598         if (dir) {
18599             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18600         }
18601         
18602         Roo.each(this.picker().select('>div',true).elements, function(v){
18603             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18604             v.hide();
18605         });
18606         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18607     },
18608     
18609     place: function()
18610     {
18611         if(this.isInline) {
18612             return;
18613         }
18614         
18615         this.picker().removeClass(['bottom', 'top']);
18616         
18617         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18618             /*
18619              * place to the top of element!
18620              *
18621              */
18622             
18623             this.picker().addClass('top');
18624             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18625             
18626             return;
18627         }
18628         
18629         this.picker().addClass('bottom');
18630         
18631         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18632     },
18633     
18634     parseDate : function(value)
18635     {
18636         if(!value || value instanceof Date){
18637             return value;
18638         }
18639         var v = Date.parseDate(value, this.format);
18640         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18641             v = Date.parseDate(value, 'Y-m-d');
18642         }
18643         if(!v && this.altFormats){
18644             if(!this.altFormatsArray){
18645                 this.altFormatsArray = this.altFormats.split("|");
18646             }
18647             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18648                 v = Date.parseDate(value, this.altFormatsArray[i]);
18649             }
18650         }
18651         return v;
18652     },
18653     
18654     formatDate : function(date, fmt)
18655     {   
18656         return (!date || !(date instanceof Date)) ?
18657         date : date.dateFormat(fmt || this.format);
18658     },
18659     
18660     onFocus : function()
18661     {
18662         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18663         this.show();
18664     },
18665     
18666     onBlur : function()
18667     {
18668         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18669         
18670         var d = this.inputEl().getValue();
18671         
18672         this.setValue(d);
18673                 
18674         this.hide();
18675     },
18676     
18677     show : function()
18678     {
18679         this.picker().show();
18680         this.update();
18681         this.place();
18682         
18683         this.fireEvent('show', this, this.date);
18684     },
18685     
18686     hide : function()
18687     {
18688         if(this.isInline) {
18689             return;
18690         }
18691         this.picker().hide();
18692         this.viewMode = this.startViewMode;
18693         this.showMode();
18694         
18695         this.fireEvent('hide', this, this.date);
18696         
18697     },
18698     
18699     onMousedown: function(e)
18700     {
18701         e.stopPropagation();
18702         e.preventDefault();
18703     },
18704     
18705     keyup: function(e)
18706     {
18707         Roo.bootstrap.DateField.superclass.keyup.call(this);
18708         this.update();
18709     },
18710
18711     setValue: function(v)
18712     {
18713         if(this.fireEvent('beforeselect', this, v) !== false){
18714             var d = new Date(this.parseDate(v) ).clearTime();
18715         
18716             if(isNaN(d.getTime())){
18717                 this.date = this.viewDate = '';
18718                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18719                 return;
18720             }
18721
18722             v = this.formatDate(d);
18723
18724             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18725
18726             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18727
18728             this.update();
18729
18730             this.fireEvent('select', this, this.date);
18731         }
18732     },
18733     
18734     getValue: function()
18735     {
18736         return this.formatDate(this.date);
18737     },
18738     
18739     fireKey: function(e)
18740     {
18741         if (!this.picker().isVisible()){
18742             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18743                 this.show();
18744             }
18745             return;
18746         }
18747         
18748         var dateChanged = false,
18749         dir, day, month,
18750         newDate, newViewDate;
18751         
18752         switch(e.keyCode){
18753             case 27: // escape
18754                 this.hide();
18755                 e.preventDefault();
18756                 break;
18757             case 37: // left
18758             case 39: // right
18759                 if (!this.keyboardNavigation) {
18760                     break;
18761                 }
18762                 dir = e.keyCode == 37 ? -1 : 1;
18763                 
18764                 if (e.ctrlKey){
18765                     newDate = this.moveYear(this.date, dir);
18766                     newViewDate = this.moveYear(this.viewDate, dir);
18767                 } else if (e.shiftKey){
18768                     newDate = this.moveMonth(this.date, dir);
18769                     newViewDate = this.moveMonth(this.viewDate, dir);
18770                 } else {
18771                     newDate = new Date(this.date);
18772                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18773                     newViewDate = new Date(this.viewDate);
18774                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18775                 }
18776                 if (this.dateWithinRange(newDate)){
18777                     this.date = newDate;
18778                     this.viewDate = newViewDate;
18779                     this.setValue(this.formatDate(this.date));
18780 //                    this.update();
18781                     e.preventDefault();
18782                     dateChanged = true;
18783                 }
18784                 break;
18785             case 38: // up
18786             case 40: // down
18787                 if (!this.keyboardNavigation) {
18788                     break;
18789                 }
18790                 dir = e.keyCode == 38 ? -1 : 1;
18791                 if (e.ctrlKey){
18792                     newDate = this.moveYear(this.date, dir);
18793                     newViewDate = this.moveYear(this.viewDate, dir);
18794                 } else if (e.shiftKey){
18795                     newDate = this.moveMonth(this.date, dir);
18796                     newViewDate = this.moveMonth(this.viewDate, dir);
18797                 } else {
18798                     newDate = new Date(this.date);
18799                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18800                     newViewDate = new Date(this.viewDate);
18801                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18802                 }
18803                 if (this.dateWithinRange(newDate)){
18804                     this.date = newDate;
18805                     this.viewDate = newViewDate;
18806                     this.setValue(this.formatDate(this.date));
18807 //                    this.update();
18808                     e.preventDefault();
18809                     dateChanged = true;
18810                 }
18811                 break;
18812             case 13: // enter
18813                 this.setValue(this.formatDate(this.date));
18814                 this.hide();
18815                 e.preventDefault();
18816                 break;
18817             case 9: // tab
18818                 this.setValue(this.formatDate(this.date));
18819                 this.hide();
18820                 break;
18821             case 16: // shift
18822             case 17: // ctrl
18823             case 18: // alt
18824                 break;
18825             default :
18826                 this.hide();
18827                 
18828         }
18829     },
18830     
18831     
18832     onClick: function(e) 
18833     {
18834         e.stopPropagation();
18835         e.preventDefault();
18836         
18837         var target = e.getTarget();
18838         
18839         if(target.nodeName.toLowerCase() === 'i'){
18840             target = Roo.get(target).dom.parentNode;
18841         }
18842         
18843         var nodeName = target.nodeName;
18844         var className = target.className;
18845         var html = target.innerHTML;
18846         //Roo.log(nodeName);
18847         
18848         switch(nodeName.toLowerCase()) {
18849             case 'th':
18850                 switch(className) {
18851                     case 'switch':
18852                         this.showMode(1);
18853                         break;
18854                     case 'prev':
18855                     case 'next':
18856                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18857                         switch(this.viewMode){
18858                                 case 0:
18859                                         this.viewDate = this.moveMonth(this.viewDate, dir);
18860                                         break;
18861                                 case 1:
18862                                 case 2:
18863                                         this.viewDate = this.moveYear(this.viewDate, dir);
18864                                         break;
18865                         }
18866                         this.fill();
18867                         break;
18868                     case 'today':
18869                         var date = new Date();
18870                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18871 //                        this.fill()
18872                         this.setValue(this.formatDate(this.date));
18873                         
18874                         this.hide();
18875                         break;
18876                 }
18877                 break;
18878             case 'span':
18879                 if (className.indexOf('disabled') < 0) {
18880                     this.viewDate.setUTCDate(1);
18881                     if (className.indexOf('month') > -1) {
18882                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18883                     } else {
18884                         var year = parseInt(html, 10) || 0;
18885                         this.viewDate.setUTCFullYear(year);
18886                         
18887                     }
18888                     
18889                     if(this.singleMode){
18890                         this.setValue(this.formatDate(this.viewDate));
18891                         this.hide();
18892                         return;
18893                     }
18894                     
18895                     this.showMode(-1);
18896                     this.fill();
18897                 }
18898                 break;
18899                 
18900             case 'td':
18901                 //Roo.log(className);
18902                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18903                     var day = parseInt(html, 10) || 1;
18904                     var year = this.viewDate.getUTCFullYear(),
18905                         month = this.viewDate.getUTCMonth();
18906
18907                     if (className.indexOf('old') > -1) {
18908                         if(month === 0 ){
18909                             month = 11;
18910                             year -= 1;
18911                         }else{
18912                             month -= 1;
18913                         }
18914                     } else if (className.indexOf('new') > -1) {
18915                         if (month == 11) {
18916                             month = 0;
18917                             year += 1;
18918                         } else {
18919                             month += 1;
18920                         }
18921                     }
18922                     //Roo.log([year,month,day]);
18923                     this.date = this.UTCDate(year, month, day,0,0,0,0);
18924                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18925 //                    this.fill();
18926                     //Roo.log(this.formatDate(this.date));
18927                     this.setValue(this.formatDate(this.date));
18928                     this.hide();
18929                 }
18930                 break;
18931         }
18932     },
18933     
18934     setStartDate: function(startDate)
18935     {
18936         this.startDate = startDate || -Infinity;
18937         if (this.startDate !== -Infinity) {
18938             this.startDate = this.parseDate(this.startDate);
18939         }
18940         this.update();
18941         this.updateNavArrows();
18942     },
18943
18944     setEndDate: function(endDate)
18945     {
18946         this.endDate = endDate || Infinity;
18947         if (this.endDate !== Infinity) {
18948             this.endDate = this.parseDate(this.endDate);
18949         }
18950         this.update();
18951         this.updateNavArrows();
18952     },
18953     
18954     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18955     {
18956         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18957         if (typeof(this.daysOfWeekDisabled) !== 'object') {
18958             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18959         }
18960         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18961             return parseInt(d, 10);
18962         });
18963         this.update();
18964         this.updateNavArrows();
18965     },
18966     
18967     updateNavArrows: function() 
18968     {
18969         if(this.singleMode){
18970             return;
18971         }
18972         
18973         var d = new Date(this.viewDate),
18974         year = d.getUTCFullYear(),
18975         month = d.getUTCMonth();
18976         
18977         Roo.each(this.picker().select('.prev', true).elements, function(v){
18978             v.show();
18979             switch (this.viewMode) {
18980                 case 0:
18981
18982                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18983                         v.hide();
18984                     }
18985                     break;
18986                 case 1:
18987                 case 2:
18988                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18989                         v.hide();
18990                     }
18991                     break;
18992             }
18993         });
18994         
18995         Roo.each(this.picker().select('.next', true).elements, function(v){
18996             v.show();
18997             switch (this.viewMode) {
18998                 case 0:
18999
19000                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
19001                         v.hide();
19002                     }
19003                     break;
19004                 case 1:
19005                 case 2:
19006                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
19007                         v.hide();
19008                     }
19009                     break;
19010             }
19011         })
19012     },
19013     
19014     moveMonth: function(date, dir)
19015     {
19016         if (!dir) {
19017             return date;
19018         }
19019         var new_date = new Date(date.valueOf()),
19020         day = new_date.getUTCDate(),
19021         month = new_date.getUTCMonth(),
19022         mag = Math.abs(dir),
19023         new_month, test;
19024         dir = dir > 0 ? 1 : -1;
19025         if (mag == 1){
19026             test = dir == -1
19027             // If going back one month, make sure month is not current month
19028             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19029             ? function(){
19030                 return new_date.getUTCMonth() == month;
19031             }
19032             // If going forward one month, make sure month is as expected
19033             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19034             : function(){
19035                 return new_date.getUTCMonth() != new_month;
19036             };
19037             new_month = month + dir;
19038             new_date.setUTCMonth(new_month);
19039             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19040             if (new_month < 0 || new_month > 11) {
19041                 new_month = (new_month + 12) % 12;
19042             }
19043         } else {
19044             // For magnitudes >1, move one month at a time...
19045             for (var i=0; i<mag; i++) {
19046                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19047                 new_date = this.moveMonth(new_date, dir);
19048             }
19049             // ...then reset the day, keeping it in the new month
19050             new_month = new_date.getUTCMonth();
19051             new_date.setUTCDate(day);
19052             test = function(){
19053                 return new_month != new_date.getUTCMonth();
19054             };
19055         }
19056         // Common date-resetting loop -- if date is beyond end of month, make it
19057         // end of month
19058         while (test()){
19059             new_date.setUTCDate(--day);
19060             new_date.setUTCMonth(new_month);
19061         }
19062         return new_date;
19063     },
19064
19065     moveYear: function(date, dir)
19066     {
19067         return this.moveMonth(date, dir*12);
19068     },
19069
19070     dateWithinRange: function(date)
19071     {
19072         return date >= this.startDate && date <= this.endDate;
19073     },
19074
19075     
19076     remove: function() 
19077     {
19078         this.picker().remove();
19079     },
19080     
19081     validateValue : function(value)
19082     {
19083         if(value.length < 1)  {
19084             if(this.allowBlank){
19085                 return true;
19086             }
19087             return false;
19088         }
19089         
19090         if(value.length < this.minLength){
19091             return false;
19092         }
19093         if(value.length > this.maxLength){
19094             return false;
19095         }
19096         if(this.vtype){
19097             var vt = Roo.form.VTypes;
19098             if(!vt[this.vtype](value, this)){
19099                 return false;
19100             }
19101         }
19102         if(typeof this.validator == "function"){
19103             var msg = this.validator(value);
19104             if(msg !== true){
19105                 return false;
19106             }
19107         }
19108         
19109         if(this.regex && !this.regex.test(value)){
19110             return false;
19111         }
19112         
19113         if(typeof(this.parseDate(value)) == 'undefined'){
19114             return false;
19115         }
19116         
19117         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19118             return false;
19119         }      
19120         
19121         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19122             return false;
19123         } 
19124         
19125         
19126         return true;
19127     }
19128    
19129 });
19130
19131 Roo.apply(Roo.bootstrap.DateField,  {
19132     
19133     head : {
19134         tag: 'thead',
19135         cn: [
19136         {
19137             tag: 'tr',
19138             cn: [
19139             {
19140                 tag: 'th',
19141                 cls: 'prev',
19142                 html: '<i class="fa fa-arrow-left"/>'
19143             },
19144             {
19145                 tag: 'th',
19146                 cls: 'switch',
19147                 colspan: '5'
19148             },
19149             {
19150                 tag: 'th',
19151                 cls: 'next',
19152                 html: '<i class="fa fa-arrow-right"/>'
19153             }
19154
19155             ]
19156         }
19157         ]
19158     },
19159     
19160     content : {
19161         tag: 'tbody',
19162         cn: [
19163         {
19164             tag: 'tr',
19165             cn: [
19166             {
19167                 tag: 'td',
19168                 colspan: '7'
19169             }
19170             ]
19171         }
19172         ]
19173     },
19174     
19175     footer : {
19176         tag: 'tfoot',
19177         cn: [
19178         {
19179             tag: 'tr',
19180             cn: [
19181             {
19182                 tag: 'th',
19183                 colspan: '7',
19184                 cls: 'today'
19185             }
19186                     
19187             ]
19188         }
19189         ]
19190     },
19191     
19192     dates:{
19193         en: {
19194             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19195             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19196             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19197             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19198             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19199             today: "Today"
19200         }
19201     },
19202     
19203     modes: [
19204     {
19205         clsName: 'days',
19206         navFnc: 'Month',
19207         navStep: 1
19208     },
19209     {
19210         clsName: 'months',
19211         navFnc: 'FullYear',
19212         navStep: 1
19213     },
19214     {
19215         clsName: 'years',
19216         navFnc: 'FullYear',
19217         navStep: 10
19218     }]
19219 });
19220
19221 Roo.apply(Roo.bootstrap.DateField,  {
19222   
19223     template : {
19224         tag: 'div',
19225         cls: 'datepicker dropdown-menu roo-dynamic',
19226         cn: [
19227         {
19228             tag: 'div',
19229             cls: 'datepicker-days',
19230             cn: [
19231             {
19232                 tag: 'table',
19233                 cls: 'table-condensed',
19234                 cn:[
19235                 Roo.bootstrap.DateField.head,
19236                 {
19237                     tag: 'tbody'
19238                 },
19239                 Roo.bootstrap.DateField.footer
19240                 ]
19241             }
19242             ]
19243         },
19244         {
19245             tag: 'div',
19246             cls: 'datepicker-months',
19247             cn: [
19248             {
19249                 tag: 'table',
19250                 cls: 'table-condensed',
19251                 cn:[
19252                 Roo.bootstrap.DateField.head,
19253                 Roo.bootstrap.DateField.content,
19254                 Roo.bootstrap.DateField.footer
19255                 ]
19256             }
19257             ]
19258         },
19259         {
19260             tag: 'div',
19261             cls: 'datepicker-years',
19262             cn: [
19263             {
19264                 tag: 'table',
19265                 cls: 'table-condensed',
19266                 cn:[
19267                 Roo.bootstrap.DateField.head,
19268                 Roo.bootstrap.DateField.content,
19269                 Roo.bootstrap.DateField.footer
19270                 ]
19271             }
19272             ]
19273         }
19274         ]
19275     }
19276 });
19277
19278  
19279
19280  /*
19281  * - LGPL
19282  *
19283  * TimeField
19284  * 
19285  */
19286
19287 /**
19288  * @class Roo.bootstrap.TimeField
19289  * @extends Roo.bootstrap.Input
19290  * Bootstrap DateField class
19291  * 
19292  * 
19293  * @constructor
19294  * Create a new TimeField
19295  * @param {Object} config The config object
19296  */
19297
19298 Roo.bootstrap.TimeField = function(config){
19299     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19300     this.addEvents({
19301             /**
19302              * @event show
19303              * Fires when this field show.
19304              * @param {Roo.bootstrap.DateField} thisthis
19305              * @param {Mixed} date The date value
19306              */
19307             show : true,
19308             /**
19309              * @event show
19310              * Fires when this field hide.
19311              * @param {Roo.bootstrap.DateField} this
19312              * @param {Mixed} date The date value
19313              */
19314             hide : true,
19315             /**
19316              * @event select
19317              * Fires when select a date.
19318              * @param {Roo.bootstrap.DateField} this
19319              * @param {Mixed} date The date value
19320              */
19321             select : true
19322         });
19323 };
19324
19325 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19326     
19327     /**
19328      * @cfg {String} format
19329      * The default time format string which can be overriden for localization support.  The format must be
19330      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19331      */
19332     format : "H:i",
19333        
19334     onRender: function(ct, position)
19335     {
19336         
19337         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19338                 
19339         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19340         
19341         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19342         
19343         this.pop = this.picker().select('>.datepicker-time',true).first();
19344         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19345         
19346         this.picker().on('mousedown', this.onMousedown, this);
19347         this.picker().on('click', this.onClick, this);
19348         
19349         this.picker().addClass('datepicker-dropdown');
19350     
19351         this.fillTime();
19352         this.update();
19353             
19354         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19355         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19356         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19357         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19358         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19359         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19360
19361     },
19362     
19363     fireKey: function(e){
19364         if (!this.picker().isVisible()){
19365             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19366                 this.show();
19367             }
19368             return;
19369         }
19370
19371         e.preventDefault();
19372         
19373         switch(e.keyCode){
19374             case 27: // escape
19375                 this.hide();
19376                 break;
19377             case 37: // left
19378             case 39: // right
19379                 this.onTogglePeriod();
19380                 break;
19381             case 38: // up
19382                 this.onIncrementMinutes();
19383                 break;
19384             case 40: // down
19385                 this.onDecrementMinutes();
19386                 break;
19387             case 13: // enter
19388             case 9: // tab
19389                 this.setTime();
19390                 break;
19391         }
19392     },
19393     
19394     onClick: function(e) {
19395         e.stopPropagation();
19396         e.preventDefault();
19397     },
19398     
19399     picker : function()
19400     {
19401         return this.el.select('.datepicker', true).first();
19402     },
19403     
19404     fillTime: function()
19405     {    
19406         var time = this.pop.select('tbody', true).first();
19407         
19408         time.dom.innerHTML = '';
19409         
19410         time.createChild({
19411             tag: 'tr',
19412             cn: [
19413                 {
19414                     tag: 'td',
19415                     cn: [
19416                         {
19417                             tag: 'a',
19418                             href: '#',
19419                             cls: 'btn',
19420                             cn: [
19421                                 {
19422                                     tag: 'span',
19423                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19424                                 }
19425                             ]
19426                         } 
19427                     ]
19428                 },
19429                 {
19430                     tag: 'td',
19431                     cls: 'separator'
19432                 },
19433                 {
19434                     tag: 'td',
19435                     cn: [
19436                         {
19437                             tag: 'a',
19438                             href: '#',
19439                             cls: 'btn',
19440                             cn: [
19441                                 {
19442                                     tag: 'span',
19443                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19444                                 }
19445                             ]
19446                         }
19447                     ]
19448                 },
19449                 {
19450                     tag: 'td',
19451                     cls: 'separator'
19452                 }
19453             ]
19454         });
19455         
19456         time.createChild({
19457             tag: 'tr',
19458             cn: [
19459                 {
19460                     tag: 'td',
19461                     cn: [
19462                         {
19463                             tag: 'span',
19464                             cls: 'timepicker-hour',
19465                             html: '00'
19466                         }  
19467                     ]
19468                 },
19469                 {
19470                     tag: 'td',
19471                     cls: 'separator',
19472                     html: ':'
19473                 },
19474                 {
19475                     tag: 'td',
19476                     cn: [
19477                         {
19478                             tag: 'span',
19479                             cls: 'timepicker-minute',
19480                             html: '00'
19481                         }  
19482                     ]
19483                 },
19484                 {
19485                     tag: 'td',
19486                     cls: 'separator'
19487                 },
19488                 {
19489                     tag: 'td',
19490                     cn: [
19491                         {
19492                             tag: 'button',
19493                             type: 'button',
19494                             cls: 'btn btn-primary period',
19495                             html: 'AM'
19496                             
19497                         }
19498                     ]
19499                 }
19500             ]
19501         });
19502         
19503         time.createChild({
19504             tag: 'tr',
19505             cn: [
19506                 {
19507                     tag: 'td',
19508                     cn: [
19509                         {
19510                             tag: 'a',
19511                             href: '#',
19512                             cls: 'btn',
19513                             cn: [
19514                                 {
19515                                     tag: 'span',
19516                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19517                                 }
19518                             ]
19519                         }
19520                     ]
19521                 },
19522                 {
19523                     tag: 'td',
19524                     cls: 'separator'
19525                 },
19526                 {
19527                     tag: 'td',
19528                     cn: [
19529                         {
19530                             tag: 'a',
19531                             href: '#',
19532                             cls: 'btn',
19533                             cn: [
19534                                 {
19535                                     tag: 'span',
19536                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19537                                 }
19538                             ]
19539                         }
19540                     ]
19541                 },
19542                 {
19543                     tag: 'td',
19544                     cls: 'separator'
19545                 }
19546             ]
19547         });
19548         
19549     },
19550     
19551     update: function()
19552     {
19553         
19554         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19555         
19556         this.fill();
19557     },
19558     
19559     fill: function() 
19560     {
19561         var hours = this.time.getHours();
19562         var minutes = this.time.getMinutes();
19563         var period = 'AM';
19564         
19565         if(hours > 11){
19566             period = 'PM';
19567         }
19568         
19569         if(hours == 0){
19570             hours = 12;
19571         }
19572         
19573         
19574         if(hours > 12){
19575             hours = hours - 12;
19576         }
19577         
19578         if(hours < 10){
19579             hours = '0' + hours;
19580         }
19581         
19582         if(minutes < 10){
19583             minutes = '0' + minutes;
19584         }
19585         
19586         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19587         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19588         this.pop.select('button', true).first().dom.innerHTML = period;
19589         
19590     },
19591     
19592     place: function()
19593     {   
19594         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19595         
19596         var cls = ['bottom'];
19597         
19598         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19599             cls.pop();
19600             cls.push('top');
19601         }
19602         
19603         cls.push('right');
19604         
19605         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19606             cls.pop();
19607             cls.push('left');
19608         }
19609         
19610         this.picker().addClass(cls.join('-'));
19611         
19612         var _this = this;
19613         
19614         Roo.each(cls, function(c){
19615             if(c == 'bottom'){
19616                 _this.picker().setTop(_this.inputEl().getHeight());
19617                 return;
19618             }
19619             if(c == 'top'){
19620                 _this.picker().setTop(0 - _this.picker().getHeight());
19621                 return;
19622             }
19623             
19624             if(c == 'left'){
19625                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19626                 return;
19627             }
19628             if(c == 'right'){
19629                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19630                 return;
19631             }
19632         });
19633         
19634     },
19635   
19636     onFocus : function()
19637     {
19638         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19639         this.show();
19640     },
19641     
19642     onBlur : function()
19643     {
19644         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19645         this.hide();
19646     },
19647     
19648     show : function()
19649     {
19650         this.picker().show();
19651         this.pop.show();
19652         this.update();
19653         this.place();
19654         
19655         this.fireEvent('show', this, this.date);
19656     },
19657     
19658     hide : function()
19659     {
19660         this.picker().hide();
19661         this.pop.hide();
19662         
19663         this.fireEvent('hide', this, this.date);
19664     },
19665     
19666     setTime : function()
19667     {
19668         this.hide();
19669         this.setValue(this.time.format(this.format));
19670         
19671         this.fireEvent('select', this, this.date);
19672         
19673         
19674     },
19675     
19676     onMousedown: function(e){
19677         e.stopPropagation();
19678         e.preventDefault();
19679     },
19680     
19681     onIncrementHours: function()
19682     {
19683         Roo.log('onIncrementHours');
19684         this.time = this.time.add(Date.HOUR, 1);
19685         this.update();
19686         
19687     },
19688     
19689     onDecrementHours: function()
19690     {
19691         Roo.log('onDecrementHours');
19692         this.time = this.time.add(Date.HOUR, -1);
19693         this.update();
19694     },
19695     
19696     onIncrementMinutes: function()
19697     {
19698         Roo.log('onIncrementMinutes');
19699         this.time = this.time.add(Date.MINUTE, 1);
19700         this.update();
19701     },
19702     
19703     onDecrementMinutes: function()
19704     {
19705         Roo.log('onDecrementMinutes');
19706         this.time = this.time.add(Date.MINUTE, -1);
19707         this.update();
19708     },
19709     
19710     onTogglePeriod: function()
19711     {
19712         Roo.log('onTogglePeriod');
19713         this.time = this.time.add(Date.HOUR, 12);
19714         this.update();
19715     }
19716     
19717    
19718 });
19719
19720 Roo.apply(Roo.bootstrap.TimeField,  {
19721     
19722     content : {
19723         tag: 'tbody',
19724         cn: [
19725             {
19726                 tag: 'tr',
19727                 cn: [
19728                 {
19729                     tag: 'td',
19730                     colspan: '7'
19731                 }
19732                 ]
19733             }
19734         ]
19735     },
19736     
19737     footer : {
19738         tag: 'tfoot',
19739         cn: [
19740             {
19741                 tag: 'tr',
19742                 cn: [
19743                 {
19744                     tag: 'th',
19745                     colspan: '7',
19746                     cls: '',
19747                     cn: [
19748                         {
19749                             tag: 'button',
19750                             cls: 'btn btn-info ok',
19751                             html: 'OK'
19752                         }
19753                     ]
19754                 }
19755
19756                 ]
19757             }
19758         ]
19759     }
19760 });
19761
19762 Roo.apply(Roo.bootstrap.TimeField,  {
19763   
19764     template : {
19765         tag: 'div',
19766         cls: 'datepicker dropdown-menu',
19767         cn: [
19768             {
19769                 tag: 'div',
19770                 cls: 'datepicker-time',
19771                 cn: [
19772                 {
19773                     tag: 'table',
19774                     cls: 'table-condensed',
19775                     cn:[
19776                     Roo.bootstrap.TimeField.content,
19777                     Roo.bootstrap.TimeField.footer
19778                     ]
19779                 }
19780                 ]
19781             }
19782         ]
19783     }
19784 });
19785
19786  
19787
19788  /*
19789  * - LGPL
19790  *
19791  * MonthField
19792  * 
19793  */
19794
19795 /**
19796  * @class Roo.bootstrap.MonthField
19797  * @extends Roo.bootstrap.Input
19798  * Bootstrap MonthField class
19799  * 
19800  * @cfg {String} language default en
19801  * 
19802  * @constructor
19803  * Create a new MonthField
19804  * @param {Object} config The config object
19805  */
19806
19807 Roo.bootstrap.MonthField = function(config){
19808     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19809     
19810     this.addEvents({
19811         /**
19812          * @event show
19813          * Fires when this field show.
19814          * @param {Roo.bootstrap.MonthField} this
19815          * @param {Mixed} date The date value
19816          */
19817         show : true,
19818         /**
19819          * @event show
19820          * Fires when this field hide.
19821          * @param {Roo.bootstrap.MonthField} this
19822          * @param {Mixed} date The date value
19823          */
19824         hide : true,
19825         /**
19826          * @event select
19827          * Fires when select a date.
19828          * @param {Roo.bootstrap.MonthField} this
19829          * @param {String} oldvalue The old value
19830          * @param {String} newvalue The new value
19831          */
19832         select : true
19833     });
19834 };
19835
19836 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
19837     
19838     onRender: function(ct, position)
19839     {
19840         
19841         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19842         
19843         this.language = this.language || 'en';
19844         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19845         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19846         
19847         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19848         this.isInline = false;
19849         this.isInput = true;
19850         this.component = this.el.select('.add-on', true).first() || false;
19851         this.component = (this.component && this.component.length === 0) ? false : this.component;
19852         this.hasInput = this.component && this.inputEL().length;
19853         
19854         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19855         
19856         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19857         
19858         this.picker().on('mousedown', this.onMousedown, this);
19859         this.picker().on('click', this.onClick, this);
19860         
19861         this.picker().addClass('datepicker-dropdown');
19862         
19863         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19864             v.setStyle('width', '189px');
19865         });
19866         
19867         this.fillMonths();
19868         
19869         this.update();
19870         
19871         if(this.isInline) {
19872             this.show();
19873         }
19874         
19875     },
19876     
19877     setValue: function(v, suppressEvent)
19878     {   
19879         var o = this.getValue();
19880         
19881         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19882         
19883         this.update();
19884
19885         if(suppressEvent !== true){
19886             this.fireEvent('select', this, o, v);
19887         }
19888         
19889     },
19890     
19891     getValue: function()
19892     {
19893         return this.value;
19894     },
19895     
19896     onClick: function(e) 
19897     {
19898         e.stopPropagation();
19899         e.preventDefault();
19900         
19901         var target = e.getTarget();
19902         
19903         if(target.nodeName.toLowerCase() === 'i'){
19904             target = Roo.get(target).dom.parentNode;
19905         }
19906         
19907         var nodeName = target.nodeName;
19908         var className = target.className;
19909         var html = target.innerHTML;
19910         
19911         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19912             return;
19913         }
19914         
19915         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19916         
19917         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19918         
19919         this.hide();
19920                         
19921     },
19922     
19923     picker : function()
19924     {
19925         return this.pickerEl;
19926     },
19927     
19928     fillMonths: function()
19929     {    
19930         var i = 0;
19931         var months = this.picker().select('>.datepicker-months td', true).first();
19932         
19933         months.dom.innerHTML = '';
19934         
19935         while (i < 12) {
19936             var month = {
19937                 tag: 'span',
19938                 cls: 'month',
19939                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19940             };
19941             
19942             months.createChild(month);
19943         }
19944         
19945     },
19946     
19947     update: function()
19948     {
19949         var _this = this;
19950         
19951         if(typeof(this.vIndex) == 'undefined' && this.value.length){
19952             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19953         }
19954         
19955         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19956             e.removeClass('active');
19957             
19958             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19959                 e.addClass('active');
19960             }
19961         })
19962     },
19963     
19964     place: function()
19965     {
19966         if(this.isInline) {
19967             return;
19968         }
19969         
19970         this.picker().removeClass(['bottom', 'top']);
19971         
19972         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19973             /*
19974              * place to the top of element!
19975              *
19976              */
19977             
19978             this.picker().addClass('top');
19979             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19980             
19981             return;
19982         }
19983         
19984         this.picker().addClass('bottom');
19985         
19986         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19987     },
19988     
19989     onFocus : function()
19990     {
19991         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19992         this.show();
19993     },
19994     
19995     onBlur : function()
19996     {
19997         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19998         
19999         var d = this.inputEl().getValue();
20000         
20001         this.setValue(d);
20002                 
20003         this.hide();
20004     },
20005     
20006     show : function()
20007     {
20008         this.picker().show();
20009         this.picker().select('>.datepicker-months', true).first().show();
20010         this.update();
20011         this.place();
20012         
20013         this.fireEvent('show', this, this.date);
20014     },
20015     
20016     hide : function()
20017     {
20018         if(this.isInline) {
20019             return;
20020         }
20021         this.picker().hide();
20022         this.fireEvent('hide', this, this.date);
20023         
20024     },
20025     
20026     onMousedown: function(e)
20027     {
20028         e.stopPropagation();
20029         e.preventDefault();
20030     },
20031     
20032     keyup: function(e)
20033     {
20034         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20035         this.update();
20036     },
20037
20038     fireKey: function(e)
20039     {
20040         if (!this.picker().isVisible()){
20041             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20042                 this.show();
20043             }
20044             return;
20045         }
20046         
20047         var dir;
20048         
20049         switch(e.keyCode){
20050             case 27: // escape
20051                 this.hide();
20052                 e.preventDefault();
20053                 break;
20054             case 37: // left
20055             case 39: // right
20056                 dir = e.keyCode == 37 ? -1 : 1;
20057                 
20058                 this.vIndex = this.vIndex + dir;
20059                 
20060                 if(this.vIndex < 0){
20061                     this.vIndex = 0;
20062                 }
20063                 
20064                 if(this.vIndex > 11){
20065                     this.vIndex = 11;
20066                 }
20067                 
20068                 if(isNaN(this.vIndex)){
20069                     this.vIndex = 0;
20070                 }
20071                 
20072                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20073                 
20074                 break;
20075             case 38: // up
20076             case 40: // down
20077                 
20078                 dir = e.keyCode == 38 ? -1 : 1;
20079                 
20080                 this.vIndex = this.vIndex + dir * 4;
20081                 
20082                 if(this.vIndex < 0){
20083                     this.vIndex = 0;
20084                 }
20085                 
20086                 if(this.vIndex > 11){
20087                     this.vIndex = 11;
20088                 }
20089                 
20090                 if(isNaN(this.vIndex)){
20091                     this.vIndex = 0;
20092                 }
20093                 
20094                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20095                 break;
20096                 
20097             case 13: // enter
20098                 
20099                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20100                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20101                 }
20102                 
20103                 this.hide();
20104                 e.preventDefault();
20105                 break;
20106             case 9: // tab
20107                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20108                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20109                 }
20110                 this.hide();
20111                 break;
20112             case 16: // shift
20113             case 17: // ctrl
20114             case 18: // alt
20115                 break;
20116             default :
20117                 this.hide();
20118                 
20119         }
20120     },
20121     
20122     remove: function() 
20123     {
20124         this.picker().remove();
20125     }
20126    
20127 });
20128
20129 Roo.apply(Roo.bootstrap.MonthField,  {
20130     
20131     content : {
20132         tag: 'tbody',
20133         cn: [
20134         {
20135             tag: 'tr',
20136             cn: [
20137             {
20138                 tag: 'td',
20139                 colspan: '7'
20140             }
20141             ]
20142         }
20143         ]
20144     },
20145     
20146     dates:{
20147         en: {
20148             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20149             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20150         }
20151     }
20152 });
20153
20154 Roo.apply(Roo.bootstrap.MonthField,  {
20155   
20156     template : {
20157         tag: 'div',
20158         cls: 'datepicker dropdown-menu roo-dynamic',
20159         cn: [
20160             {
20161                 tag: 'div',
20162                 cls: 'datepicker-months',
20163                 cn: [
20164                 {
20165                     tag: 'table',
20166                     cls: 'table-condensed',
20167                     cn:[
20168                         Roo.bootstrap.DateField.content
20169                     ]
20170                 }
20171                 ]
20172             }
20173         ]
20174     }
20175 });
20176
20177  
20178
20179  
20180  /*
20181  * - LGPL
20182  *
20183  * CheckBox
20184  * 
20185  */
20186
20187 /**
20188  * @class Roo.bootstrap.CheckBox
20189  * @extends Roo.bootstrap.Input
20190  * Bootstrap CheckBox class
20191  * 
20192  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20193  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20194  * @cfg {String} boxLabel The text that appears beside the checkbox
20195  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20196  * @cfg {Boolean} checked initnal the element
20197  * @cfg {Boolean} inline inline the element (default false)
20198  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20199  * @cfg {String} tooltip label tooltip
20200  * 
20201  * @constructor
20202  * Create a new CheckBox
20203  * @param {Object} config The config object
20204  */
20205
20206 Roo.bootstrap.CheckBox = function(config){
20207     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20208    
20209     this.addEvents({
20210         /**
20211         * @event check
20212         * Fires when the element is checked or unchecked.
20213         * @param {Roo.bootstrap.CheckBox} this This input
20214         * @param {Boolean} checked The new checked value
20215         */
20216        check : true,
20217        /**
20218         * @event click
20219         * Fires when the element is click.
20220         * @param {Roo.bootstrap.CheckBox} this This input
20221         */
20222        click : true
20223     });
20224     
20225 };
20226
20227 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20228   
20229     inputType: 'checkbox',
20230     inputValue: 1,
20231     valueOff: 0,
20232     boxLabel: false,
20233     checked: false,
20234     weight : false,
20235     inline: false,
20236     tooltip : '',
20237     
20238     getAutoCreate : function()
20239     {
20240         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20241         
20242         var id = Roo.id();
20243         
20244         var cfg = {};
20245         
20246         cfg.cls = 'form-group ' + this.inputType; //input-group
20247         
20248         if(this.inline){
20249             cfg.cls += ' ' + this.inputType + '-inline';
20250         }
20251         
20252         var input =  {
20253             tag: 'input',
20254             id : id,
20255             type : this.inputType,
20256             value : this.inputValue,
20257             cls : 'roo-' + this.inputType, //'form-box',
20258             placeholder : this.placeholder || ''
20259             
20260         };
20261         
20262         if(this.inputType != 'radio'){
20263             var hidden =  {
20264                 tag: 'input',
20265                 type : 'hidden',
20266                 cls : 'roo-hidden-value',
20267                 value : this.checked ? this.inputValue : this.valueOff
20268             };
20269         }
20270         
20271             
20272         if (this.weight) { // Validity check?
20273             cfg.cls += " " + this.inputType + "-" + this.weight;
20274         }
20275         
20276         if (this.disabled) {
20277             input.disabled=true;
20278         }
20279         
20280         if(this.checked){
20281             input.checked = this.checked;
20282         }
20283         
20284         if (this.name) {
20285             
20286             input.name = this.name;
20287             
20288             if(this.inputType != 'radio'){
20289                 hidden.name = this.name;
20290                 input.name = '_hidden_' + this.name;
20291             }
20292         }
20293         
20294         if (this.size) {
20295             input.cls += ' input-' + this.size;
20296         }
20297         
20298         var settings=this;
20299         
20300         ['xs','sm','md','lg'].map(function(size){
20301             if (settings[size]) {
20302                 cfg.cls += ' col-' + size + '-' + settings[size];
20303             }
20304         });
20305         
20306         var inputblock = input;
20307          
20308         if (this.before || this.after) {
20309             
20310             inputblock = {
20311                 cls : 'input-group',
20312                 cn :  [] 
20313             };
20314             
20315             if (this.before) {
20316                 inputblock.cn.push({
20317                     tag :'span',
20318                     cls : 'input-group-addon',
20319                     html : this.before
20320                 });
20321             }
20322             
20323             inputblock.cn.push(input);
20324             
20325             if(this.inputType != 'radio'){
20326                 inputblock.cn.push(hidden);
20327             }
20328             
20329             if (this.after) {
20330                 inputblock.cn.push({
20331                     tag :'span',
20332                     cls : 'input-group-addon',
20333                     html : this.after
20334                 });
20335             }
20336             
20337         }
20338         
20339         if (align ==='left' && this.fieldLabel.length) {
20340 //                Roo.log("left and has label");
20341             cfg.cn = [
20342                 {
20343                     tag: 'label',
20344                     'for' :  id,
20345                     cls : 'control-label',
20346                     html : this.fieldLabel
20347                 },
20348                 {
20349                     cls : "", 
20350                     cn: [
20351                         inputblock
20352                     ]
20353                 }
20354             ];
20355             
20356             if(this.labelWidth > 12){
20357                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20358             }
20359             
20360             if(this.labelWidth < 13 && this.labelmd == 0){
20361                 this.labelmd = this.labelWidth;
20362             }
20363             
20364             if(this.labellg > 0){
20365                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20366                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20367             }
20368             
20369             if(this.labelmd > 0){
20370                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20371                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20372             }
20373             
20374             if(this.labelsm > 0){
20375                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20376                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20377             }
20378             
20379             if(this.labelxs > 0){
20380                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20381                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20382             }
20383             
20384         } else if ( this.fieldLabel.length) {
20385 //                Roo.log(" label");
20386                 cfg.cn = [
20387                    
20388                     {
20389                         tag: this.boxLabel ? 'span' : 'label',
20390                         'for': id,
20391                         cls: 'control-label box-input-label',
20392                         //cls : 'input-group-addon',
20393                         html : this.fieldLabel
20394                     },
20395                     
20396                     inputblock
20397                     
20398                 ];
20399
20400         } else {
20401             
20402 //                Roo.log(" no label && no align");
20403                 cfg.cn = [  inputblock ] ;
20404                 
20405                 
20406         }
20407         
20408         if(this.boxLabel){
20409              var boxLabelCfg = {
20410                 tag: 'label',
20411                 //'for': id, // box label is handled by onclick - so no for...
20412                 cls: 'box-label',
20413                 html: this.boxLabel
20414             };
20415             
20416             if(this.tooltip){
20417                 boxLabelCfg.tooltip = this.tooltip;
20418             }
20419              
20420             cfg.cn.push(boxLabelCfg);
20421         }
20422         
20423         if(this.inputType != 'radio'){
20424             cfg.cn.push(hidden);
20425         }
20426         
20427         return cfg;
20428         
20429     },
20430     
20431     /**
20432      * return the real input element.
20433      */
20434     inputEl: function ()
20435     {
20436         return this.el.select('input.roo-' + this.inputType,true).first();
20437     },
20438     hiddenEl: function ()
20439     {
20440         return this.el.select('input.roo-hidden-value',true).first();
20441     },
20442     
20443     labelEl: function()
20444     {
20445         return this.el.select('label.control-label',true).first();
20446     },
20447     /* depricated... */
20448     
20449     label: function()
20450     {
20451         return this.labelEl();
20452     },
20453     
20454     boxLabelEl: function()
20455     {
20456         return this.el.select('label.box-label',true).first();
20457     },
20458     
20459     initEvents : function()
20460     {
20461 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20462         
20463         this.inputEl().on('click', this.onClick,  this);
20464         
20465         if (this.boxLabel) { 
20466             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20467         }
20468         
20469         this.startValue = this.getValue();
20470         
20471         if(this.groupId){
20472             Roo.bootstrap.CheckBox.register(this);
20473         }
20474     },
20475     
20476     onClick : function(e)
20477     {   
20478         if(this.fireEvent('click', this, e) !== false){
20479             this.setChecked(!this.checked);
20480         }
20481         
20482     },
20483     
20484     setChecked : function(state,suppressEvent)
20485     {
20486         this.startValue = this.getValue();
20487
20488         if(this.inputType == 'radio'){
20489             
20490             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20491                 e.dom.checked = false;
20492             });
20493             
20494             this.inputEl().dom.checked = true;
20495             
20496             this.inputEl().dom.value = this.inputValue;
20497             
20498             if(suppressEvent !== true){
20499                 this.fireEvent('check', this, true);
20500             }
20501             
20502             this.validate();
20503             
20504             return;
20505         }
20506         
20507         this.checked = state;
20508         
20509         this.inputEl().dom.checked = state;
20510         
20511         
20512         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20513         
20514         if(suppressEvent !== true){
20515             this.fireEvent('check', this, state);
20516         }
20517         
20518         this.validate();
20519     },
20520     
20521     getValue : function()
20522     {
20523         if(this.inputType == 'radio'){
20524             return this.getGroupValue();
20525         }
20526         
20527         return this.hiddenEl().dom.value;
20528         
20529     },
20530     
20531     getGroupValue : function()
20532     {
20533         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20534             return '';
20535         }
20536         
20537         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20538     },
20539     
20540     setValue : function(v,suppressEvent)
20541     {
20542         if(this.inputType == 'radio'){
20543             this.setGroupValue(v, suppressEvent);
20544             return;
20545         }
20546         
20547         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20548         
20549         this.validate();
20550     },
20551     
20552     setGroupValue : function(v, suppressEvent)
20553     {
20554         this.startValue = this.getValue();
20555         
20556         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20557             e.dom.checked = false;
20558             
20559             if(e.dom.value == v){
20560                 e.dom.checked = true;
20561             }
20562         });
20563         
20564         if(suppressEvent !== true){
20565             this.fireEvent('check', this, true);
20566         }
20567
20568         this.validate();
20569         
20570         return;
20571     },
20572     
20573     validate : function()
20574     {
20575         if(
20576                 this.disabled || 
20577                 (this.inputType == 'radio' && this.validateRadio()) ||
20578                 (this.inputType == 'checkbox' && this.validateCheckbox())
20579         ){
20580             this.markValid();
20581             return true;
20582         }
20583         
20584         this.markInvalid();
20585         return false;
20586     },
20587     
20588     validateRadio : function()
20589     {
20590         if(this.allowBlank){
20591             return true;
20592         }
20593         
20594         var valid = false;
20595         
20596         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20597             if(!e.dom.checked){
20598                 return;
20599             }
20600             
20601             valid = true;
20602             
20603             return false;
20604         });
20605         
20606         return valid;
20607     },
20608     
20609     validateCheckbox : function()
20610     {
20611         if(!this.groupId){
20612             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20613             //return (this.getValue() == this.inputValue) ? true : false;
20614         }
20615         
20616         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20617         
20618         if(!group){
20619             return false;
20620         }
20621         
20622         var r = false;
20623         
20624         for(var i in group){
20625             if(group[i].el.isVisible(true)){
20626                 r = false;
20627                 break;
20628             }
20629             
20630             r = true;
20631         }
20632         
20633         for(var i in group){
20634             if(r){
20635                 break;
20636             }
20637             
20638             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20639         }
20640         
20641         return r;
20642     },
20643     
20644     /**
20645      * Mark this field as valid
20646      */
20647     markValid : function()
20648     {
20649         var _this = this;
20650         
20651         this.fireEvent('valid', this);
20652         
20653         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20654         
20655         if(this.groupId){
20656             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20657         }
20658         
20659         if(label){
20660             label.markValid();
20661         }
20662
20663         if(this.inputType == 'radio'){
20664             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20665                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20666                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20667             });
20668             
20669             return;
20670         }
20671
20672         if(!this.groupId){
20673             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20674             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20675             return;
20676         }
20677         
20678         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20679         
20680         if(!group){
20681             return;
20682         }
20683         
20684         for(var i in group){
20685             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20686             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20687         }
20688     },
20689     
20690      /**
20691      * Mark this field as invalid
20692      * @param {String} msg The validation message
20693      */
20694     markInvalid : function(msg)
20695     {
20696         if(this.allowBlank){
20697             return;
20698         }
20699         
20700         var _this = this;
20701         
20702         this.fireEvent('invalid', this, msg);
20703         
20704         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20705         
20706         if(this.groupId){
20707             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20708         }
20709         
20710         if(label){
20711             label.markInvalid();
20712         }
20713             
20714         if(this.inputType == 'radio'){
20715             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20716                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20717                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20718             });
20719             
20720             return;
20721         }
20722         
20723         if(!this.groupId){
20724             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20725             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20726             return;
20727         }
20728         
20729         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20730         
20731         if(!group){
20732             return;
20733         }
20734         
20735         for(var i in group){
20736             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20737             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20738         }
20739         
20740     },
20741     
20742     clearInvalid : function()
20743     {
20744         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20745         
20746         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20747         
20748         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20749         
20750         if (label && label.iconEl) {
20751             label.iconEl.removeClass(label.validClass);
20752             label.iconEl.removeClass(label.invalidClass);
20753         }
20754     },
20755     
20756     disable : function()
20757     {
20758         if(this.inputType != 'radio'){
20759             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20760             return;
20761         }
20762         
20763         var _this = this;
20764         
20765         if(this.rendered){
20766             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20767                 _this.getActionEl().addClass(this.disabledClass);
20768                 e.dom.disabled = true;
20769             });
20770         }
20771         
20772         this.disabled = true;
20773         this.fireEvent("disable", this);
20774         return this;
20775     },
20776
20777     enable : function()
20778     {
20779         if(this.inputType != 'radio'){
20780             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20781             return;
20782         }
20783         
20784         var _this = this;
20785         
20786         if(this.rendered){
20787             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20788                 _this.getActionEl().removeClass(this.disabledClass);
20789                 e.dom.disabled = false;
20790             });
20791         }
20792         
20793         this.disabled = false;
20794         this.fireEvent("enable", this);
20795         return this;
20796     },
20797     
20798     setBoxLabel : function(v)
20799     {
20800         this.boxLabel = v;
20801         
20802         if(this.rendered){
20803             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
20804         }
20805     }
20806
20807 });
20808
20809 Roo.apply(Roo.bootstrap.CheckBox, {
20810     
20811     groups: {},
20812     
20813      /**
20814     * register a CheckBox Group
20815     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20816     */
20817     register : function(checkbox)
20818     {
20819         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20820             this.groups[checkbox.groupId] = {};
20821         }
20822         
20823         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20824             return;
20825         }
20826         
20827         this.groups[checkbox.groupId][checkbox.name] = checkbox;
20828         
20829     },
20830     /**
20831     * fetch a CheckBox Group based on the group ID
20832     * @param {string} the group ID
20833     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20834     */
20835     get: function(groupId) {
20836         if (typeof(this.groups[groupId]) == 'undefined') {
20837             return false;
20838         }
20839         
20840         return this.groups[groupId] ;
20841     }
20842     
20843     
20844 });
20845 /*
20846  * - LGPL
20847  *
20848  * RadioItem
20849  * 
20850  */
20851
20852 /**
20853  * @class Roo.bootstrap.Radio
20854  * @extends Roo.bootstrap.Component
20855  * Bootstrap Radio class
20856  * @cfg {String} boxLabel - the label associated
20857  * @cfg {String} value - the value of radio
20858  * 
20859  * @constructor
20860  * Create a new Radio
20861  * @param {Object} config The config object
20862  */
20863 Roo.bootstrap.Radio = function(config){
20864     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20865     
20866 };
20867
20868 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
20869     
20870     boxLabel : '',
20871     
20872     value : '',
20873     
20874     getAutoCreate : function()
20875     {
20876         var cfg = {
20877             tag : 'div',
20878             cls : 'form-group radio',
20879             cn : [
20880                 {
20881                     tag : 'label',
20882                     cls : 'box-label',
20883                     html : this.boxLabel
20884                 }
20885             ]
20886         };
20887         
20888         return cfg;
20889     },
20890     
20891     initEvents : function() 
20892     {
20893         this.parent().register(this);
20894         
20895         this.el.on('click', this.onClick, this);
20896         
20897     },
20898     
20899     onClick : function()
20900     {
20901         this.setChecked(true);
20902     },
20903     
20904     setChecked : function(state, suppressEvent)
20905     {
20906         this.parent().setValue(this.value, suppressEvent);
20907         
20908     },
20909     
20910     setBoxLabel : function(v)
20911     {
20912         this.boxLabel = v;
20913         
20914         if(this.rendered){
20915             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
20916         }
20917     }
20918     
20919 });
20920  
20921
20922  /*
20923  * - LGPL
20924  *
20925  * Input
20926  * 
20927  */
20928
20929 /**
20930  * @class Roo.bootstrap.SecurePass
20931  * @extends Roo.bootstrap.Input
20932  * Bootstrap SecurePass class
20933  *
20934  * 
20935  * @constructor
20936  * Create a new SecurePass
20937  * @param {Object} config The config object
20938  */
20939  
20940 Roo.bootstrap.SecurePass = function (config) {
20941     // these go here, so the translation tool can replace them..
20942     this.errors = {
20943         PwdEmpty: "Please type a password, and then retype it to confirm.",
20944         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20945         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20946         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20947         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20948         FNInPwd: "Your password can't contain your first name. Please type a different password.",
20949         LNInPwd: "Your password can't contain your last name. Please type a different password.",
20950         TooWeak: "Your password is Too Weak."
20951     },
20952     this.meterLabel = "Password strength:";
20953     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
20954     this.meterClass = [
20955         "roo-password-meter-tooweak", 
20956         "roo-password-meter-weak", 
20957         "roo-password-meter-medium", 
20958         "roo-password-meter-strong", 
20959         "roo-password-meter-grey"
20960     ];
20961     
20962     this.errors = {};
20963     
20964     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
20965 }
20966
20967 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
20968     /**
20969      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
20970      * {
20971      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
20972      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20973      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20974      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20975      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20976      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
20977      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
20978      * })
20979      */
20980     // private
20981     
20982     meterWidth: 300,
20983     errorMsg :'',    
20984     errors: false,
20985     imageRoot: '/',
20986     /**
20987      * @cfg {String/Object} Label for the strength meter (defaults to
20988      * 'Password strength:')
20989      */
20990     // private
20991     meterLabel: '',
20992     /**
20993      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
20994      * ['Weak', 'Medium', 'Strong'])
20995      */
20996     // private    
20997     pwdStrengths: false,    
20998     // private
20999     strength: 0,
21000     // private
21001     _lastPwd: null,
21002     // private
21003     kCapitalLetter: 0,
21004     kSmallLetter: 1,
21005     kDigit: 2,
21006     kPunctuation: 3,
21007     
21008     insecure: false,
21009     // private
21010     initEvents: function ()
21011     {
21012         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
21013
21014         if (this.el.is('input[type=password]') && Roo.isSafari) {
21015             this.el.on('keydown', this.SafariOnKeyDown, this);
21016         }
21017
21018         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21019     },
21020     // private
21021     onRender: function (ct, position)
21022     {
21023         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21024         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21025         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21026
21027         this.trigger.createChild({
21028                    cn: [
21029                     {
21030                     //id: 'PwdMeter',
21031                     tag: 'div',
21032                     cls: 'roo-password-meter-grey col-xs-12',
21033                     style: {
21034                         //width: 0,
21035                         //width: this.meterWidth + 'px'                                                
21036                         }
21037                     },
21038                     {                            
21039                          cls: 'roo-password-meter-text'                          
21040                     }
21041                 ]            
21042         });
21043
21044          
21045         if (this.hideTrigger) {
21046             this.trigger.setDisplayed(false);
21047         }
21048         this.setSize(this.width || '', this.height || '');
21049     },
21050     // private
21051     onDestroy: function ()
21052     {
21053         if (this.trigger) {
21054             this.trigger.removeAllListeners();
21055             this.trigger.remove();
21056         }
21057         if (this.wrap) {
21058             this.wrap.remove();
21059         }
21060         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21061     },
21062     // private
21063     checkStrength: function ()
21064     {
21065         var pwd = this.inputEl().getValue();
21066         if (pwd == this._lastPwd) {
21067             return;
21068         }
21069
21070         var strength;
21071         if (this.ClientSideStrongPassword(pwd)) {
21072             strength = 3;
21073         } else if (this.ClientSideMediumPassword(pwd)) {
21074             strength = 2;
21075         } else if (this.ClientSideWeakPassword(pwd)) {
21076             strength = 1;
21077         } else {
21078             strength = 0;
21079         }
21080         
21081         Roo.log('strength1: ' + strength);
21082         
21083         //var pm = this.trigger.child('div/div/div').dom;
21084         var pm = this.trigger.child('div/div');
21085         pm.removeClass(this.meterClass);
21086         pm.addClass(this.meterClass[strength]);
21087                 
21088         
21089         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21090                 
21091         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21092         
21093         this._lastPwd = pwd;
21094     },
21095     reset: function ()
21096     {
21097         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21098         
21099         this._lastPwd = '';
21100         
21101         var pm = this.trigger.child('div/div');
21102         pm.removeClass(this.meterClass);
21103         pm.addClass('roo-password-meter-grey');        
21104         
21105         
21106         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21107         
21108         pt.innerHTML = '';
21109         this.inputEl().dom.type='password';
21110     },
21111     // private
21112     validateValue: function (value)
21113     {
21114         
21115         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21116             return false;
21117         }
21118         if (value.length == 0) {
21119             if (this.allowBlank) {
21120                 this.clearInvalid();
21121                 return true;
21122             }
21123
21124             this.markInvalid(this.errors.PwdEmpty);
21125             this.errorMsg = this.errors.PwdEmpty;
21126             return false;
21127         }
21128         
21129         if(this.insecure){
21130             return true;
21131         }
21132         
21133         if ('[\x21-\x7e]*'.match(value)) {
21134             this.markInvalid(this.errors.PwdBadChar);
21135             this.errorMsg = this.errors.PwdBadChar;
21136             return false;
21137         }
21138         if (value.length < 6) {
21139             this.markInvalid(this.errors.PwdShort);
21140             this.errorMsg = this.errors.PwdShort;
21141             return false;
21142         }
21143         if (value.length > 16) {
21144             this.markInvalid(this.errors.PwdLong);
21145             this.errorMsg = this.errors.PwdLong;
21146             return false;
21147         }
21148         var strength;
21149         if (this.ClientSideStrongPassword(value)) {
21150             strength = 3;
21151         } else if (this.ClientSideMediumPassword(value)) {
21152             strength = 2;
21153         } else if (this.ClientSideWeakPassword(value)) {
21154             strength = 1;
21155         } else {
21156             strength = 0;
21157         }
21158
21159         
21160         if (strength < 2) {
21161             //this.markInvalid(this.errors.TooWeak);
21162             this.errorMsg = this.errors.TooWeak;
21163             //return false;
21164         }
21165         
21166         
21167         console.log('strength2: ' + strength);
21168         
21169         //var pm = this.trigger.child('div/div/div').dom;
21170         
21171         var pm = this.trigger.child('div/div');
21172         pm.removeClass(this.meterClass);
21173         pm.addClass(this.meterClass[strength]);
21174                 
21175         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21176                 
21177         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21178         
21179         this.errorMsg = ''; 
21180         return true;
21181     },
21182     // private
21183     CharacterSetChecks: function (type)
21184     {
21185         this.type = type;
21186         this.fResult = false;
21187     },
21188     // private
21189     isctype: function (character, type)
21190     {
21191         switch (type) {  
21192             case this.kCapitalLetter:
21193                 if (character >= 'A' && character <= 'Z') {
21194                     return true;
21195                 }
21196                 break;
21197             
21198             case this.kSmallLetter:
21199                 if (character >= 'a' && character <= 'z') {
21200                     return true;
21201                 }
21202                 break;
21203             
21204             case this.kDigit:
21205                 if (character >= '0' && character <= '9') {
21206                     return true;
21207                 }
21208                 break;
21209             
21210             case this.kPunctuation:
21211                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21212                     return true;
21213                 }
21214                 break;
21215             
21216             default:
21217                 return false;
21218         }
21219
21220     },
21221     // private
21222     IsLongEnough: function (pwd, size)
21223     {
21224         return !(pwd == null || isNaN(size) || pwd.length < size);
21225     },
21226     // private
21227     SpansEnoughCharacterSets: function (word, nb)
21228     {
21229         if (!this.IsLongEnough(word, nb))
21230         {
21231             return false;
21232         }
21233
21234         var characterSetChecks = new Array(
21235             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21236             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21237         );
21238         
21239         for (var index = 0; index < word.length; ++index) {
21240             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21241                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21242                     characterSetChecks[nCharSet].fResult = true;
21243                     break;
21244                 }
21245             }
21246         }
21247
21248         var nCharSets = 0;
21249         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21250             if (characterSetChecks[nCharSet].fResult) {
21251                 ++nCharSets;
21252             }
21253         }
21254
21255         if (nCharSets < nb) {
21256             return false;
21257         }
21258         return true;
21259     },
21260     // private
21261     ClientSideStrongPassword: function (pwd)
21262     {
21263         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21264     },
21265     // private
21266     ClientSideMediumPassword: function (pwd)
21267     {
21268         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21269     },
21270     // private
21271     ClientSideWeakPassword: function (pwd)
21272     {
21273         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21274     }
21275           
21276 })//<script type="text/javascript">
21277
21278 /*
21279  * Based  Ext JS Library 1.1.1
21280  * Copyright(c) 2006-2007, Ext JS, LLC.
21281  * LGPL
21282  *
21283  */
21284  
21285 /**
21286  * @class Roo.HtmlEditorCore
21287  * @extends Roo.Component
21288  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21289  *
21290  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21291  */
21292
21293 Roo.HtmlEditorCore = function(config){
21294     
21295     
21296     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21297     
21298     
21299     this.addEvents({
21300         /**
21301          * @event initialize
21302          * Fires when the editor is fully initialized (including the iframe)
21303          * @param {Roo.HtmlEditorCore} this
21304          */
21305         initialize: true,
21306         /**
21307          * @event activate
21308          * Fires when the editor is first receives the focus. Any insertion must wait
21309          * until after this event.
21310          * @param {Roo.HtmlEditorCore} this
21311          */
21312         activate: true,
21313          /**
21314          * @event beforesync
21315          * Fires before the textarea is updated with content from the editor iframe. Return false
21316          * to cancel the sync.
21317          * @param {Roo.HtmlEditorCore} this
21318          * @param {String} html
21319          */
21320         beforesync: true,
21321          /**
21322          * @event beforepush
21323          * Fires before the iframe editor is updated with content from the textarea. Return false
21324          * to cancel the push.
21325          * @param {Roo.HtmlEditorCore} this
21326          * @param {String} html
21327          */
21328         beforepush: true,
21329          /**
21330          * @event sync
21331          * Fires when the textarea is updated with content from the editor iframe.
21332          * @param {Roo.HtmlEditorCore} this
21333          * @param {String} html
21334          */
21335         sync: true,
21336          /**
21337          * @event push
21338          * Fires when the iframe editor is updated with content from the textarea.
21339          * @param {Roo.HtmlEditorCore} this
21340          * @param {String} html
21341          */
21342         push: true,
21343         
21344         /**
21345          * @event editorevent
21346          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21347          * @param {Roo.HtmlEditorCore} this
21348          */
21349         editorevent: true
21350         
21351     });
21352     
21353     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21354     
21355     // defaults : white / black...
21356     this.applyBlacklists();
21357     
21358     
21359     
21360 };
21361
21362
21363 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21364
21365
21366      /**
21367      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21368      */
21369     
21370     owner : false,
21371     
21372      /**
21373      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21374      *                        Roo.resizable.
21375      */
21376     resizable : false,
21377      /**
21378      * @cfg {Number} height (in pixels)
21379      */   
21380     height: 300,
21381    /**
21382      * @cfg {Number} width (in pixels)
21383      */   
21384     width: 500,
21385     
21386     /**
21387      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21388      * 
21389      */
21390     stylesheets: false,
21391     
21392     // id of frame..
21393     frameId: false,
21394     
21395     // private properties
21396     validationEvent : false,
21397     deferHeight: true,
21398     initialized : false,
21399     activated : false,
21400     sourceEditMode : false,
21401     onFocus : Roo.emptyFn,
21402     iframePad:3,
21403     hideMode:'offsets',
21404     
21405     clearUp: true,
21406     
21407     // blacklist + whitelisted elements..
21408     black: false,
21409     white: false,
21410      
21411     bodyCls : '',
21412
21413     /**
21414      * Protected method that will not generally be called directly. It
21415      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21416      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21417      */
21418     getDocMarkup : function(){
21419         // body styles..
21420         var st = '';
21421         
21422         // inherit styels from page...?? 
21423         if (this.stylesheets === false) {
21424             
21425             Roo.get(document.head).select('style').each(function(node) {
21426                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21427             });
21428             
21429             Roo.get(document.head).select('link').each(function(node) { 
21430                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21431             });
21432             
21433         } else if (!this.stylesheets.length) {
21434                 // simple..
21435                 st = '<style type="text/css">' +
21436                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21437                    '</style>';
21438         } else { 
21439             st = '<style type="text/css">' +
21440                     this.stylesheets +
21441                 '</style>';
21442         }
21443         
21444         st +=  '<style type="text/css">' +
21445             'IMG { cursor: pointer } ' +
21446         '</style>';
21447
21448         var cls = 'roo-htmleditor-body';
21449         
21450         if(this.bodyCls.length){
21451             cls += ' ' + this.bodyCls;
21452         }
21453         
21454         return '<html><head>' + st  +
21455             //<style type="text/css">' +
21456             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21457             //'</style>' +
21458             ' </head><body class="' +  cls + '"></body></html>';
21459     },
21460
21461     // private
21462     onRender : function(ct, position)
21463     {
21464         var _t = this;
21465         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21466         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21467         
21468         
21469         this.el.dom.style.border = '0 none';
21470         this.el.dom.setAttribute('tabIndex', -1);
21471         this.el.addClass('x-hidden hide');
21472         
21473         
21474         
21475         if(Roo.isIE){ // fix IE 1px bogus margin
21476             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21477         }
21478        
21479         
21480         this.frameId = Roo.id();
21481         
21482          
21483         
21484         var iframe = this.owner.wrap.createChild({
21485             tag: 'iframe',
21486             cls: 'form-control', // bootstrap..
21487             id: this.frameId,
21488             name: this.frameId,
21489             frameBorder : 'no',
21490             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21491         }, this.el
21492         );
21493         
21494         
21495         this.iframe = iframe.dom;
21496
21497          this.assignDocWin();
21498         
21499         this.doc.designMode = 'on';
21500        
21501         this.doc.open();
21502         this.doc.write(this.getDocMarkup());
21503         this.doc.close();
21504
21505         
21506         var task = { // must defer to wait for browser to be ready
21507             run : function(){
21508                 //console.log("run task?" + this.doc.readyState);
21509                 this.assignDocWin();
21510                 if(this.doc.body || this.doc.readyState == 'complete'){
21511                     try {
21512                         this.doc.designMode="on";
21513                     } catch (e) {
21514                         return;
21515                     }
21516                     Roo.TaskMgr.stop(task);
21517                     this.initEditor.defer(10, this);
21518                 }
21519             },
21520             interval : 10,
21521             duration: 10000,
21522             scope: this
21523         };
21524         Roo.TaskMgr.start(task);
21525
21526     },
21527
21528     // private
21529     onResize : function(w, h)
21530     {
21531          Roo.log('resize: ' +w + ',' + h );
21532         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21533         if(!this.iframe){
21534             return;
21535         }
21536         if(typeof w == 'number'){
21537             
21538             this.iframe.style.width = w + 'px';
21539         }
21540         if(typeof h == 'number'){
21541             
21542             this.iframe.style.height = h + 'px';
21543             if(this.doc){
21544                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21545             }
21546         }
21547         
21548     },
21549
21550     /**
21551      * Toggles the editor between standard and source edit mode.
21552      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21553      */
21554     toggleSourceEdit : function(sourceEditMode){
21555         
21556         this.sourceEditMode = sourceEditMode === true;
21557         
21558         if(this.sourceEditMode){
21559  
21560             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21561             
21562         }else{
21563             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21564             //this.iframe.className = '';
21565             this.deferFocus();
21566         }
21567         //this.setSize(this.owner.wrap.getSize());
21568         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21569     },
21570
21571     
21572   
21573
21574     /**
21575      * Protected method that will not generally be called directly. If you need/want
21576      * custom HTML cleanup, this is the method you should override.
21577      * @param {String} html The HTML to be cleaned
21578      * return {String} The cleaned HTML
21579      */
21580     cleanHtml : function(html){
21581         html = String(html);
21582         if(html.length > 5){
21583             if(Roo.isSafari){ // strip safari nonsense
21584                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21585             }
21586         }
21587         if(html == '&nbsp;'){
21588             html = '';
21589         }
21590         return html;
21591     },
21592
21593     /**
21594      * HTML Editor -> Textarea
21595      * Protected method that will not generally be called directly. Syncs the contents
21596      * of the editor iframe with the textarea.
21597      */
21598     syncValue : function(){
21599         if(this.initialized){
21600             var bd = (this.doc.body || this.doc.documentElement);
21601             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21602             var html = bd.innerHTML;
21603             if(Roo.isSafari){
21604                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21605                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21606                 if(m && m[1]){
21607                     html = '<div style="'+m[0]+'">' + html + '</div>';
21608                 }
21609             }
21610             html = this.cleanHtml(html);
21611             // fix up the special chars.. normaly like back quotes in word...
21612             // however we do not want to do this with chinese..
21613             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21614                 var cc = b.charCodeAt();
21615                 if (
21616                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21617                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21618                     (cc >= 0xf900 && cc < 0xfb00 )
21619                 ) {
21620                         return b;
21621                 }
21622                 return "&#"+cc+";" 
21623             });
21624             if(this.owner.fireEvent('beforesync', this, html) !== false){
21625                 this.el.dom.value = html;
21626                 this.owner.fireEvent('sync', this, html);
21627             }
21628         }
21629     },
21630
21631     /**
21632      * Protected method that will not generally be called directly. Pushes the value of the textarea
21633      * into the iframe editor.
21634      */
21635     pushValue : function(){
21636         if(this.initialized){
21637             var v = this.el.dom.value.trim();
21638             
21639 //            if(v.length < 1){
21640 //                v = '&#160;';
21641 //            }
21642             
21643             if(this.owner.fireEvent('beforepush', this, v) !== false){
21644                 var d = (this.doc.body || this.doc.documentElement);
21645                 d.innerHTML = v;
21646                 this.cleanUpPaste();
21647                 this.el.dom.value = d.innerHTML;
21648                 this.owner.fireEvent('push', this, v);
21649             }
21650         }
21651     },
21652
21653     // private
21654     deferFocus : function(){
21655         this.focus.defer(10, this);
21656     },
21657
21658     // doc'ed in Field
21659     focus : function(){
21660         if(this.win && !this.sourceEditMode){
21661             this.win.focus();
21662         }else{
21663             this.el.focus();
21664         }
21665     },
21666     
21667     assignDocWin: function()
21668     {
21669         var iframe = this.iframe;
21670         
21671          if(Roo.isIE){
21672             this.doc = iframe.contentWindow.document;
21673             this.win = iframe.contentWindow;
21674         } else {
21675 //            if (!Roo.get(this.frameId)) {
21676 //                return;
21677 //            }
21678 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21679 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21680             
21681             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21682                 return;
21683             }
21684             
21685             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21686             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21687         }
21688     },
21689     
21690     // private
21691     initEditor : function(){
21692         //console.log("INIT EDITOR");
21693         this.assignDocWin();
21694         
21695         
21696         
21697         this.doc.designMode="on";
21698         this.doc.open();
21699         this.doc.write(this.getDocMarkup());
21700         this.doc.close();
21701         
21702         var dbody = (this.doc.body || this.doc.documentElement);
21703         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21704         // this copies styles from the containing element into thsi one..
21705         // not sure why we need all of this..
21706         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21707         
21708         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21709         //ss['background-attachment'] = 'fixed'; // w3c
21710         dbody.bgProperties = 'fixed'; // ie
21711         //Roo.DomHelper.applyStyles(dbody, ss);
21712         Roo.EventManager.on(this.doc, {
21713             //'mousedown': this.onEditorEvent,
21714             'mouseup': this.onEditorEvent,
21715             'dblclick': this.onEditorEvent,
21716             'click': this.onEditorEvent,
21717             'keyup': this.onEditorEvent,
21718             buffer:100,
21719             scope: this
21720         });
21721         if(Roo.isGecko){
21722             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21723         }
21724         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21725             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21726         }
21727         this.initialized = true;
21728
21729         this.owner.fireEvent('initialize', this);
21730         this.pushValue();
21731     },
21732
21733     // private
21734     onDestroy : function(){
21735         
21736         
21737         
21738         if(this.rendered){
21739             
21740             //for (var i =0; i < this.toolbars.length;i++) {
21741             //    // fixme - ask toolbars for heights?
21742             //    this.toolbars[i].onDestroy();
21743            // }
21744             
21745             //this.wrap.dom.innerHTML = '';
21746             //this.wrap.remove();
21747         }
21748     },
21749
21750     // private
21751     onFirstFocus : function(){
21752         
21753         this.assignDocWin();
21754         
21755         
21756         this.activated = true;
21757          
21758     
21759         if(Roo.isGecko){ // prevent silly gecko errors
21760             this.win.focus();
21761             var s = this.win.getSelection();
21762             if(!s.focusNode || s.focusNode.nodeType != 3){
21763                 var r = s.getRangeAt(0);
21764                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21765                 r.collapse(true);
21766                 this.deferFocus();
21767             }
21768             try{
21769                 this.execCmd('useCSS', true);
21770                 this.execCmd('styleWithCSS', false);
21771             }catch(e){}
21772         }
21773         this.owner.fireEvent('activate', this);
21774     },
21775
21776     // private
21777     adjustFont: function(btn){
21778         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21779         //if(Roo.isSafari){ // safari
21780         //    adjust *= 2;
21781        // }
21782         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21783         if(Roo.isSafari){ // safari
21784             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21785             v =  (v < 10) ? 10 : v;
21786             v =  (v > 48) ? 48 : v;
21787             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21788             
21789         }
21790         
21791         
21792         v = Math.max(1, v+adjust);
21793         
21794         this.execCmd('FontSize', v  );
21795     },
21796
21797     onEditorEvent : function(e)
21798     {
21799         this.owner.fireEvent('editorevent', this, e);
21800       //  this.updateToolbar();
21801         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21802     },
21803
21804     insertTag : function(tg)
21805     {
21806         // could be a bit smarter... -> wrap the current selected tRoo..
21807         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21808             
21809             range = this.createRange(this.getSelection());
21810             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21811             wrappingNode.appendChild(range.extractContents());
21812             range.insertNode(wrappingNode);
21813
21814             return;
21815             
21816             
21817             
21818         }
21819         this.execCmd("formatblock",   tg);
21820         
21821     },
21822     
21823     insertText : function(txt)
21824     {
21825         
21826         
21827         var range = this.createRange();
21828         range.deleteContents();
21829                //alert(Sender.getAttribute('label'));
21830                
21831         range.insertNode(this.doc.createTextNode(txt));
21832     } ,
21833     
21834      
21835
21836     /**
21837      * Executes a Midas editor command on the editor document and performs necessary focus and
21838      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21839      * @param {String} cmd The Midas command
21840      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21841      */
21842     relayCmd : function(cmd, value){
21843         this.win.focus();
21844         this.execCmd(cmd, value);
21845         this.owner.fireEvent('editorevent', this);
21846         //this.updateToolbar();
21847         this.owner.deferFocus();
21848     },
21849
21850     /**
21851      * Executes a Midas editor command directly on the editor document.
21852      * For visual commands, you should use {@link #relayCmd} instead.
21853      * <b>This should only be called after the editor is initialized.</b>
21854      * @param {String} cmd The Midas command
21855      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21856      */
21857     execCmd : function(cmd, value){
21858         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21859         this.syncValue();
21860     },
21861  
21862  
21863    
21864     /**
21865      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21866      * to insert tRoo.
21867      * @param {String} text | dom node.. 
21868      */
21869     insertAtCursor : function(text)
21870     {
21871         
21872         if(!this.activated){
21873             return;
21874         }
21875         /*
21876         if(Roo.isIE){
21877             this.win.focus();
21878             var r = this.doc.selection.createRange();
21879             if(r){
21880                 r.collapse(true);
21881                 r.pasteHTML(text);
21882                 this.syncValue();
21883                 this.deferFocus();
21884             
21885             }
21886             return;
21887         }
21888         */
21889         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21890             this.win.focus();
21891             
21892             
21893             // from jquery ui (MIT licenced)
21894             var range, node;
21895             var win = this.win;
21896             
21897             if (win.getSelection && win.getSelection().getRangeAt) {
21898                 range = win.getSelection().getRangeAt(0);
21899                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21900                 range.insertNode(node);
21901             } else if (win.document.selection && win.document.selection.createRange) {
21902                 // no firefox support
21903                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21904                 win.document.selection.createRange().pasteHTML(txt);
21905             } else {
21906                 // no firefox support
21907                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21908                 this.execCmd('InsertHTML', txt);
21909             } 
21910             
21911             this.syncValue();
21912             
21913             this.deferFocus();
21914         }
21915     },
21916  // private
21917     mozKeyPress : function(e){
21918         if(e.ctrlKey){
21919             var c = e.getCharCode(), cmd;
21920           
21921             if(c > 0){
21922                 c = String.fromCharCode(c).toLowerCase();
21923                 switch(c){
21924                     case 'b':
21925                         cmd = 'bold';
21926                         break;
21927                     case 'i':
21928                         cmd = 'italic';
21929                         break;
21930                     
21931                     case 'u':
21932                         cmd = 'underline';
21933                         break;
21934                     
21935                     case 'v':
21936                         this.cleanUpPaste.defer(100, this);
21937                         return;
21938                         
21939                 }
21940                 if(cmd){
21941                     this.win.focus();
21942                     this.execCmd(cmd);
21943                     this.deferFocus();
21944                     e.preventDefault();
21945                 }
21946                 
21947             }
21948         }
21949     },
21950
21951     // private
21952     fixKeys : function(){ // load time branching for fastest keydown performance
21953         if(Roo.isIE){
21954             return function(e){
21955                 var k = e.getKey(), r;
21956                 if(k == e.TAB){
21957                     e.stopEvent();
21958                     r = this.doc.selection.createRange();
21959                     if(r){
21960                         r.collapse(true);
21961                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21962                         this.deferFocus();
21963                     }
21964                     return;
21965                 }
21966                 
21967                 if(k == e.ENTER){
21968                     r = this.doc.selection.createRange();
21969                     if(r){
21970                         var target = r.parentElement();
21971                         if(!target || target.tagName.toLowerCase() != 'li'){
21972                             e.stopEvent();
21973                             r.pasteHTML('<br />');
21974                             r.collapse(false);
21975                             r.select();
21976                         }
21977                     }
21978                 }
21979                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21980                     this.cleanUpPaste.defer(100, this);
21981                     return;
21982                 }
21983                 
21984                 
21985             };
21986         }else if(Roo.isOpera){
21987             return function(e){
21988                 var k = e.getKey();
21989                 if(k == e.TAB){
21990                     e.stopEvent();
21991                     this.win.focus();
21992                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21993                     this.deferFocus();
21994                 }
21995                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21996                     this.cleanUpPaste.defer(100, this);
21997                     return;
21998                 }
21999                 
22000             };
22001         }else if(Roo.isSafari){
22002             return function(e){
22003                 var k = e.getKey();
22004                 
22005                 if(k == e.TAB){
22006                     e.stopEvent();
22007                     this.execCmd('InsertText','\t');
22008                     this.deferFocus();
22009                     return;
22010                 }
22011                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
22012                     this.cleanUpPaste.defer(100, this);
22013                     return;
22014                 }
22015                 
22016              };
22017         }
22018     }(),
22019     
22020     getAllAncestors: function()
22021     {
22022         var p = this.getSelectedNode();
22023         var a = [];
22024         if (!p) {
22025             a.push(p); // push blank onto stack..
22026             p = this.getParentElement();
22027         }
22028         
22029         
22030         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22031             a.push(p);
22032             p = p.parentNode;
22033         }
22034         a.push(this.doc.body);
22035         return a;
22036     },
22037     lastSel : false,
22038     lastSelNode : false,
22039     
22040     
22041     getSelection : function() 
22042     {
22043         this.assignDocWin();
22044         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22045     },
22046     
22047     getSelectedNode: function() 
22048     {
22049         // this may only work on Gecko!!!
22050         
22051         // should we cache this!!!!
22052         
22053         
22054         
22055          
22056         var range = this.createRange(this.getSelection()).cloneRange();
22057         
22058         if (Roo.isIE) {
22059             var parent = range.parentElement();
22060             while (true) {
22061                 var testRange = range.duplicate();
22062                 testRange.moveToElementText(parent);
22063                 if (testRange.inRange(range)) {
22064                     break;
22065                 }
22066                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22067                     break;
22068                 }
22069                 parent = parent.parentElement;
22070             }
22071             return parent;
22072         }
22073         
22074         // is ancestor a text element.
22075         var ac =  range.commonAncestorContainer;
22076         if (ac.nodeType == 3) {
22077             ac = ac.parentNode;
22078         }
22079         
22080         var ar = ac.childNodes;
22081          
22082         var nodes = [];
22083         var other_nodes = [];
22084         var has_other_nodes = false;
22085         for (var i=0;i<ar.length;i++) {
22086             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22087                 continue;
22088             }
22089             // fullly contained node.
22090             
22091             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22092                 nodes.push(ar[i]);
22093                 continue;
22094             }
22095             
22096             // probably selected..
22097             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22098                 other_nodes.push(ar[i]);
22099                 continue;
22100             }
22101             // outer..
22102             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22103                 continue;
22104             }
22105             
22106             
22107             has_other_nodes = true;
22108         }
22109         if (!nodes.length && other_nodes.length) {
22110             nodes= other_nodes;
22111         }
22112         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22113             return false;
22114         }
22115         
22116         return nodes[0];
22117     },
22118     createRange: function(sel)
22119     {
22120         // this has strange effects when using with 
22121         // top toolbar - not sure if it's a great idea.
22122         //this.editor.contentWindow.focus();
22123         if (typeof sel != "undefined") {
22124             try {
22125                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22126             } catch(e) {
22127                 return this.doc.createRange();
22128             }
22129         } else {
22130             return this.doc.createRange();
22131         }
22132     },
22133     getParentElement: function()
22134     {
22135         
22136         this.assignDocWin();
22137         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22138         
22139         var range = this.createRange(sel);
22140          
22141         try {
22142             var p = range.commonAncestorContainer;
22143             while (p.nodeType == 3) { // text node
22144                 p = p.parentNode;
22145             }
22146             return p;
22147         } catch (e) {
22148             return null;
22149         }
22150     
22151     },
22152     /***
22153      *
22154      * Range intersection.. the hard stuff...
22155      *  '-1' = before
22156      *  '0' = hits..
22157      *  '1' = after.
22158      *         [ -- selected range --- ]
22159      *   [fail]                        [fail]
22160      *
22161      *    basically..
22162      *      if end is before start or  hits it. fail.
22163      *      if start is after end or hits it fail.
22164      *
22165      *   if either hits (but other is outside. - then it's not 
22166      *   
22167      *    
22168      **/
22169     
22170     
22171     // @see http://www.thismuchiknow.co.uk/?p=64.
22172     rangeIntersectsNode : function(range, node)
22173     {
22174         var nodeRange = node.ownerDocument.createRange();
22175         try {
22176             nodeRange.selectNode(node);
22177         } catch (e) {
22178             nodeRange.selectNodeContents(node);
22179         }
22180     
22181         var rangeStartRange = range.cloneRange();
22182         rangeStartRange.collapse(true);
22183     
22184         var rangeEndRange = range.cloneRange();
22185         rangeEndRange.collapse(false);
22186     
22187         var nodeStartRange = nodeRange.cloneRange();
22188         nodeStartRange.collapse(true);
22189     
22190         var nodeEndRange = nodeRange.cloneRange();
22191         nodeEndRange.collapse(false);
22192     
22193         return rangeStartRange.compareBoundaryPoints(
22194                  Range.START_TO_START, nodeEndRange) == -1 &&
22195                rangeEndRange.compareBoundaryPoints(
22196                  Range.START_TO_START, nodeStartRange) == 1;
22197         
22198          
22199     },
22200     rangeCompareNode : function(range, node)
22201     {
22202         var nodeRange = node.ownerDocument.createRange();
22203         try {
22204             nodeRange.selectNode(node);
22205         } catch (e) {
22206             nodeRange.selectNodeContents(node);
22207         }
22208         
22209         
22210         range.collapse(true);
22211     
22212         nodeRange.collapse(true);
22213      
22214         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22215         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22216          
22217         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22218         
22219         var nodeIsBefore   =  ss == 1;
22220         var nodeIsAfter    = ee == -1;
22221         
22222         if (nodeIsBefore && nodeIsAfter) {
22223             return 0; // outer
22224         }
22225         if (!nodeIsBefore && nodeIsAfter) {
22226             return 1; //right trailed.
22227         }
22228         
22229         if (nodeIsBefore && !nodeIsAfter) {
22230             return 2;  // left trailed.
22231         }
22232         // fully contined.
22233         return 3;
22234     },
22235
22236     // private? - in a new class?
22237     cleanUpPaste :  function()
22238     {
22239         // cleans up the whole document..
22240         Roo.log('cleanuppaste');
22241         
22242         this.cleanUpChildren(this.doc.body);
22243         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22244         if (clean != this.doc.body.innerHTML) {
22245             this.doc.body.innerHTML = clean;
22246         }
22247         
22248     },
22249     
22250     cleanWordChars : function(input) {// change the chars to hex code
22251         var he = Roo.HtmlEditorCore;
22252         
22253         var output = input;
22254         Roo.each(he.swapCodes, function(sw) { 
22255             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22256             
22257             output = output.replace(swapper, sw[1]);
22258         });
22259         
22260         return output;
22261     },
22262     
22263     
22264     cleanUpChildren : function (n)
22265     {
22266         if (!n.childNodes.length) {
22267             return;
22268         }
22269         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22270            this.cleanUpChild(n.childNodes[i]);
22271         }
22272     },
22273     
22274     
22275         
22276     
22277     cleanUpChild : function (node)
22278     {
22279         var ed = this;
22280         //console.log(node);
22281         if (node.nodeName == "#text") {
22282             // clean up silly Windows -- stuff?
22283             return; 
22284         }
22285         if (node.nodeName == "#comment") {
22286             node.parentNode.removeChild(node);
22287             // clean up silly Windows -- stuff?
22288             return; 
22289         }
22290         var lcname = node.tagName.toLowerCase();
22291         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22292         // whitelist of tags..
22293         
22294         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22295             // remove node.
22296             node.parentNode.removeChild(node);
22297             return;
22298             
22299         }
22300         
22301         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22302         
22303         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22304         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22305         
22306         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22307         //    remove_keep_children = true;
22308         //}
22309         
22310         if (remove_keep_children) {
22311             this.cleanUpChildren(node);
22312             // inserts everything just before this node...
22313             while (node.childNodes.length) {
22314                 var cn = node.childNodes[0];
22315                 node.removeChild(cn);
22316                 node.parentNode.insertBefore(cn, node);
22317             }
22318             node.parentNode.removeChild(node);
22319             return;
22320         }
22321         
22322         if (!node.attributes || !node.attributes.length) {
22323             this.cleanUpChildren(node);
22324             return;
22325         }
22326         
22327         function cleanAttr(n,v)
22328         {
22329             
22330             if (v.match(/^\./) || v.match(/^\//)) {
22331                 return;
22332             }
22333             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
22334                 return;
22335             }
22336             if (v.match(/^#/)) {
22337                 return;
22338             }
22339 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22340             node.removeAttribute(n);
22341             
22342         }
22343         
22344         var cwhite = this.cwhite;
22345         var cblack = this.cblack;
22346             
22347         function cleanStyle(n,v)
22348         {
22349             if (v.match(/expression/)) { //XSS?? should we even bother..
22350                 node.removeAttribute(n);
22351                 return;
22352             }
22353             
22354             var parts = v.split(/;/);
22355             var clean = [];
22356             
22357             Roo.each(parts, function(p) {
22358                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22359                 if (!p.length) {
22360                     return true;
22361                 }
22362                 var l = p.split(':').shift().replace(/\s+/g,'');
22363                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22364                 
22365                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22366 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22367                     //node.removeAttribute(n);
22368                     return true;
22369                 }
22370                 //Roo.log()
22371                 // only allow 'c whitelisted system attributes'
22372                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22373 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22374                     //node.removeAttribute(n);
22375                     return true;
22376                 }
22377                 
22378                 
22379                  
22380                 
22381                 clean.push(p);
22382                 return true;
22383             });
22384             if (clean.length) { 
22385                 node.setAttribute(n, clean.join(';'));
22386             } else {
22387                 node.removeAttribute(n);
22388             }
22389             
22390         }
22391         
22392         
22393         for (var i = node.attributes.length-1; i > -1 ; i--) {
22394             var a = node.attributes[i];
22395             //console.log(a);
22396             
22397             if (a.name.toLowerCase().substr(0,2)=='on')  {
22398                 node.removeAttribute(a.name);
22399                 continue;
22400             }
22401             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22402                 node.removeAttribute(a.name);
22403                 continue;
22404             }
22405             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22406                 cleanAttr(a.name,a.value); // fixme..
22407                 continue;
22408             }
22409             if (a.name == 'style') {
22410                 cleanStyle(a.name,a.value);
22411                 continue;
22412             }
22413             /// clean up MS crap..
22414             // tecnically this should be a list of valid class'es..
22415             
22416             
22417             if (a.name == 'class') {
22418                 if (a.value.match(/^Mso/)) {
22419                     node.className = '';
22420                 }
22421                 
22422                 if (a.value.match(/^body$/)) {
22423                     node.className = '';
22424                 }
22425                 continue;
22426             }
22427             
22428             // style cleanup!?
22429             // class cleanup?
22430             
22431         }
22432         
22433         
22434         this.cleanUpChildren(node);
22435         
22436         
22437     },
22438     
22439     /**
22440      * Clean up MS wordisms...
22441      */
22442     cleanWord : function(node)
22443     {
22444         
22445         
22446         if (!node) {
22447             this.cleanWord(this.doc.body);
22448             return;
22449         }
22450         if (node.nodeName == "#text") {
22451             // clean up silly Windows -- stuff?
22452             return; 
22453         }
22454         if (node.nodeName == "#comment") {
22455             node.parentNode.removeChild(node);
22456             // clean up silly Windows -- stuff?
22457             return; 
22458         }
22459         
22460         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22461             node.parentNode.removeChild(node);
22462             return;
22463         }
22464         
22465         // remove - but keep children..
22466         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22467             while (node.childNodes.length) {
22468                 var cn = node.childNodes[0];
22469                 node.removeChild(cn);
22470                 node.parentNode.insertBefore(cn, node);
22471             }
22472             node.parentNode.removeChild(node);
22473             this.iterateChildren(node, this.cleanWord);
22474             return;
22475         }
22476         // clean styles
22477         if (node.className.length) {
22478             
22479             var cn = node.className.split(/\W+/);
22480             var cna = [];
22481             Roo.each(cn, function(cls) {
22482                 if (cls.match(/Mso[a-zA-Z]+/)) {
22483                     return;
22484                 }
22485                 cna.push(cls);
22486             });
22487             node.className = cna.length ? cna.join(' ') : '';
22488             if (!cna.length) {
22489                 node.removeAttribute("class");
22490             }
22491         }
22492         
22493         if (node.hasAttribute("lang")) {
22494             node.removeAttribute("lang");
22495         }
22496         
22497         if (node.hasAttribute("style")) {
22498             
22499             var styles = node.getAttribute("style").split(";");
22500             var nstyle = [];
22501             Roo.each(styles, function(s) {
22502                 if (!s.match(/:/)) {
22503                     return;
22504                 }
22505                 var kv = s.split(":");
22506                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22507                     return;
22508                 }
22509                 // what ever is left... we allow.
22510                 nstyle.push(s);
22511             });
22512             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22513             if (!nstyle.length) {
22514                 node.removeAttribute('style');
22515             }
22516         }
22517         this.iterateChildren(node, this.cleanWord);
22518         
22519         
22520         
22521     },
22522     /**
22523      * iterateChildren of a Node, calling fn each time, using this as the scole..
22524      * @param {DomNode} node node to iterate children of.
22525      * @param {Function} fn method of this class to call on each item.
22526      */
22527     iterateChildren : function(node, fn)
22528     {
22529         if (!node.childNodes.length) {
22530                 return;
22531         }
22532         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22533            fn.call(this, node.childNodes[i])
22534         }
22535     },
22536     
22537     
22538     /**
22539      * cleanTableWidths.
22540      *
22541      * Quite often pasting from word etc.. results in tables with column and widths.
22542      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22543      *
22544      */
22545     cleanTableWidths : function(node)
22546     {
22547          
22548          
22549         if (!node) {
22550             this.cleanTableWidths(this.doc.body);
22551             return;
22552         }
22553         
22554         // ignore list...
22555         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22556             return; 
22557         }
22558         Roo.log(node.tagName);
22559         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22560             this.iterateChildren(node, this.cleanTableWidths);
22561             return;
22562         }
22563         if (node.hasAttribute('width')) {
22564             node.removeAttribute('width');
22565         }
22566         
22567          
22568         if (node.hasAttribute("style")) {
22569             // pretty basic...
22570             
22571             var styles = node.getAttribute("style").split(";");
22572             var nstyle = [];
22573             Roo.each(styles, function(s) {
22574                 if (!s.match(/:/)) {
22575                     return;
22576                 }
22577                 var kv = s.split(":");
22578                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22579                     return;
22580                 }
22581                 // what ever is left... we allow.
22582                 nstyle.push(s);
22583             });
22584             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22585             if (!nstyle.length) {
22586                 node.removeAttribute('style');
22587             }
22588         }
22589         
22590         this.iterateChildren(node, this.cleanTableWidths);
22591         
22592         
22593     },
22594     
22595     
22596     
22597     
22598     domToHTML : function(currentElement, depth, nopadtext) {
22599         
22600         depth = depth || 0;
22601         nopadtext = nopadtext || false;
22602     
22603         if (!currentElement) {
22604             return this.domToHTML(this.doc.body);
22605         }
22606         
22607         //Roo.log(currentElement);
22608         var j;
22609         var allText = false;
22610         var nodeName = currentElement.nodeName;
22611         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22612         
22613         if  (nodeName == '#text') {
22614             
22615             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22616         }
22617         
22618         
22619         var ret = '';
22620         if (nodeName != 'BODY') {
22621              
22622             var i = 0;
22623             // Prints the node tagName, such as <A>, <IMG>, etc
22624             if (tagName) {
22625                 var attr = [];
22626                 for(i = 0; i < currentElement.attributes.length;i++) {
22627                     // quoting?
22628                     var aname = currentElement.attributes.item(i).name;
22629                     if (!currentElement.attributes.item(i).value.length) {
22630                         continue;
22631                     }
22632                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22633                 }
22634                 
22635                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22636             } 
22637             else {
22638                 
22639                 // eack
22640             }
22641         } else {
22642             tagName = false;
22643         }
22644         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22645             return ret;
22646         }
22647         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22648             nopadtext = true;
22649         }
22650         
22651         
22652         // Traverse the tree
22653         i = 0;
22654         var currentElementChild = currentElement.childNodes.item(i);
22655         var allText = true;
22656         var innerHTML  = '';
22657         lastnode = '';
22658         while (currentElementChild) {
22659             // Formatting code (indent the tree so it looks nice on the screen)
22660             var nopad = nopadtext;
22661             if (lastnode == 'SPAN') {
22662                 nopad  = true;
22663             }
22664             // text
22665             if  (currentElementChild.nodeName == '#text') {
22666                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22667                 toadd = nopadtext ? toadd : toadd.trim();
22668                 if (!nopad && toadd.length > 80) {
22669                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22670                 }
22671                 innerHTML  += toadd;
22672                 
22673                 i++;
22674                 currentElementChild = currentElement.childNodes.item(i);
22675                 lastNode = '';
22676                 continue;
22677             }
22678             allText = false;
22679             
22680             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22681                 
22682             // Recursively traverse the tree structure of the child node
22683             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22684             lastnode = currentElementChild.nodeName;
22685             i++;
22686             currentElementChild=currentElement.childNodes.item(i);
22687         }
22688         
22689         ret += innerHTML;
22690         
22691         if (!allText) {
22692                 // The remaining code is mostly for formatting the tree
22693             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22694         }
22695         
22696         
22697         if (tagName) {
22698             ret+= "</"+tagName+">";
22699         }
22700         return ret;
22701         
22702     },
22703         
22704     applyBlacklists : function()
22705     {
22706         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22707         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22708         
22709         this.white = [];
22710         this.black = [];
22711         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22712             if (b.indexOf(tag) > -1) {
22713                 return;
22714             }
22715             this.white.push(tag);
22716             
22717         }, this);
22718         
22719         Roo.each(w, function(tag) {
22720             if (b.indexOf(tag) > -1) {
22721                 return;
22722             }
22723             if (this.white.indexOf(tag) > -1) {
22724                 return;
22725             }
22726             this.white.push(tag);
22727             
22728         }, this);
22729         
22730         
22731         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22732             if (w.indexOf(tag) > -1) {
22733                 return;
22734             }
22735             this.black.push(tag);
22736             
22737         }, this);
22738         
22739         Roo.each(b, function(tag) {
22740             if (w.indexOf(tag) > -1) {
22741                 return;
22742             }
22743             if (this.black.indexOf(tag) > -1) {
22744                 return;
22745             }
22746             this.black.push(tag);
22747             
22748         }, this);
22749         
22750         
22751         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22752         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22753         
22754         this.cwhite = [];
22755         this.cblack = [];
22756         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22757             if (b.indexOf(tag) > -1) {
22758                 return;
22759             }
22760             this.cwhite.push(tag);
22761             
22762         }, this);
22763         
22764         Roo.each(w, function(tag) {
22765             if (b.indexOf(tag) > -1) {
22766                 return;
22767             }
22768             if (this.cwhite.indexOf(tag) > -1) {
22769                 return;
22770             }
22771             this.cwhite.push(tag);
22772             
22773         }, this);
22774         
22775         
22776         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22777             if (w.indexOf(tag) > -1) {
22778                 return;
22779             }
22780             this.cblack.push(tag);
22781             
22782         }, this);
22783         
22784         Roo.each(b, function(tag) {
22785             if (w.indexOf(tag) > -1) {
22786                 return;
22787             }
22788             if (this.cblack.indexOf(tag) > -1) {
22789                 return;
22790             }
22791             this.cblack.push(tag);
22792             
22793         }, this);
22794     },
22795     
22796     setStylesheets : function(stylesheets)
22797     {
22798         if(typeof(stylesheets) == 'string'){
22799             Roo.get(this.iframe.contentDocument.head).createChild({
22800                 tag : 'link',
22801                 rel : 'stylesheet',
22802                 type : 'text/css',
22803                 href : stylesheets
22804             });
22805             
22806             return;
22807         }
22808         var _this = this;
22809      
22810         Roo.each(stylesheets, function(s) {
22811             if(!s.length){
22812                 return;
22813             }
22814             
22815             Roo.get(_this.iframe.contentDocument.head).createChild({
22816                 tag : 'link',
22817                 rel : 'stylesheet',
22818                 type : 'text/css',
22819                 href : s
22820             });
22821         });
22822
22823         
22824     },
22825     
22826     removeStylesheets : function()
22827     {
22828         var _this = this;
22829         
22830         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22831             s.remove();
22832         });
22833     },
22834     
22835     setStyle : function(style)
22836     {
22837         Roo.get(this.iframe.contentDocument.head).createChild({
22838             tag : 'style',
22839             type : 'text/css',
22840             html : style
22841         });
22842
22843         return;
22844     }
22845     
22846     // hide stuff that is not compatible
22847     /**
22848      * @event blur
22849      * @hide
22850      */
22851     /**
22852      * @event change
22853      * @hide
22854      */
22855     /**
22856      * @event focus
22857      * @hide
22858      */
22859     /**
22860      * @event specialkey
22861      * @hide
22862      */
22863     /**
22864      * @cfg {String} fieldClass @hide
22865      */
22866     /**
22867      * @cfg {String} focusClass @hide
22868      */
22869     /**
22870      * @cfg {String} autoCreate @hide
22871      */
22872     /**
22873      * @cfg {String} inputType @hide
22874      */
22875     /**
22876      * @cfg {String} invalidClass @hide
22877      */
22878     /**
22879      * @cfg {String} invalidText @hide
22880      */
22881     /**
22882      * @cfg {String} msgFx @hide
22883      */
22884     /**
22885      * @cfg {String} validateOnBlur @hide
22886      */
22887 });
22888
22889 Roo.HtmlEditorCore.white = [
22890         'area', 'br', 'img', 'input', 'hr', 'wbr',
22891         
22892        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22893        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22894        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22895        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22896        'table',   'ul',         'xmp', 
22897        
22898        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22899       'thead',   'tr', 
22900      
22901       'dir', 'menu', 'ol', 'ul', 'dl',
22902        
22903       'embed',  'object'
22904 ];
22905
22906
22907 Roo.HtmlEditorCore.black = [
22908     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22909         'applet', // 
22910         'base',   'basefont', 'bgsound', 'blink',  'body', 
22911         'frame',  'frameset', 'head',    'html',   'ilayer', 
22912         'iframe', 'layer',  'link',     'meta',    'object',   
22913         'script', 'style' ,'title',  'xml' // clean later..
22914 ];
22915 Roo.HtmlEditorCore.clean = [
22916     'script', 'style', 'title', 'xml'
22917 ];
22918 Roo.HtmlEditorCore.remove = [
22919     'font'
22920 ];
22921 // attributes..
22922
22923 Roo.HtmlEditorCore.ablack = [
22924     'on'
22925 ];
22926     
22927 Roo.HtmlEditorCore.aclean = [ 
22928     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22929 ];
22930
22931 // protocols..
22932 Roo.HtmlEditorCore.pwhite= [
22933         'http',  'https',  'mailto'
22934 ];
22935
22936 // white listed style attributes.
22937 Roo.HtmlEditorCore.cwhite= [
22938       //  'text-align', /// default is to allow most things..
22939       
22940          
22941 //        'font-size'//??
22942 ];
22943
22944 // black listed style attributes.
22945 Roo.HtmlEditorCore.cblack= [
22946       //  'font-size' -- this can be set by the project 
22947 ];
22948
22949
22950 Roo.HtmlEditorCore.swapCodes   =[ 
22951     [    8211, "--" ], 
22952     [    8212, "--" ], 
22953     [    8216,  "'" ],  
22954     [    8217, "'" ],  
22955     [    8220, '"' ],  
22956     [    8221, '"' ],  
22957     [    8226, "*" ],  
22958     [    8230, "..." ]
22959 ]; 
22960
22961     /*
22962  * - LGPL
22963  *
22964  * HtmlEditor
22965  * 
22966  */
22967
22968 /**
22969  * @class Roo.bootstrap.HtmlEditor
22970  * @extends Roo.bootstrap.TextArea
22971  * Bootstrap HtmlEditor class
22972
22973  * @constructor
22974  * Create a new HtmlEditor
22975  * @param {Object} config The config object
22976  */
22977
22978 Roo.bootstrap.HtmlEditor = function(config){
22979     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
22980     if (!this.toolbars) {
22981         this.toolbars = [];
22982     }
22983     
22984     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22985     this.addEvents({
22986             /**
22987              * @event initialize
22988              * Fires when the editor is fully initialized (including the iframe)
22989              * @param {HtmlEditor} this
22990              */
22991             initialize: true,
22992             /**
22993              * @event activate
22994              * Fires when the editor is first receives the focus. Any insertion must wait
22995              * until after this event.
22996              * @param {HtmlEditor} this
22997              */
22998             activate: true,
22999              /**
23000              * @event beforesync
23001              * Fires before the textarea is updated with content from the editor iframe. Return false
23002              * to cancel the sync.
23003              * @param {HtmlEditor} this
23004              * @param {String} html
23005              */
23006             beforesync: true,
23007              /**
23008              * @event beforepush
23009              * Fires before the iframe editor is updated with content from the textarea. Return false
23010              * to cancel the push.
23011              * @param {HtmlEditor} this
23012              * @param {String} html
23013              */
23014             beforepush: true,
23015              /**
23016              * @event sync
23017              * Fires when the textarea is updated with content from the editor iframe.
23018              * @param {HtmlEditor} this
23019              * @param {String} html
23020              */
23021             sync: true,
23022              /**
23023              * @event push
23024              * Fires when the iframe editor is updated with content from the textarea.
23025              * @param {HtmlEditor} this
23026              * @param {String} html
23027              */
23028             push: true,
23029              /**
23030              * @event editmodechange
23031              * Fires when the editor switches edit modes
23032              * @param {HtmlEditor} this
23033              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23034              */
23035             editmodechange: true,
23036             /**
23037              * @event editorevent
23038              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23039              * @param {HtmlEditor} this
23040              */
23041             editorevent: true,
23042             /**
23043              * @event firstfocus
23044              * Fires when on first focus - needed by toolbars..
23045              * @param {HtmlEditor} this
23046              */
23047             firstfocus: true,
23048             /**
23049              * @event autosave
23050              * Auto save the htmlEditor value as a file into Events
23051              * @param {HtmlEditor} this
23052              */
23053             autosave: true,
23054             /**
23055              * @event savedpreview
23056              * preview the saved version of htmlEditor
23057              * @param {HtmlEditor} this
23058              */
23059             savedpreview: true
23060         });
23061 };
23062
23063
23064 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23065     
23066     
23067       /**
23068      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23069      */
23070     toolbars : false,
23071     
23072      /**
23073     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23074     */
23075     btns : [],
23076    
23077      /**
23078      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23079      *                        Roo.resizable.
23080      */
23081     resizable : false,
23082      /**
23083      * @cfg {Number} height (in pixels)
23084      */   
23085     height: 300,
23086    /**
23087      * @cfg {Number} width (in pixels)
23088      */   
23089     width: false,
23090     
23091     /**
23092      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23093      * 
23094      */
23095     stylesheets: false,
23096     
23097     // id of frame..
23098     frameId: false,
23099     
23100     // private properties
23101     validationEvent : false,
23102     deferHeight: true,
23103     initialized : false,
23104     activated : false,
23105     
23106     onFocus : Roo.emptyFn,
23107     iframePad:3,
23108     hideMode:'offsets',
23109     
23110     tbContainer : false,
23111     
23112     bodyCls : '',
23113     
23114     toolbarContainer :function() {
23115         return this.wrap.select('.x-html-editor-tb',true).first();
23116     },
23117
23118     /**
23119      * Protected method that will not generally be called directly. It
23120      * is called when the editor creates its toolbar. Override this method if you need to
23121      * add custom toolbar buttons.
23122      * @param {HtmlEditor} editor
23123      */
23124     createToolbar : function(){
23125         Roo.log('renewing');
23126         Roo.log("create toolbars");
23127         
23128         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23129         this.toolbars[0].render(this.toolbarContainer());
23130         
23131         return;
23132         
23133 //        if (!editor.toolbars || !editor.toolbars.length) {
23134 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23135 //        }
23136 //        
23137 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23138 //            editor.toolbars[i] = Roo.factory(
23139 //                    typeof(editor.toolbars[i]) == 'string' ?
23140 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23141 //                Roo.bootstrap.HtmlEditor);
23142 //            editor.toolbars[i].init(editor);
23143 //        }
23144     },
23145
23146      
23147     // private
23148     onRender : function(ct, position)
23149     {
23150        // Roo.log("Call onRender: " + this.xtype);
23151         var _t = this;
23152         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23153       
23154         this.wrap = this.inputEl().wrap({
23155             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23156         });
23157         
23158         this.editorcore.onRender(ct, position);
23159          
23160         if (this.resizable) {
23161             this.resizeEl = new Roo.Resizable(this.wrap, {
23162                 pinned : true,
23163                 wrap: true,
23164                 dynamic : true,
23165                 minHeight : this.height,
23166                 height: this.height,
23167                 handles : this.resizable,
23168                 width: this.width,
23169                 listeners : {
23170                     resize : function(r, w, h) {
23171                         _t.onResize(w,h); // -something
23172                     }
23173                 }
23174             });
23175             
23176         }
23177         this.createToolbar(this);
23178        
23179         
23180         if(!this.width && this.resizable){
23181             this.setSize(this.wrap.getSize());
23182         }
23183         if (this.resizeEl) {
23184             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23185             // should trigger onReize..
23186         }
23187         
23188     },
23189
23190     // private
23191     onResize : function(w, h)
23192     {
23193         Roo.log('resize: ' +w + ',' + h );
23194         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23195         var ew = false;
23196         var eh = false;
23197         
23198         if(this.inputEl() ){
23199             if(typeof w == 'number'){
23200                 var aw = w - this.wrap.getFrameWidth('lr');
23201                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23202                 ew = aw;
23203             }
23204             if(typeof h == 'number'){
23205                  var tbh = -11;  // fixme it needs to tool bar size!
23206                 for (var i =0; i < this.toolbars.length;i++) {
23207                     // fixme - ask toolbars for heights?
23208                     tbh += this.toolbars[i].el.getHeight();
23209                     //if (this.toolbars[i].footer) {
23210                     //    tbh += this.toolbars[i].footer.el.getHeight();
23211                     //}
23212                 }
23213               
23214                 
23215                 
23216                 
23217                 
23218                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23219                 ah -= 5; // knock a few pixes off for look..
23220                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23221                 var eh = ah;
23222             }
23223         }
23224         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23225         this.editorcore.onResize(ew,eh);
23226         
23227     },
23228
23229     /**
23230      * Toggles the editor between standard and source edit mode.
23231      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23232      */
23233     toggleSourceEdit : function(sourceEditMode)
23234     {
23235         this.editorcore.toggleSourceEdit(sourceEditMode);
23236         
23237         if(this.editorcore.sourceEditMode){
23238             Roo.log('editor - showing textarea');
23239             
23240 //            Roo.log('in');
23241 //            Roo.log(this.syncValue());
23242             this.syncValue();
23243             this.inputEl().removeClass(['hide', 'x-hidden']);
23244             this.inputEl().dom.removeAttribute('tabIndex');
23245             this.inputEl().focus();
23246         }else{
23247             Roo.log('editor - hiding textarea');
23248 //            Roo.log('out')
23249 //            Roo.log(this.pushValue()); 
23250             this.pushValue();
23251             
23252             this.inputEl().addClass(['hide', 'x-hidden']);
23253             this.inputEl().dom.setAttribute('tabIndex', -1);
23254             //this.deferFocus();
23255         }
23256          
23257         if(this.resizable){
23258             this.setSize(this.wrap.getSize());
23259         }
23260         
23261         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23262     },
23263  
23264     // private (for BoxComponent)
23265     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23266
23267     // private (for BoxComponent)
23268     getResizeEl : function(){
23269         return this.wrap;
23270     },
23271
23272     // private (for BoxComponent)
23273     getPositionEl : function(){
23274         return this.wrap;
23275     },
23276
23277     // private
23278     initEvents : function(){
23279         this.originalValue = this.getValue();
23280     },
23281
23282 //    /**
23283 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23284 //     * @method
23285 //     */
23286 //    markInvalid : Roo.emptyFn,
23287 //    /**
23288 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23289 //     * @method
23290 //     */
23291 //    clearInvalid : Roo.emptyFn,
23292
23293     setValue : function(v){
23294         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23295         this.editorcore.pushValue();
23296     },
23297
23298      
23299     // private
23300     deferFocus : function(){
23301         this.focus.defer(10, this);
23302     },
23303
23304     // doc'ed in Field
23305     focus : function(){
23306         this.editorcore.focus();
23307         
23308     },
23309       
23310
23311     // private
23312     onDestroy : function(){
23313         
23314         
23315         
23316         if(this.rendered){
23317             
23318             for (var i =0; i < this.toolbars.length;i++) {
23319                 // fixme - ask toolbars for heights?
23320                 this.toolbars[i].onDestroy();
23321             }
23322             
23323             this.wrap.dom.innerHTML = '';
23324             this.wrap.remove();
23325         }
23326     },
23327
23328     // private
23329     onFirstFocus : function(){
23330         //Roo.log("onFirstFocus");
23331         this.editorcore.onFirstFocus();
23332          for (var i =0; i < this.toolbars.length;i++) {
23333             this.toolbars[i].onFirstFocus();
23334         }
23335         
23336     },
23337     
23338     // private
23339     syncValue : function()
23340     {   
23341         this.editorcore.syncValue();
23342     },
23343     
23344     pushValue : function()
23345     {   
23346         this.editorcore.pushValue();
23347     }
23348      
23349     
23350     // hide stuff that is not compatible
23351     /**
23352      * @event blur
23353      * @hide
23354      */
23355     /**
23356      * @event change
23357      * @hide
23358      */
23359     /**
23360      * @event focus
23361      * @hide
23362      */
23363     /**
23364      * @event specialkey
23365      * @hide
23366      */
23367     /**
23368      * @cfg {String} fieldClass @hide
23369      */
23370     /**
23371      * @cfg {String} focusClass @hide
23372      */
23373     /**
23374      * @cfg {String} autoCreate @hide
23375      */
23376     /**
23377      * @cfg {String} inputType @hide
23378      */
23379     /**
23380      * @cfg {String} invalidClass @hide
23381      */
23382     /**
23383      * @cfg {String} invalidText @hide
23384      */
23385     /**
23386      * @cfg {String} msgFx @hide
23387      */
23388     /**
23389      * @cfg {String} validateOnBlur @hide
23390      */
23391 });
23392  
23393     
23394    
23395    
23396    
23397       
23398 Roo.namespace('Roo.bootstrap.htmleditor');
23399 /**
23400  * @class Roo.bootstrap.HtmlEditorToolbar1
23401  * Basic Toolbar
23402  * 
23403  * Usage:
23404  *
23405  new Roo.bootstrap.HtmlEditor({
23406     ....
23407     toolbars : [
23408         new Roo.bootstrap.HtmlEditorToolbar1({
23409             disable : { fonts: 1 , format: 1, ..., ... , ...],
23410             btns : [ .... ]
23411         })
23412     }
23413      
23414  * 
23415  * @cfg {Object} disable List of elements to disable..
23416  * @cfg {Array} btns List of additional buttons.
23417  * 
23418  * 
23419  * NEEDS Extra CSS? 
23420  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23421  */
23422  
23423 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23424 {
23425     
23426     Roo.apply(this, config);
23427     
23428     // default disabled, based on 'good practice'..
23429     this.disable = this.disable || {};
23430     Roo.applyIf(this.disable, {
23431         fontSize : true,
23432         colors : true,
23433         specialElements : true
23434     });
23435     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23436     
23437     this.editor = config.editor;
23438     this.editorcore = config.editor.editorcore;
23439     
23440     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23441     
23442     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23443     // dont call parent... till later.
23444 }
23445 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23446      
23447     bar : true,
23448     
23449     editor : false,
23450     editorcore : false,
23451     
23452     
23453     formats : [
23454         "p" ,  
23455         "h1","h2","h3","h4","h5","h6", 
23456         "pre", "code", 
23457         "abbr", "acronym", "address", "cite", "samp", "var",
23458         'div','span'
23459     ],
23460     
23461     onRender : function(ct, position)
23462     {
23463        // Roo.log("Call onRender: " + this.xtype);
23464         
23465        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23466        Roo.log(this.el);
23467        this.el.dom.style.marginBottom = '0';
23468        var _this = this;
23469        var editorcore = this.editorcore;
23470        var editor= this.editor;
23471        
23472        var children = [];
23473        var btn = function(id,cmd , toggle, handler, html){
23474        
23475             var  event = toggle ? 'toggle' : 'click';
23476        
23477             var a = {
23478                 size : 'sm',
23479                 xtype: 'Button',
23480                 xns: Roo.bootstrap,
23481                 glyphicon : id,
23482                 cmd : id || cmd,
23483                 enableToggle:toggle !== false,
23484                 html : html || '',
23485                 pressed : toggle ? false : null,
23486                 listeners : {}
23487             };
23488             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23489                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23490             };
23491             children.push(a);
23492             return a;
23493        }
23494        
23495     //    var cb_box = function...
23496         
23497         var style = {
23498                 xtype: 'Button',
23499                 size : 'sm',
23500                 xns: Roo.bootstrap,
23501                 glyphicon : 'font',
23502                 //html : 'submit'
23503                 menu : {
23504                     xtype: 'Menu',
23505                     xns: Roo.bootstrap,
23506                     items:  []
23507                 }
23508         };
23509         Roo.each(this.formats, function(f) {
23510             style.menu.items.push({
23511                 xtype :'MenuItem',
23512                 xns: Roo.bootstrap,
23513                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23514                 tagname : f,
23515                 listeners : {
23516                     click : function()
23517                     {
23518                         editorcore.insertTag(this.tagname);
23519                         editor.focus();
23520                     }
23521                 }
23522                 
23523             });
23524         });
23525         children.push(style);   
23526         
23527         btn('bold',false,true);
23528         btn('italic',false,true);
23529         btn('align-left', 'justifyleft',true);
23530         btn('align-center', 'justifycenter',true);
23531         btn('align-right' , 'justifyright',true);
23532         btn('link', false, false, function(btn) {
23533             //Roo.log("create link?");
23534             var url = prompt(this.createLinkText, this.defaultLinkValue);
23535             if(url && url != 'http:/'+'/'){
23536                 this.editorcore.relayCmd('createlink', url);
23537             }
23538         }),
23539         btn('list','insertunorderedlist',true);
23540         btn('pencil', false,true, function(btn){
23541                 Roo.log(this);
23542                 this.toggleSourceEdit(btn.pressed);
23543         });
23544         
23545         if (this.editor.btns.length > 0) {
23546             for (var i = 0; i<this.editor.btns.length; i++) {
23547                 children.push(this.editor.btns[i]);
23548             }
23549         }
23550         
23551         /*
23552         var cog = {
23553                 xtype: 'Button',
23554                 size : 'sm',
23555                 xns: Roo.bootstrap,
23556                 glyphicon : 'cog',
23557                 //html : 'submit'
23558                 menu : {
23559                     xtype: 'Menu',
23560                     xns: Roo.bootstrap,
23561                     items:  []
23562                 }
23563         };
23564         
23565         cog.menu.items.push({
23566             xtype :'MenuItem',
23567             xns: Roo.bootstrap,
23568             html : Clean styles,
23569             tagname : f,
23570             listeners : {
23571                 click : function()
23572                 {
23573                     editorcore.insertTag(this.tagname);
23574                     editor.focus();
23575                 }
23576             }
23577             
23578         });
23579        */
23580         
23581          
23582        this.xtype = 'NavSimplebar';
23583         
23584         for(var i=0;i< children.length;i++) {
23585             
23586             this.buttons.add(this.addxtypeChild(children[i]));
23587             
23588         }
23589         
23590         editor.on('editorevent', this.updateToolbar, this);
23591     },
23592     onBtnClick : function(id)
23593     {
23594        this.editorcore.relayCmd(id);
23595        this.editorcore.focus();
23596     },
23597     
23598     /**
23599      * Protected method that will not generally be called directly. It triggers
23600      * a toolbar update by reading the markup state of the current selection in the editor.
23601      */
23602     updateToolbar: function(){
23603
23604         if(!this.editorcore.activated){
23605             this.editor.onFirstFocus(); // is this neeed?
23606             return;
23607         }
23608
23609         var btns = this.buttons; 
23610         var doc = this.editorcore.doc;
23611         btns.get('bold').setActive(doc.queryCommandState('bold'));
23612         btns.get('italic').setActive(doc.queryCommandState('italic'));
23613         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23614         
23615         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23616         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23617         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23618         
23619         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23620         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23621          /*
23622         
23623         var ans = this.editorcore.getAllAncestors();
23624         if (this.formatCombo) {
23625             
23626             
23627             var store = this.formatCombo.store;
23628             this.formatCombo.setValue("");
23629             for (var i =0; i < ans.length;i++) {
23630                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23631                     // select it..
23632                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23633                     break;
23634                 }
23635             }
23636         }
23637         
23638         
23639         
23640         // hides menus... - so this cant be on a menu...
23641         Roo.bootstrap.MenuMgr.hideAll();
23642         */
23643         Roo.bootstrap.MenuMgr.hideAll();
23644         //this.editorsyncValue();
23645     },
23646     onFirstFocus: function() {
23647         this.buttons.each(function(item){
23648            item.enable();
23649         });
23650     },
23651     toggleSourceEdit : function(sourceEditMode){
23652         
23653           
23654         if(sourceEditMode){
23655             Roo.log("disabling buttons");
23656            this.buttons.each( function(item){
23657                 if(item.cmd != 'pencil'){
23658                     item.disable();
23659                 }
23660             });
23661           
23662         }else{
23663             Roo.log("enabling buttons");
23664             if(this.editorcore.initialized){
23665                 this.buttons.each( function(item){
23666                     item.enable();
23667                 });
23668             }
23669             
23670         }
23671         Roo.log("calling toggole on editor");
23672         // tell the editor that it's been pressed..
23673         this.editor.toggleSourceEdit(sourceEditMode);
23674        
23675     }
23676 });
23677
23678
23679
23680
23681
23682 /**
23683  * @class Roo.bootstrap.Table.AbstractSelectionModel
23684  * @extends Roo.util.Observable
23685  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23686  * implemented by descendant classes.  This class should not be directly instantiated.
23687  * @constructor
23688  */
23689 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23690     this.locked = false;
23691     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23692 };
23693
23694
23695 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23696     /** @ignore Called by the grid automatically. Do not call directly. */
23697     init : function(grid){
23698         this.grid = grid;
23699         this.initEvents();
23700     },
23701
23702     /**
23703      * Locks the selections.
23704      */
23705     lock : function(){
23706         this.locked = true;
23707     },
23708
23709     /**
23710      * Unlocks the selections.
23711      */
23712     unlock : function(){
23713         this.locked = false;
23714     },
23715
23716     /**
23717      * Returns true if the selections are locked.
23718      * @return {Boolean}
23719      */
23720     isLocked : function(){
23721         return this.locked;
23722     }
23723 });
23724 /**
23725  * @extends Roo.bootstrap.Table.AbstractSelectionModel
23726  * @class Roo.bootstrap.Table.RowSelectionModel
23727  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23728  * It supports multiple selections and keyboard selection/navigation. 
23729  * @constructor
23730  * @param {Object} config
23731  */
23732
23733 Roo.bootstrap.Table.RowSelectionModel = function(config){
23734     Roo.apply(this, config);
23735     this.selections = new Roo.util.MixedCollection(false, function(o){
23736         return o.id;
23737     });
23738
23739     this.last = false;
23740     this.lastActive = false;
23741
23742     this.addEvents({
23743         /**
23744              * @event selectionchange
23745              * Fires when the selection changes
23746              * @param {SelectionModel} this
23747              */
23748             "selectionchange" : true,
23749         /**
23750              * @event afterselectionchange
23751              * Fires after the selection changes (eg. by key press or clicking)
23752              * @param {SelectionModel} this
23753              */
23754             "afterselectionchange" : true,
23755         /**
23756              * @event beforerowselect
23757              * Fires when a row is selected being selected, return false to cancel.
23758              * @param {SelectionModel} this
23759              * @param {Number} rowIndex The selected index
23760              * @param {Boolean} keepExisting False if other selections will be cleared
23761              */
23762             "beforerowselect" : true,
23763         /**
23764              * @event rowselect
23765              * Fires when a row is selected.
23766              * @param {SelectionModel} this
23767              * @param {Number} rowIndex The selected index
23768              * @param {Roo.data.Record} r The record
23769              */
23770             "rowselect" : true,
23771         /**
23772              * @event rowdeselect
23773              * Fires when a row is deselected.
23774              * @param {SelectionModel} this
23775              * @param {Number} rowIndex The selected index
23776              */
23777         "rowdeselect" : true
23778     });
23779     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23780     this.locked = false;
23781  };
23782
23783 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
23784     /**
23785      * @cfg {Boolean} singleSelect
23786      * True to allow selection of only one row at a time (defaults to false)
23787      */
23788     singleSelect : false,
23789
23790     // private
23791     initEvents : function()
23792     {
23793
23794         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23795         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
23796         //}else{ // allow click to work like normal
23797          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
23798         //}
23799         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23800         this.grid.on("rowclick", this.handleMouseDown, this);
23801         
23802         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23803             "up" : function(e){
23804                 if(!e.shiftKey){
23805                     this.selectPrevious(e.shiftKey);
23806                 }else if(this.last !== false && this.lastActive !== false){
23807                     var last = this.last;
23808                     this.selectRange(this.last,  this.lastActive-1);
23809                     this.grid.getView().focusRow(this.lastActive);
23810                     if(last !== false){
23811                         this.last = last;
23812                     }
23813                 }else{
23814                     this.selectFirstRow();
23815                 }
23816                 this.fireEvent("afterselectionchange", this);
23817             },
23818             "down" : function(e){
23819                 if(!e.shiftKey){
23820                     this.selectNext(e.shiftKey);
23821                 }else if(this.last !== false && this.lastActive !== false){
23822                     var last = this.last;
23823                     this.selectRange(this.last,  this.lastActive+1);
23824                     this.grid.getView().focusRow(this.lastActive);
23825                     if(last !== false){
23826                         this.last = last;
23827                     }
23828                 }else{
23829                     this.selectFirstRow();
23830                 }
23831                 this.fireEvent("afterselectionchange", this);
23832             },
23833             scope: this
23834         });
23835         this.grid.store.on('load', function(){
23836             this.selections.clear();
23837         },this);
23838         /*
23839         var view = this.grid.view;
23840         view.on("refresh", this.onRefresh, this);
23841         view.on("rowupdated", this.onRowUpdated, this);
23842         view.on("rowremoved", this.onRemove, this);
23843         */
23844     },
23845
23846     // private
23847     onRefresh : function()
23848     {
23849         var ds = this.grid.store, i, v = this.grid.view;
23850         var s = this.selections;
23851         s.each(function(r){
23852             if((i = ds.indexOfId(r.id)) != -1){
23853                 v.onRowSelect(i);
23854             }else{
23855                 s.remove(r);
23856             }
23857         });
23858     },
23859
23860     // private
23861     onRemove : function(v, index, r){
23862         this.selections.remove(r);
23863     },
23864
23865     // private
23866     onRowUpdated : function(v, index, r){
23867         if(this.isSelected(r)){
23868             v.onRowSelect(index);
23869         }
23870     },
23871
23872     /**
23873      * Select records.
23874      * @param {Array} records The records to select
23875      * @param {Boolean} keepExisting (optional) True to keep existing selections
23876      */
23877     selectRecords : function(records, keepExisting)
23878     {
23879         if(!keepExisting){
23880             this.clearSelections();
23881         }
23882             var ds = this.grid.store;
23883         for(var i = 0, len = records.length; i < len; i++){
23884             this.selectRow(ds.indexOf(records[i]), true);
23885         }
23886     },
23887
23888     /**
23889      * Gets the number of selected rows.
23890      * @return {Number}
23891      */
23892     getCount : function(){
23893         return this.selections.length;
23894     },
23895
23896     /**
23897      * Selects the first row in the grid.
23898      */
23899     selectFirstRow : function(){
23900         this.selectRow(0);
23901     },
23902
23903     /**
23904      * Select the last row.
23905      * @param {Boolean} keepExisting (optional) True to keep existing selections
23906      */
23907     selectLastRow : function(keepExisting){
23908         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
23909         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
23910     },
23911
23912     /**
23913      * Selects the row immediately following the last selected row.
23914      * @param {Boolean} keepExisting (optional) True to keep existing selections
23915      */
23916     selectNext : function(keepExisting)
23917     {
23918             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
23919             this.selectRow(this.last+1, keepExisting);
23920             this.grid.getView().focusRow(this.last);
23921         }
23922     },
23923
23924     /**
23925      * Selects the row that precedes the last selected row.
23926      * @param {Boolean} keepExisting (optional) True to keep existing selections
23927      */
23928     selectPrevious : function(keepExisting){
23929         if(this.last){
23930             this.selectRow(this.last-1, keepExisting);
23931             this.grid.getView().focusRow(this.last);
23932         }
23933     },
23934
23935     /**
23936      * Returns the selected records
23937      * @return {Array} Array of selected records
23938      */
23939     getSelections : function(){
23940         return [].concat(this.selections.items);
23941     },
23942
23943     /**
23944      * Returns the first selected record.
23945      * @return {Record}
23946      */
23947     getSelected : function(){
23948         return this.selections.itemAt(0);
23949     },
23950
23951
23952     /**
23953      * Clears all selections.
23954      */
23955     clearSelections : function(fast)
23956     {
23957         if(this.locked) {
23958             return;
23959         }
23960         if(fast !== true){
23961                 var ds = this.grid.store;
23962             var s = this.selections;
23963             s.each(function(r){
23964                 this.deselectRow(ds.indexOfId(r.id));
23965             }, this);
23966             s.clear();
23967         }else{
23968             this.selections.clear();
23969         }
23970         this.last = false;
23971     },
23972
23973
23974     /**
23975      * Selects all rows.
23976      */
23977     selectAll : function(){
23978         if(this.locked) {
23979             return;
23980         }
23981         this.selections.clear();
23982         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
23983             this.selectRow(i, true);
23984         }
23985     },
23986
23987     /**
23988      * Returns True if there is a selection.
23989      * @return {Boolean}
23990      */
23991     hasSelection : function(){
23992         return this.selections.length > 0;
23993     },
23994
23995     /**
23996      * Returns True if the specified row is selected.
23997      * @param {Number/Record} record The record or index of the record to check
23998      * @return {Boolean}
23999      */
24000     isSelected : function(index){
24001             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
24002         return (r && this.selections.key(r.id) ? true : false);
24003     },
24004
24005     /**
24006      * Returns True if the specified record id is selected.
24007      * @param {String} id The id of record to check
24008      * @return {Boolean}
24009      */
24010     isIdSelected : function(id){
24011         return (this.selections.key(id) ? true : false);
24012     },
24013
24014
24015     // private
24016     handleMouseDBClick : function(e, t){
24017         
24018     },
24019     // private
24020     handleMouseDown : function(e, t)
24021     {
24022             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24023         if(this.isLocked() || rowIndex < 0 ){
24024             return;
24025         };
24026         if(e.shiftKey && this.last !== false){
24027             var last = this.last;
24028             this.selectRange(last, rowIndex, e.ctrlKey);
24029             this.last = last; // reset the last
24030             t.focus();
24031     
24032         }else{
24033             var isSelected = this.isSelected(rowIndex);
24034             //Roo.log("select row:" + rowIndex);
24035             if(isSelected){
24036                 this.deselectRow(rowIndex);
24037             } else {
24038                         this.selectRow(rowIndex, true);
24039             }
24040     
24041             /*
24042                 if(e.button !== 0 && isSelected){
24043                 alert('rowIndex 2: ' + rowIndex);
24044                     view.focusRow(rowIndex);
24045                 }else if(e.ctrlKey && isSelected){
24046                     this.deselectRow(rowIndex);
24047                 }else if(!isSelected){
24048                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24049                     view.focusRow(rowIndex);
24050                 }
24051             */
24052         }
24053         this.fireEvent("afterselectionchange", this);
24054     },
24055     // private
24056     handleDragableRowClick :  function(grid, rowIndex, e) 
24057     {
24058         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24059             this.selectRow(rowIndex, false);
24060             grid.view.focusRow(rowIndex);
24061              this.fireEvent("afterselectionchange", this);
24062         }
24063     },
24064     
24065     /**
24066      * Selects multiple rows.
24067      * @param {Array} rows Array of the indexes of the row to select
24068      * @param {Boolean} keepExisting (optional) True to keep existing selections
24069      */
24070     selectRows : function(rows, keepExisting){
24071         if(!keepExisting){
24072             this.clearSelections();
24073         }
24074         for(var i = 0, len = rows.length; i < len; i++){
24075             this.selectRow(rows[i], true);
24076         }
24077     },
24078
24079     /**
24080      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24081      * @param {Number} startRow The index of the first row in the range
24082      * @param {Number} endRow The index of the last row in the range
24083      * @param {Boolean} keepExisting (optional) True to retain existing selections
24084      */
24085     selectRange : function(startRow, endRow, keepExisting){
24086         if(this.locked) {
24087             return;
24088         }
24089         if(!keepExisting){
24090             this.clearSelections();
24091         }
24092         if(startRow <= endRow){
24093             for(var i = startRow; i <= endRow; i++){
24094                 this.selectRow(i, true);
24095             }
24096         }else{
24097             for(var i = startRow; i >= endRow; i--){
24098                 this.selectRow(i, true);
24099             }
24100         }
24101     },
24102
24103     /**
24104      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24105      * @param {Number} startRow The index of the first row in the range
24106      * @param {Number} endRow The index of the last row in the range
24107      */
24108     deselectRange : function(startRow, endRow, preventViewNotify){
24109         if(this.locked) {
24110             return;
24111         }
24112         for(var i = startRow; i <= endRow; i++){
24113             this.deselectRow(i, preventViewNotify);
24114         }
24115     },
24116
24117     /**
24118      * Selects a row.
24119      * @param {Number} row The index of the row to select
24120      * @param {Boolean} keepExisting (optional) True to keep existing selections
24121      */
24122     selectRow : function(index, keepExisting, preventViewNotify)
24123     {
24124             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24125             return;
24126         }
24127         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24128             if(!keepExisting || this.singleSelect){
24129                 this.clearSelections();
24130             }
24131             
24132             var r = this.grid.store.getAt(index);
24133             //console.log('selectRow - record id :' + r.id);
24134             
24135             this.selections.add(r);
24136             this.last = this.lastActive = index;
24137             if(!preventViewNotify){
24138                 var proxy = new Roo.Element(
24139                                 this.grid.getRowDom(index)
24140                 );
24141                 proxy.addClass('bg-info info');
24142             }
24143             this.fireEvent("rowselect", this, index, r);
24144             this.fireEvent("selectionchange", this);
24145         }
24146     },
24147
24148     /**
24149      * Deselects a row.
24150      * @param {Number} row The index of the row to deselect
24151      */
24152     deselectRow : function(index, preventViewNotify)
24153     {
24154         if(this.locked) {
24155             return;
24156         }
24157         if(this.last == index){
24158             this.last = false;
24159         }
24160         if(this.lastActive == index){
24161             this.lastActive = false;
24162         }
24163         
24164         var r = this.grid.store.getAt(index);
24165         if (!r) {
24166             return;
24167         }
24168         
24169         this.selections.remove(r);
24170         //.console.log('deselectRow - record id :' + r.id);
24171         if(!preventViewNotify){
24172         
24173             var proxy = new Roo.Element(
24174                 this.grid.getRowDom(index)
24175             );
24176             proxy.removeClass('bg-info info');
24177         }
24178         this.fireEvent("rowdeselect", this, index);
24179         this.fireEvent("selectionchange", this);
24180     },
24181
24182     // private
24183     restoreLast : function(){
24184         if(this._last){
24185             this.last = this._last;
24186         }
24187     },
24188
24189     // private
24190     acceptsNav : function(row, col, cm){
24191         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24192     },
24193
24194     // private
24195     onEditorKey : function(field, e){
24196         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24197         if(k == e.TAB){
24198             e.stopEvent();
24199             ed.completeEdit();
24200             if(e.shiftKey){
24201                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24202             }else{
24203                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24204             }
24205         }else if(k == e.ENTER && !e.ctrlKey){
24206             e.stopEvent();
24207             ed.completeEdit();
24208             if(e.shiftKey){
24209                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24210             }else{
24211                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24212             }
24213         }else if(k == e.ESC){
24214             ed.cancelEdit();
24215         }
24216         if(newCell){
24217             g.startEditing(newCell[0], newCell[1]);
24218         }
24219     }
24220 });
24221 /*
24222  * Based on:
24223  * Ext JS Library 1.1.1
24224  * Copyright(c) 2006-2007, Ext JS, LLC.
24225  *
24226  * Originally Released Under LGPL - original licence link has changed is not relivant.
24227  *
24228  * Fork - LGPL
24229  * <script type="text/javascript">
24230  */
24231  
24232 /**
24233  * @class Roo.bootstrap.PagingToolbar
24234  * @extends Roo.bootstrap.NavSimplebar
24235  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24236  * @constructor
24237  * Create a new PagingToolbar
24238  * @param {Object} config The config object
24239  * @param {Roo.data.Store} store
24240  */
24241 Roo.bootstrap.PagingToolbar = function(config)
24242 {
24243     // old args format still supported... - xtype is prefered..
24244         // created from xtype...
24245     
24246     this.ds = config.dataSource;
24247     
24248     if (config.store && !this.ds) {
24249         this.store= Roo.factory(config.store, Roo.data);
24250         this.ds = this.store;
24251         this.ds.xmodule = this.xmodule || false;
24252     }
24253     
24254     this.toolbarItems = [];
24255     if (config.items) {
24256         this.toolbarItems = config.items;
24257     }
24258     
24259     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24260     
24261     this.cursor = 0;
24262     
24263     if (this.ds) { 
24264         this.bind(this.ds);
24265     }
24266     
24267     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24268     
24269 };
24270
24271 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24272     /**
24273      * @cfg {Roo.data.Store} dataSource
24274      * The underlying data store providing the paged data
24275      */
24276     /**
24277      * @cfg {String/HTMLElement/Element} container
24278      * container The id or element that will contain the toolbar
24279      */
24280     /**
24281      * @cfg {Boolean} displayInfo
24282      * True to display the displayMsg (defaults to false)
24283      */
24284     /**
24285      * @cfg {Number} pageSize
24286      * The number of records to display per page (defaults to 20)
24287      */
24288     pageSize: 20,
24289     /**
24290      * @cfg {String} displayMsg
24291      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24292      */
24293     displayMsg : 'Displaying {0} - {1} of {2}',
24294     /**
24295      * @cfg {String} emptyMsg
24296      * The message to display when no records are found (defaults to "No data to display")
24297      */
24298     emptyMsg : 'No data to display',
24299     /**
24300      * Customizable piece of the default paging text (defaults to "Page")
24301      * @type String
24302      */
24303     beforePageText : "Page",
24304     /**
24305      * Customizable piece of the default paging text (defaults to "of %0")
24306      * @type String
24307      */
24308     afterPageText : "of {0}",
24309     /**
24310      * Customizable piece of the default paging text (defaults to "First Page")
24311      * @type String
24312      */
24313     firstText : "First Page",
24314     /**
24315      * Customizable piece of the default paging text (defaults to "Previous Page")
24316      * @type String
24317      */
24318     prevText : "Previous Page",
24319     /**
24320      * Customizable piece of the default paging text (defaults to "Next Page")
24321      * @type String
24322      */
24323     nextText : "Next Page",
24324     /**
24325      * Customizable piece of the default paging text (defaults to "Last Page")
24326      * @type String
24327      */
24328     lastText : "Last Page",
24329     /**
24330      * Customizable piece of the default paging text (defaults to "Refresh")
24331      * @type String
24332      */
24333     refreshText : "Refresh",
24334
24335     buttons : false,
24336     // private
24337     onRender : function(ct, position) 
24338     {
24339         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24340         this.navgroup.parentId = this.id;
24341         this.navgroup.onRender(this.el, null);
24342         // add the buttons to the navgroup
24343         
24344         if(this.displayInfo){
24345             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24346             this.displayEl = this.el.select('.x-paging-info', true).first();
24347 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24348 //            this.displayEl = navel.el.select('span',true).first();
24349         }
24350         
24351         var _this = this;
24352         
24353         if(this.buttons){
24354             Roo.each(_this.buttons, function(e){ // this might need to use render????
24355                Roo.factory(e).onRender(_this.el, null);
24356             });
24357         }
24358             
24359         Roo.each(_this.toolbarItems, function(e) {
24360             _this.navgroup.addItem(e);
24361         });
24362         
24363         
24364         this.first = this.navgroup.addItem({
24365             tooltip: this.firstText,
24366             cls: "prev",
24367             icon : 'fa fa-backward',
24368             disabled: true,
24369             preventDefault: true,
24370             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24371         });
24372         
24373         this.prev =  this.navgroup.addItem({
24374             tooltip: this.prevText,
24375             cls: "prev",
24376             icon : 'fa fa-step-backward',
24377             disabled: true,
24378             preventDefault: true,
24379             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24380         });
24381     //this.addSeparator();
24382         
24383         
24384         var field = this.navgroup.addItem( {
24385             tagtype : 'span',
24386             cls : 'x-paging-position',
24387             
24388             html : this.beforePageText  +
24389                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24390                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24391          } ); //?? escaped?
24392         
24393         this.field = field.el.select('input', true).first();
24394         this.field.on("keydown", this.onPagingKeydown, this);
24395         this.field.on("focus", function(){this.dom.select();});
24396     
24397     
24398         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24399         //this.field.setHeight(18);
24400         //this.addSeparator();
24401         this.next = this.navgroup.addItem({
24402             tooltip: this.nextText,
24403             cls: "next",
24404             html : ' <i class="fa fa-step-forward">',
24405             disabled: true,
24406             preventDefault: true,
24407             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24408         });
24409         this.last = this.navgroup.addItem({
24410             tooltip: this.lastText,
24411             icon : 'fa fa-forward',
24412             cls: "next",
24413             disabled: true,
24414             preventDefault: true,
24415             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24416         });
24417     //this.addSeparator();
24418         this.loading = this.navgroup.addItem({
24419             tooltip: this.refreshText,
24420             icon: 'fa fa-refresh',
24421             preventDefault: true,
24422             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24423         });
24424         
24425     },
24426
24427     // private
24428     updateInfo : function(){
24429         if(this.displayEl){
24430             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24431             var msg = count == 0 ?
24432                 this.emptyMsg :
24433                 String.format(
24434                     this.displayMsg,
24435                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24436                 );
24437             this.displayEl.update(msg);
24438         }
24439     },
24440
24441     // private
24442     onLoad : function(ds, r, o)
24443     {
24444         this.cursor = o.params ? o.params.start : 0;
24445         var d = this.getPageData(),
24446             ap = d.activePage,
24447             ps = d.pages;
24448         
24449         
24450         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24451         this.field.dom.value = ap;
24452         this.first.setDisabled(ap == 1);
24453         this.prev.setDisabled(ap == 1);
24454         this.next.setDisabled(ap == ps);
24455         this.last.setDisabled(ap == ps);
24456         this.loading.enable();
24457         this.updateInfo();
24458     },
24459
24460     // private
24461     getPageData : function(){
24462         var total = this.ds.getTotalCount();
24463         return {
24464             total : total,
24465             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24466             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24467         };
24468     },
24469
24470     // private
24471     onLoadError : function(){
24472         this.loading.enable();
24473     },
24474
24475     // private
24476     onPagingKeydown : function(e){
24477         var k = e.getKey();
24478         var d = this.getPageData();
24479         if(k == e.RETURN){
24480             var v = this.field.dom.value, pageNum;
24481             if(!v || isNaN(pageNum = parseInt(v, 10))){
24482                 this.field.dom.value = d.activePage;
24483                 return;
24484             }
24485             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24486             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24487             e.stopEvent();
24488         }
24489         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))
24490         {
24491           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24492           this.field.dom.value = pageNum;
24493           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24494           e.stopEvent();
24495         }
24496         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24497         {
24498           var v = this.field.dom.value, pageNum; 
24499           var increment = (e.shiftKey) ? 10 : 1;
24500           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24501                 increment *= -1;
24502           }
24503           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24504             this.field.dom.value = d.activePage;
24505             return;
24506           }
24507           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24508           {
24509             this.field.dom.value = parseInt(v, 10) + increment;
24510             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24511             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24512           }
24513           e.stopEvent();
24514         }
24515     },
24516
24517     // private
24518     beforeLoad : function(){
24519         if(this.loading){
24520             this.loading.disable();
24521         }
24522     },
24523
24524     // private
24525     onClick : function(which){
24526         
24527         var ds = this.ds;
24528         if (!ds) {
24529             return;
24530         }
24531         
24532         switch(which){
24533             case "first":
24534                 ds.load({params:{start: 0, limit: this.pageSize}});
24535             break;
24536             case "prev":
24537                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24538             break;
24539             case "next":
24540                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24541             break;
24542             case "last":
24543                 var total = ds.getTotalCount();
24544                 var extra = total % this.pageSize;
24545                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24546                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24547             break;
24548             case "refresh":
24549                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24550             break;
24551         }
24552     },
24553
24554     /**
24555      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24556      * @param {Roo.data.Store} store The data store to unbind
24557      */
24558     unbind : function(ds){
24559         ds.un("beforeload", this.beforeLoad, this);
24560         ds.un("load", this.onLoad, this);
24561         ds.un("loadexception", this.onLoadError, this);
24562         ds.un("remove", this.updateInfo, this);
24563         ds.un("add", this.updateInfo, this);
24564         this.ds = undefined;
24565     },
24566
24567     /**
24568      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24569      * @param {Roo.data.Store} store The data store to bind
24570      */
24571     bind : function(ds){
24572         ds.on("beforeload", this.beforeLoad, this);
24573         ds.on("load", this.onLoad, this);
24574         ds.on("loadexception", this.onLoadError, this);
24575         ds.on("remove", this.updateInfo, this);
24576         ds.on("add", this.updateInfo, this);
24577         this.ds = ds;
24578     }
24579 });/*
24580  * - LGPL
24581  *
24582  * element
24583  * 
24584  */
24585
24586 /**
24587  * @class Roo.bootstrap.MessageBar
24588  * @extends Roo.bootstrap.Component
24589  * Bootstrap MessageBar class
24590  * @cfg {String} html contents of the MessageBar
24591  * @cfg {String} weight (info | success | warning | danger) default info
24592  * @cfg {String} beforeClass insert the bar before the given class
24593  * @cfg {Boolean} closable (true | false) default false
24594  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24595  * 
24596  * @constructor
24597  * Create a new Element
24598  * @param {Object} config The config object
24599  */
24600
24601 Roo.bootstrap.MessageBar = function(config){
24602     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24603 };
24604
24605 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24606     
24607     html: '',
24608     weight: 'info',
24609     closable: false,
24610     fixed: false,
24611     beforeClass: 'bootstrap-sticky-wrap',
24612     
24613     getAutoCreate : function(){
24614         
24615         var cfg = {
24616             tag: 'div',
24617             cls: 'alert alert-dismissable alert-' + this.weight,
24618             cn: [
24619                 {
24620                     tag: 'span',
24621                     cls: 'message',
24622                     html: this.html || ''
24623                 }
24624             ]
24625         };
24626         
24627         if(this.fixed){
24628             cfg.cls += ' alert-messages-fixed';
24629         }
24630         
24631         if(this.closable){
24632             cfg.cn.push({
24633                 tag: 'button',
24634                 cls: 'close',
24635                 html: 'x'
24636             });
24637         }
24638         
24639         return cfg;
24640     },
24641     
24642     onRender : function(ct, position)
24643     {
24644         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24645         
24646         if(!this.el){
24647             var cfg = Roo.apply({},  this.getAutoCreate());
24648             cfg.id = Roo.id();
24649             
24650             if (this.cls) {
24651                 cfg.cls += ' ' + this.cls;
24652             }
24653             if (this.style) {
24654                 cfg.style = this.style;
24655             }
24656             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24657             
24658             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24659         }
24660         
24661         this.el.select('>button.close').on('click', this.hide, this);
24662         
24663     },
24664     
24665     show : function()
24666     {
24667         if (!this.rendered) {
24668             this.render();
24669         }
24670         
24671         this.el.show();
24672         
24673         this.fireEvent('show', this);
24674         
24675     },
24676     
24677     hide : function()
24678     {
24679         if (!this.rendered) {
24680             this.render();
24681         }
24682         
24683         this.el.hide();
24684         
24685         this.fireEvent('hide', this);
24686     },
24687     
24688     update : function()
24689     {
24690 //        var e = this.el.dom.firstChild;
24691 //        
24692 //        if(this.closable){
24693 //            e = e.nextSibling;
24694 //        }
24695 //        
24696 //        e.data = this.html || '';
24697
24698         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24699     }
24700    
24701 });
24702
24703  
24704
24705      /*
24706  * - LGPL
24707  *
24708  * Graph
24709  * 
24710  */
24711
24712
24713 /**
24714  * @class Roo.bootstrap.Graph
24715  * @extends Roo.bootstrap.Component
24716  * Bootstrap Graph class
24717 > Prameters
24718  -sm {number} sm 4
24719  -md {number} md 5
24720  @cfg {String} graphtype  bar | vbar | pie
24721  @cfg {number} g_x coodinator | centre x (pie)
24722  @cfg {number} g_y coodinator | centre y (pie)
24723  @cfg {number} g_r radius (pie)
24724  @cfg {number} g_height height of the chart (respected by all elements in the set)
24725  @cfg {number} g_width width of the chart (respected by all elements in the set)
24726  @cfg {Object} title The title of the chart
24727     
24728  -{Array}  values
24729  -opts (object) options for the chart 
24730      o {
24731      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24732      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24733      o vgutter (number)
24734      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.
24735      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24736      o to
24737      o stretch (boolean)
24738      o }
24739  -opts (object) options for the pie
24740      o{
24741      o cut
24742      o startAngle (number)
24743      o endAngle (number)
24744      } 
24745  *
24746  * @constructor
24747  * Create a new Input
24748  * @param {Object} config The config object
24749  */
24750
24751 Roo.bootstrap.Graph = function(config){
24752     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24753     
24754     this.addEvents({
24755         // img events
24756         /**
24757          * @event click
24758          * The img click event for the img.
24759          * @param {Roo.EventObject} e
24760          */
24761         "click" : true
24762     });
24763 };
24764
24765 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
24766     
24767     sm: 4,
24768     md: 5,
24769     graphtype: 'bar',
24770     g_height: 250,
24771     g_width: 400,
24772     g_x: 50,
24773     g_y: 50,
24774     g_r: 30,
24775     opts:{
24776         //g_colors: this.colors,
24777         g_type: 'soft',
24778         g_gutter: '20%'
24779
24780     },
24781     title : false,
24782
24783     getAutoCreate : function(){
24784         
24785         var cfg = {
24786             tag: 'div',
24787             html : null
24788         };
24789         
24790         
24791         return  cfg;
24792     },
24793
24794     onRender : function(ct,position){
24795         
24796         
24797         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24798         
24799         if (typeof(Raphael) == 'undefined') {
24800             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24801             return;
24802         }
24803         
24804         this.raphael = Raphael(this.el.dom);
24805         
24806                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24807                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24808                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24809                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24810                 /*
24811                 r.text(160, 10, "Single Series Chart").attr(txtattr);
24812                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24813                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24814                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
24815                 
24816                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
24817                 r.barchart(330, 10, 300, 220, data1);
24818                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
24819                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
24820                 */
24821                 
24822                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24823                 // r.barchart(30, 30, 560, 250,  xdata, {
24824                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
24825                 //     axis : "0 0 1 1",
24826                 //     axisxlabels :  xdata
24827                 //     //yvalues : cols,
24828                    
24829                 // });
24830 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24831 //        
24832 //        this.load(null,xdata,{
24833 //                axis : "0 0 1 1",
24834 //                axisxlabels :  xdata
24835 //                });
24836
24837     },
24838
24839     load : function(graphtype,xdata,opts)
24840     {
24841         this.raphael.clear();
24842         if(!graphtype) {
24843             graphtype = this.graphtype;
24844         }
24845         if(!opts){
24846             opts = this.opts;
24847         }
24848         var r = this.raphael,
24849             fin = function () {
24850                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
24851             },
24852             fout = function () {
24853                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
24854             },
24855             pfin = function() {
24856                 this.sector.stop();
24857                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
24858
24859                 if (this.label) {
24860                     this.label[0].stop();
24861                     this.label[0].attr({ r: 7.5 });
24862                     this.label[1].attr({ "font-weight": 800 });
24863                 }
24864             },
24865             pfout = function() {
24866                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
24867
24868                 if (this.label) {
24869                     this.label[0].animate({ r: 5 }, 500, "bounce");
24870                     this.label[1].attr({ "font-weight": 400 });
24871                 }
24872             };
24873
24874         switch(graphtype){
24875             case 'bar':
24876                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24877                 break;
24878             case 'hbar':
24879                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24880                 break;
24881             case 'pie':
24882 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
24883 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
24884 //            
24885                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
24886                 
24887                 break;
24888
24889         }
24890         
24891         if(this.title){
24892             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
24893         }
24894         
24895     },
24896     
24897     setTitle: function(o)
24898     {
24899         this.title = o;
24900     },
24901     
24902     initEvents: function() {
24903         
24904         if(!this.href){
24905             this.el.on('click', this.onClick, this);
24906         }
24907     },
24908     
24909     onClick : function(e)
24910     {
24911         Roo.log('img onclick');
24912         this.fireEvent('click', this, e);
24913     }
24914    
24915 });
24916
24917  
24918 /*
24919  * - LGPL
24920  *
24921  * numberBox
24922  * 
24923  */
24924 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24925
24926 /**
24927  * @class Roo.bootstrap.dash.NumberBox
24928  * @extends Roo.bootstrap.Component
24929  * Bootstrap NumberBox class
24930  * @cfg {String} headline Box headline
24931  * @cfg {String} content Box content
24932  * @cfg {String} icon Box icon
24933  * @cfg {String} footer Footer text
24934  * @cfg {String} fhref Footer href
24935  * 
24936  * @constructor
24937  * Create a new NumberBox
24938  * @param {Object} config The config object
24939  */
24940
24941
24942 Roo.bootstrap.dash.NumberBox = function(config){
24943     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
24944     
24945 };
24946
24947 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
24948     
24949     headline : '',
24950     content : '',
24951     icon : '',
24952     footer : '',
24953     fhref : '',
24954     ficon : '',
24955     
24956     getAutoCreate : function(){
24957         
24958         var cfg = {
24959             tag : 'div',
24960             cls : 'small-box ',
24961             cn : [
24962                 {
24963                     tag : 'div',
24964                     cls : 'inner',
24965                     cn :[
24966                         {
24967                             tag : 'h3',
24968                             cls : 'roo-headline',
24969                             html : this.headline
24970                         },
24971                         {
24972                             tag : 'p',
24973                             cls : 'roo-content',
24974                             html : this.content
24975                         }
24976                     ]
24977                 }
24978             ]
24979         };
24980         
24981         if(this.icon){
24982             cfg.cn.push({
24983                 tag : 'div',
24984                 cls : 'icon',
24985                 cn :[
24986                     {
24987                         tag : 'i',
24988                         cls : 'ion ' + this.icon
24989                     }
24990                 ]
24991             });
24992         }
24993         
24994         if(this.footer){
24995             var footer = {
24996                 tag : 'a',
24997                 cls : 'small-box-footer',
24998                 href : this.fhref || '#',
24999                 html : this.footer
25000             };
25001             
25002             cfg.cn.push(footer);
25003             
25004         }
25005         
25006         return  cfg;
25007     },
25008
25009     onRender : function(ct,position){
25010         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
25011
25012
25013        
25014                 
25015     },
25016
25017     setHeadline: function (value)
25018     {
25019         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25020     },
25021     
25022     setFooter: function (value, href)
25023     {
25024         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25025         
25026         if(href){
25027             this.el.select('a.small-box-footer',true).first().attr('href', href);
25028         }
25029         
25030     },
25031
25032     setContent: function (value)
25033     {
25034         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25035     },
25036
25037     initEvents: function() 
25038     {   
25039         
25040     }
25041     
25042 });
25043
25044  
25045 /*
25046  * - LGPL
25047  *
25048  * TabBox
25049  * 
25050  */
25051 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25052
25053 /**
25054  * @class Roo.bootstrap.dash.TabBox
25055  * @extends Roo.bootstrap.Component
25056  * Bootstrap TabBox class
25057  * @cfg {String} title Title of the TabBox
25058  * @cfg {String} icon Icon of the TabBox
25059  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25060  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25061  * 
25062  * @constructor
25063  * Create a new TabBox
25064  * @param {Object} config The config object
25065  */
25066
25067
25068 Roo.bootstrap.dash.TabBox = function(config){
25069     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25070     this.addEvents({
25071         // raw events
25072         /**
25073          * @event addpane
25074          * When a pane is added
25075          * @param {Roo.bootstrap.dash.TabPane} pane
25076          */
25077         "addpane" : true,
25078         /**
25079          * @event activatepane
25080          * When a pane is activated
25081          * @param {Roo.bootstrap.dash.TabPane} pane
25082          */
25083         "activatepane" : true
25084         
25085          
25086     });
25087     
25088     this.panes = [];
25089 };
25090
25091 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25092
25093     title : '',
25094     icon : false,
25095     showtabs : true,
25096     tabScrollable : false,
25097     
25098     getChildContainer : function()
25099     {
25100         return this.el.select('.tab-content', true).first();
25101     },
25102     
25103     getAutoCreate : function(){
25104         
25105         var header = {
25106             tag: 'li',
25107             cls: 'pull-left header',
25108             html: this.title,
25109             cn : []
25110         };
25111         
25112         if(this.icon){
25113             header.cn.push({
25114                 tag: 'i',
25115                 cls: 'fa ' + this.icon
25116             });
25117         }
25118         
25119         var h = {
25120             tag: 'ul',
25121             cls: 'nav nav-tabs pull-right',
25122             cn: [
25123                 header
25124             ]
25125         };
25126         
25127         if(this.tabScrollable){
25128             h = {
25129                 tag: 'div',
25130                 cls: 'tab-header',
25131                 cn: [
25132                     {
25133                         tag: 'ul',
25134                         cls: 'nav nav-tabs pull-right',
25135                         cn: [
25136                             header
25137                         ]
25138                     }
25139                 ]
25140             };
25141         }
25142         
25143         var cfg = {
25144             tag: 'div',
25145             cls: 'nav-tabs-custom',
25146             cn: [
25147                 h,
25148                 {
25149                     tag: 'div',
25150                     cls: 'tab-content no-padding',
25151                     cn: []
25152                 }
25153             ]
25154         };
25155
25156         return  cfg;
25157     },
25158     initEvents : function()
25159     {
25160         //Roo.log('add add pane handler');
25161         this.on('addpane', this.onAddPane, this);
25162     },
25163      /**
25164      * Updates the box title
25165      * @param {String} html to set the title to.
25166      */
25167     setTitle : function(value)
25168     {
25169         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25170     },
25171     onAddPane : function(pane)
25172     {
25173         this.panes.push(pane);
25174         //Roo.log('addpane');
25175         //Roo.log(pane);
25176         // tabs are rendere left to right..
25177         if(!this.showtabs){
25178             return;
25179         }
25180         
25181         var ctr = this.el.select('.nav-tabs', true).first();
25182          
25183          
25184         var existing = ctr.select('.nav-tab',true);
25185         var qty = existing.getCount();;
25186         
25187         
25188         var tab = ctr.createChild({
25189             tag : 'li',
25190             cls : 'nav-tab' + (qty ? '' : ' active'),
25191             cn : [
25192                 {
25193                     tag : 'a',
25194                     href:'#',
25195                     html : pane.title
25196                 }
25197             ]
25198         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25199         pane.tab = tab;
25200         
25201         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25202         if (!qty) {
25203             pane.el.addClass('active');
25204         }
25205         
25206                 
25207     },
25208     onTabClick : function(ev,un,ob,pane)
25209     {
25210         //Roo.log('tab - prev default');
25211         ev.preventDefault();
25212         
25213         
25214         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25215         pane.tab.addClass('active');
25216         //Roo.log(pane.title);
25217         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25218         // technically we should have a deactivate event.. but maybe add later.
25219         // and it should not de-activate the selected tab...
25220         this.fireEvent('activatepane', pane);
25221         pane.el.addClass('active');
25222         pane.fireEvent('activate');
25223         
25224         
25225     },
25226     
25227     getActivePane : function()
25228     {
25229         var r = false;
25230         Roo.each(this.panes, function(p) {
25231             if(p.el.hasClass('active')){
25232                 r = p;
25233                 return false;
25234             }
25235             
25236             return;
25237         });
25238         
25239         return r;
25240     }
25241     
25242     
25243 });
25244
25245  
25246 /*
25247  * - LGPL
25248  *
25249  * Tab pane
25250  * 
25251  */
25252 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25253 /**
25254  * @class Roo.bootstrap.TabPane
25255  * @extends Roo.bootstrap.Component
25256  * Bootstrap TabPane class
25257  * @cfg {Boolean} active (false | true) Default false
25258  * @cfg {String} title title of panel
25259
25260  * 
25261  * @constructor
25262  * Create a new TabPane
25263  * @param {Object} config The config object
25264  */
25265
25266 Roo.bootstrap.dash.TabPane = function(config){
25267     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25268     
25269     this.addEvents({
25270         // raw events
25271         /**
25272          * @event activate
25273          * When a pane is activated
25274          * @param {Roo.bootstrap.dash.TabPane} pane
25275          */
25276         "activate" : true
25277          
25278     });
25279 };
25280
25281 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25282     
25283     active : false,
25284     title : '',
25285     
25286     // the tabBox that this is attached to.
25287     tab : false,
25288      
25289     getAutoCreate : function() 
25290     {
25291         var cfg = {
25292             tag: 'div',
25293             cls: 'tab-pane'
25294         };
25295         
25296         if(this.active){
25297             cfg.cls += ' active';
25298         }
25299         
25300         return cfg;
25301     },
25302     initEvents  : function()
25303     {
25304         //Roo.log('trigger add pane handler');
25305         this.parent().fireEvent('addpane', this)
25306     },
25307     
25308      /**
25309      * Updates the tab title 
25310      * @param {String} html to set the title to.
25311      */
25312     setTitle: function(str)
25313     {
25314         if (!this.tab) {
25315             return;
25316         }
25317         this.title = str;
25318         this.tab.select('a', true).first().dom.innerHTML = str;
25319         
25320     }
25321     
25322     
25323     
25324 });
25325
25326  
25327
25328
25329  /*
25330  * - LGPL
25331  *
25332  * menu
25333  * 
25334  */
25335 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25336
25337 /**
25338  * @class Roo.bootstrap.menu.Menu
25339  * @extends Roo.bootstrap.Component
25340  * Bootstrap Menu class - container for Menu
25341  * @cfg {String} html Text of the menu
25342  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25343  * @cfg {String} icon Font awesome icon
25344  * @cfg {String} pos Menu align to (top | bottom) default bottom
25345  * 
25346  * 
25347  * @constructor
25348  * Create a new Menu
25349  * @param {Object} config The config object
25350  */
25351
25352
25353 Roo.bootstrap.menu.Menu = function(config){
25354     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25355     
25356     this.addEvents({
25357         /**
25358          * @event beforeshow
25359          * Fires before this menu is displayed
25360          * @param {Roo.bootstrap.menu.Menu} this
25361          */
25362         beforeshow : true,
25363         /**
25364          * @event beforehide
25365          * Fires before this menu is hidden
25366          * @param {Roo.bootstrap.menu.Menu} this
25367          */
25368         beforehide : true,
25369         /**
25370          * @event show
25371          * Fires after this menu is displayed
25372          * @param {Roo.bootstrap.menu.Menu} this
25373          */
25374         show : true,
25375         /**
25376          * @event hide
25377          * Fires after this menu is hidden
25378          * @param {Roo.bootstrap.menu.Menu} this
25379          */
25380         hide : true,
25381         /**
25382          * @event click
25383          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25384          * @param {Roo.bootstrap.menu.Menu} this
25385          * @param {Roo.EventObject} e
25386          */
25387         click : true
25388     });
25389     
25390 };
25391
25392 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25393     
25394     submenu : false,
25395     html : '',
25396     weight : 'default',
25397     icon : false,
25398     pos : 'bottom',
25399     
25400     
25401     getChildContainer : function() {
25402         if(this.isSubMenu){
25403             return this.el;
25404         }
25405         
25406         return this.el.select('ul.dropdown-menu', true).first();  
25407     },
25408     
25409     getAutoCreate : function()
25410     {
25411         var text = [
25412             {
25413                 tag : 'span',
25414                 cls : 'roo-menu-text',
25415                 html : this.html
25416             }
25417         ];
25418         
25419         if(this.icon){
25420             text.unshift({
25421                 tag : 'i',
25422                 cls : 'fa ' + this.icon
25423             })
25424         }
25425         
25426         
25427         var cfg = {
25428             tag : 'div',
25429             cls : 'btn-group',
25430             cn : [
25431                 {
25432                     tag : 'button',
25433                     cls : 'dropdown-button btn btn-' + this.weight,
25434                     cn : text
25435                 },
25436                 {
25437                     tag : 'button',
25438                     cls : 'dropdown-toggle btn btn-' + this.weight,
25439                     cn : [
25440                         {
25441                             tag : 'span',
25442                             cls : 'caret'
25443                         }
25444                     ]
25445                 },
25446                 {
25447                     tag : 'ul',
25448                     cls : 'dropdown-menu'
25449                 }
25450             ]
25451             
25452         };
25453         
25454         if(this.pos == 'top'){
25455             cfg.cls += ' dropup';
25456         }
25457         
25458         if(this.isSubMenu){
25459             cfg = {
25460                 tag : 'ul',
25461                 cls : 'dropdown-menu'
25462             }
25463         }
25464         
25465         return cfg;
25466     },
25467     
25468     onRender : function(ct, position)
25469     {
25470         this.isSubMenu = ct.hasClass('dropdown-submenu');
25471         
25472         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25473     },
25474     
25475     initEvents : function() 
25476     {
25477         if(this.isSubMenu){
25478             return;
25479         }
25480         
25481         this.hidden = true;
25482         
25483         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25484         this.triggerEl.on('click', this.onTriggerPress, this);
25485         
25486         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25487         this.buttonEl.on('click', this.onClick, this);
25488         
25489     },
25490     
25491     list : function()
25492     {
25493         if(this.isSubMenu){
25494             return this.el;
25495         }
25496         
25497         return this.el.select('ul.dropdown-menu', true).first();
25498     },
25499     
25500     onClick : function(e)
25501     {
25502         this.fireEvent("click", this, e);
25503     },
25504     
25505     onTriggerPress  : function(e)
25506     {   
25507         if (this.isVisible()) {
25508             this.hide();
25509         } else {
25510             this.show();
25511         }
25512     },
25513     
25514     isVisible : function(){
25515         return !this.hidden;
25516     },
25517     
25518     show : function()
25519     {
25520         this.fireEvent("beforeshow", this);
25521         
25522         this.hidden = false;
25523         this.el.addClass('open');
25524         
25525         Roo.get(document).on("mouseup", this.onMouseUp, this);
25526         
25527         this.fireEvent("show", this);
25528         
25529         
25530     },
25531     
25532     hide : function()
25533     {
25534         this.fireEvent("beforehide", this);
25535         
25536         this.hidden = true;
25537         this.el.removeClass('open');
25538         
25539         Roo.get(document).un("mouseup", this.onMouseUp);
25540         
25541         this.fireEvent("hide", this);
25542     },
25543     
25544     onMouseUp : function()
25545     {
25546         this.hide();
25547     }
25548     
25549 });
25550
25551  
25552  /*
25553  * - LGPL
25554  *
25555  * menu item
25556  * 
25557  */
25558 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25559
25560 /**
25561  * @class Roo.bootstrap.menu.Item
25562  * @extends Roo.bootstrap.Component
25563  * Bootstrap MenuItem class
25564  * @cfg {Boolean} submenu (true | false) default false
25565  * @cfg {String} html text of the item
25566  * @cfg {String} href the link
25567  * @cfg {Boolean} disable (true | false) default false
25568  * @cfg {Boolean} preventDefault (true | false) default true
25569  * @cfg {String} icon Font awesome icon
25570  * @cfg {String} pos Submenu align to (left | right) default right 
25571  * 
25572  * 
25573  * @constructor
25574  * Create a new Item
25575  * @param {Object} config The config object
25576  */
25577
25578
25579 Roo.bootstrap.menu.Item = function(config){
25580     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25581     this.addEvents({
25582         /**
25583          * @event mouseover
25584          * Fires when the mouse is hovering over this menu
25585          * @param {Roo.bootstrap.menu.Item} this
25586          * @param {Roo.EventObject} e
25587          */
25588         mouseover : true,
25589         /**
25590          * @event mouseout
25591          * Fires when the mouse exits this menu
25592          * @param {Roo.bootstrap.menu.Item} this
25593          * @param {Roo.EventObject} e
25594          */
25595         mouseout : true,
25596         // raw events
25597         /**
25598          * @event click
25599          * The raw click event for the entire grid.
25600          * @param {Roo.EventObject} e
25601          */
25602         click : true
25603     });
25604 };
25605
25606 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25607     
25608     submenu : false,
25609     href : '',
25610     html : '',
25611     preventDefault: true,
25612     disable : false,
25613     icon : false,
25614     pos : 'right',
25615     
25616     getAutoCreate : function()
25617     {
25618         var text = [
25619             {
25620                 tag : 'span',
25621                 cls : 'roo-menu-item-text',
25622                 html : this.html
25623             }
25624         ];
25625         
25626         if(this.icon){
25627             text.unshift({
25628                 tag : 'i',
25629                 cls : 'fa ' + this.icon
25630             })
25631         }
25632         
25633         var cfg = {
25634             tag : 'li',
25635             cn : [
25636                 {
25637                     tag : 'a',
25638                     href : this.href || '#',
25639                     cn : text
25640                 }
25641             ]
25642         };
25643         
25644         if(this.disable){
25645             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25646         }
25647         
25648         if(this.submenu){
25649             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25650             
25651             if(this.pos == 'left'){
25652                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25653             }
25654         }
25655         
25656         return cfg;
25657     },
25658     
25659     initEvents : function() 
25660     {
25661         this.el.on('mouseover', this.onMouseOver, this);
25662         this.el.on('mouseout', this.onMouseOut, this);
25663         
25664         this.el.select('a', true).first().on('click', this.onClick, this);
25665         
25666     },
25667     
25668     onClick : function(e)
25669     {
25670         if(this.preventDefault){
25671             e.preventDefault();
25672         }
25673         
25674         this.fireEvent("click", this, e);
25675     },
25676     
25677     onMouseOver : function(e)
25678     {
25679         if(this.submenu && this.pos == 'left'){
25680             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25681         }
25682         
25683         this.fireEvent("mouseover", this, e);
25684     },
25685     
25686     onMouseOut : function(e)
25687     {
25688         this.fireEvent("mouseout", this, e);
25689     }
25690 });
25691
25692  
25693
25694  /*
25695  * - LGPL
25696  *
25697  * menu separator
25698  * 
25699  */
25700 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25701
25702 /**
25703  * @class Roo.bootstrap.menu.Separator
25704  * @extends Roo.bootstrap.Component
25705  * Bootstrap Separator class
25706  * 
25707  * @constructor
25708  * Create a new Separator
25709  * @param {Object} config The config object
25710  */
25711
25712
25713 Roo.bootstrap.menu.Separator = function(config){
25714     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25715 };
25716
25717 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
25718     
25719     getAutoCreate : function(){
25720         var cfg = {
25721             tag : 'li',
25722             cls: 'divider'
25723         };
25724         
25725         return cfg;
25726     }
25727    
25728 });
25729
25730  
25731
25732  /*
25733  * - LGPL
25734  *
25735  * Tooltip
25736  * 
25737  */
25738
25739 /**
25740  * @class Roo.bootstrap.Tooltip
25741  * Bootstrap Tooltip class
25742  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25743  * to determine which dom element triggers the tooltip.
25744  * 
25745  * It needs to add support for additional attributes like tooltip-position
25746  * 
25747  * @constructor
25748  * Create a new Toolti
25749  * @param {Object} config The config object
25750  */
25751
25752 Roo.bootstrap.Tooltip = function(config){
25753     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25754     
25755     this.alignment = Roo.bootstrap.Tooltip.alignment;
25756     
25757     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25758         this.alignment = config.alignment;
25759     }
25760     
25761 };
25762
25763 Roo.apply(Roo.bootstrap.Tooltip, {
25764     /**
25765      * @function init initialize tooltip monitoring.
25766      * @static
25767      */
25768     currentEl : false,
25769     currentTip : false,
25770     currentRegion : false,
25771     
25772     //  init : delay?
25773     
25774     init : function()
25775     {
25776         Roo.get(document).on('mouseover', this.enter ,this);
25777         Roo.get(document).on('mouseout', this.leave, this);
25778          
25779         
25780         this.currentTip = new Roo.bootstrap.Tooltip();
25781     },
25782     
25783     enter : function(ev)
25784     {
25785         var dom = ev.getTarget();
25786         
25787         //Roo.log(['enter',dom]);
25788         var el = Roo.fly(dom);
25789         if (this.currentEl) {
25790             //Roo.log(dom);
25791             //Roo.log(this.currentEl);
25792             //Roo.log(this.currentEl.contains(dom));
25793             if (this.currentEl == el) {
25794                 return;
25795             }
25796             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25797                 return;
25798             }
25799
25800         }
25801         
25802         if (this.currentTip.el) {
25803             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25804         }    
25805         //Roo.log(ev);
25806         
25807         if(!el || el.dom == document){
25808             return;
25809         }
25810         
25811         var bindEl = el;
25812         
25813         // you can not look for children, as if el is the body.. then everythign is the child..
25814         if (!el.attr('tooltip')) { //
25815             if (!el.select("[tooltip]").elements.length) {
25816                 return;
25817             }
25818             // is the mouse over this child...?
25819             bindEl = el.select("[tooltip]").first();
25820             var xy = ev.getXY();
25821             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
25822                 //Roo.log("not in region.");
25823                 return;
25824             }
25825             //Roo.log("child element over..");
25826             
25827         }
25828         this.currentEl = bindEl;
25829         this.currentTip.bind(bindEl);
25830         this.currentRegion = Roo.lib.Region.getRegion(dom);
25831         this.currentTip.enter();
25832         
25833     },
25834     leave : function(ev)
25835     {
25836         var dom = ev.getTarget();
25837         //Roo.log(['leave',dom]);
25838         if (!this.currentEl) {
25839             return;
25840         }
25841         
25842         
25843         if (dom != this.currentEl.dom) {
25844             return;
25845         }
25846         var xy = ev.getXY();
25847         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
25848             return;
25849         }
25850         // only activate leave if mouse cursor is outside... bounding box..
25851         
25852         
25853         
25854         
25855         if (this.currentTip) {
25856             this.currentTip.leave();
25857         }
25858         //Roo.log('clear currentEl');
25859         this.currentEl = false;
25860         
25861         
25862     },
25863     alignment : {
25864         'left' : ['r-l', [-2,0], 'right'],
25865         'right' : ['l-r', [2,0], 'left'],
25866         'bottom' : ['t-b', [0,2], 'top'],
25867         'top' : [ 'b-t', [0,-2], 'bottom']
25868     }
25869     
25870 });
25871
25872
25873 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
25874     
25875     
25876     bindEl : false,
25877     
25878     delay : null, // can be { show : 300 , hide: 500}
25879     
25880     timeout : null,
25881     
25882     hoverState : null, //???
25883     
25884     placement : 'bottom', 
25885     
25886     alignment : false,
25887     
25888     getAutoCreate : function(){
25889     
25890         var cfg = {
25891            cls : 'tooltip',
25892            role : 'tooltip',
25893            cn : [
25894                 {
25895                     cls : 'tooltip-arrow'
25896                 },
25897                 {
25898                     cls : 'tooltip-inner'
25899                 }
25900            ]
25901         };
25902         
25903         return cfg;
25904     },
25905     bind : function(el)
25906     {
25907         this.bindEl = el;
25908     },
25909       
25910     
25911     enter : function () {
25912        
25913         if (this.timeout != null) {
25914             clearTimeout(this.timeout);
25915         }
25916         
25917         this.hoverState = 'in';
25918          //Roo.log("enter - show");
25919         if (!this.delay || !this.delay.show) {
25920             this.show();
25921             return;
25922         }
25923         var _t = this;
25924         this.timeout = setTimeout(function () {
25925             if (_t.hoverState == 'in') {
25926                 _t.show();
25927             }
25928         }, this.delay.show);
25929     },
25930     leave : function()
25931     {
25932         clearTimeout(this.timeout);
25933     
25934         this.hoverState = 'out';
25935          if (!this.delay || !this.delay.hide) {
25936             this.hide();
25937             return;
25938         }
25939        
25940         var _t = this;
25941         this.timeout = setTimeout(function () {
25942             //Roo.log("leave - timeout");
25943             
25944             if (_t.hoverState == 'out') {
25945                 _t.hide();
25946                 Roo.bootstrap.Tooltip.currentEl = false;
25947             }
25948         }, delay);
25949     },
25950     
25951     show : function (msg)
25952     {
25953         if (!this.el) {
25954             this.render(document.body);
25955         }
25956         // set content.
25957         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
25958         
25959         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
25960         
25961         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
25962         
25963         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
25964         
25965         var placement = typeof this.placement == 'function' ?
25966             this.placement.call(this, this.el, on_el) :
25967             this.placement;
25968             
25969         var autoToken = /\s?auto?\s?/i;
25970         var autoPlace = autoToken.test(placement);
25971         if (autoPlace) {
25972             placement = placement.replace(autoToken, '') || 'top';
25973         }
25974         
25975         //this.el.detach()
25976         //this.el.setXY([0,0]);
25977         this.el.show();
25978         //this.el.dom.style.display='block';
25979         
25980         //this.el.appendTo(on_el);
25981         
25982         var p = this.getPosition();
25983         var box = this.el.getBox();
25984         
25985         if (autoPlace) {
25986             // fixme..
25987         }
25988         
25989         var align = this.alignment[placement];
25990         
25991         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
25992         
25993         if(placement == 'top' || placement == 'bottom'){
25994             if(xy[0] < 0){
25995                 placement = 'right';
25996             }
25997             
25998             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
25999                 placement = 'left';
26000             }
26001             
26002             var scroll = Roo.select('body', true).first().getScroll();
26003             
26004             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
26005                 placement = 'top';
26006             }
26007             
26008         }
26009         
26010         this.el.alignTo(this.bindEl, align[0],align[1]);
26011         //var arrow = this.el.select('.arrow',true).first();
26012         //arrow.set(align[2], 
26013         
26014         this.el.addClass(placement);
26015         
26016         this.el.addClass('in fade');
26017         
26018         this.hoverState = null;
26019         
26020         if (this.el.hasClass('fade')) {
26021             // fade it?
26022         }
26023         
26024     },
26025     hide : function()
26026     {
26027          
26028         if (!this.el) {
26029             return;
26030         }
26031         //this.el.setXY([0,0]);
26032         this.el.removeClass('in');
26033         //this.el.hide();
26034         
26035     }
26036     
26037 });
26038  
26039
26040  /*
26041  * - LGPL
26042  *
26043  * Location Picker
26044  * 
26045  */
26046
26047 /**
26048  * @class Roo.bootstrap.LocationPicker
26049  * @extends Roo.bootstrap.Component
26050  * Bootstrap LocationPicker class
26051  * @cfg {Number} latitude Position when init default 0
26052  * @cfg {Number} longitude Position when init default 0
26053  * @cfg {Number} zoom default 15
26054  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26055  * @cfg {Boolean} mapTypeControl default false
26056  * @cfg {Boolean} disableDoubleClickZoom default false
26057  * @cfg {Boolean} scrollwheel default true
26058  * @cfg {Boolean} streetViewControl default false
26059  * @cfg {Number} radius default 0
26060  * @cfg {String} locationName
26061  * @cfg {Boolean} draggable default true
26062  * @cfg {Boolean} enableAutocomplete default false
26063  * @cfg {Boolean} enableReverseGeocode default true
26064  * @cfg {String} markerTitle
26065  * 
26066  * @constructor
26067  * Create a new LocationPicker
26068  * @param {Object} config The config object
26069  */
26070
26071
26072 Roo.bootstrap.LocationPicker = function(config){
26073     
26074     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26075     
26076     this.addEvents({
26077         /**
26078          * @event initial
26079          * Fires when the picker initialized.
26080          * @param {Roo.bootstrap.LocationPicker} this
26081          * @param {Google Location} location
26082          */
26083         initial : true,
26084         /**
26085          * @event positionchanged
26086          * Fires when the picker position changed.
26087          * @param {Roo.bootstrap.LocationPicker} this
26088          * @param {Google Location} location
26089          */
26090         positionchanged : true,
26091         /**
26092          * @event resize
26093          * Fires when the map resize.
26094          * @param {Roo.bootstrap.LocationPicker} this
26095          */
26096         resize : true,
26097         /**
26098          * @event show
26099          * Fires when the map show.
26100          * @param {Roo.bootstrap.LocationPicker} this
26101          */
26102         show : true,
26103         /**
26104          * @event hide
26105          * Fires when the map hide.
26106          * @param {Roo.bootstrap.LocationPicker} this
26107          */
26108         hide : true,
26109         /**
26110          * @event mapClick
26111          * Fires when click the map.
26112          * @param {Roo.bootstrap.LocationPicker} this
26113          * @param {Map event} e
26114          */
26115         mapClick : true,
26116         /**
26117          * @event mapRightClick
26118          * Fires when right click the map.
26119          * @param {Roo.bootstrap.LocationPicker} this
26120          * @param {Map event} e
26121          */
26122         mapRightClick : true,
26123         /**
26124          * @event markerClick
26125          * Fires when click the marker.
26126          * @param {Roo.bootstrap.LocationPicker} this
26127          * @param {Map event} e
26128          */
26129         markerClick : true,
26130         /**
26131          * @event markerRightClick
26132          * Fires when right click the marker.
26133          * @param {Roo.bootstrap.LocationPicker} this
26134          * @param {Map event} e
26135          */
26136         markerRightClick : true,
26137         /**
26138          * @event OverlayViewDraw
26139          * Fires when OverlayView Draw
26140          * @param {Roo.bootstrap.LocationPicker} this
26141          */
26142         OverlayViewDraw : true,
26143         /**
26144          * @event OverlayViewOnAdd
26145          * Fires when OverlayView Draw
26146          * @param {Roo.bootstrap.LocationPicker} this
26147          */
26148         OverlayViewOnAdd : true,
26149         /**
26150          * @event OverlayViewOnRemove
26151          * Fires when OverlayView Draw
26152          * @param {Roo.bootstrap.LocationPicker} this
26153          */
26154         OverlayViewOnRemove : true,
26155         /**
26156          * @event OverlayViewShow
26157          * Fires when OverlayView Draw
26158          * @param {Roo.bootstrap.LocationPicker} this
26159          * @param {Pixel} cpx
26160          */
26161         OverlayViewShow : true,
26162         /**
26163          * @event OverlayViewHide
26164          * Fires when OverlayView Draw
26165          * @param {Roo.bootstrap.LocationPicker} this
26166          */
26167         OverlayViewHide : true,
26168         /**
26169          * @event loadexception
26170          * Fires when load google lib failed.
26171          * @param {Roo.bootstrap.LocationPicker} this
26172          */
26173         loadexception : true
26174     });
26175         
26176 };
26177
26178 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26179     
26180     gMapContext: false,
26181     
26182     latitude: 0,
26183     longitude: 0,
26184     zoom: 15,
26185     mapTypeId: false,
26186     mapTypeControl: false,
26187     disableDoubleClickZoom: false,
26188     scrollwheel: true,
26189     streetViewControl: false,
26190     radius: 0,
26191     locationName: '',
26192     draggable: true,
26193     enableAutocomplete: false,
26194     enableReverseGeocode: true,
26195     markerTitle: '',
26196     
26197     getAutoCreate: function()
26198     {
26199
26200         var cfg = {
26201             tag: 'div',
26202             cls: 'roo-location-picker'
26203         };
26204         
26205         return cfg
26206     },
26207     
26208     initEvents: function(ct, position)
26209     {       
26210         if(!this.el.getWidth() || this.isApplied()){
26211             return;
26212         }
26213         
26214         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26215         
26216         this.initial();
26217     },
26218     
26219     initial: function()
26220     {
26221         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26222             this.fireEvent('loadexception', this);
26223             return;
26224         }
26225         
26226         if(!this.mapTypeId){
26227             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26228         }
26229         
26230         this.gMapContext = this.GMapContext();
26231         
26232         this.initOverlayView();
26233         
26234         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26235         
26236         var _this = this;
26237                 
26238         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26239             _this.setPosition(_this.gMapContext.marker.position);
26240         });
26241         
26242         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26243             _this.fireEvent('mapClick', this, event);
26244             
26245         });
26246
26247         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26248             _this.fireEvent('mapRightClick', this, event);
26249             
26250         });
26251         
26252         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26253             _this.fireEvent('markerClick', this, event);
26254             
26255         });
26256
26257         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26258             _this.fireEvent('markerRightClick', this, event);
26259             
26260         });
26261         
26262         this.setPosition(this.gMapContext.location);
26263         
26264         this.fireEvent('initial', this, this.gMapContext.location);
26265     },
26266     
26267     initOverlayView: function()
26268     {
26269         var _this = this;
26270         
26271         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26272             
26273             draw: function()
26274             {
26275                 _this.fireEvent('OverlayViewDraw', _this);
26276             },
26277             
26278             onAdd: function()
26279             {
26280                 _this.fireEvent('OverlayViewOnAdd', _this);
26281             },
26282             
26283             onRemove: function()
26284             {
26285                 _this.fireEvent('OverlayViewOnRemove', _this);
26286             },
26287             
26288             show: function(cpx)
26289             {
26290                 _this.fireEvent('OverlayViewShow', _this, cpx);
26291             },
26292             
26293             hide: function()
26294             {
26295                 _this.fireEvent('OverlayViewHide', _this);
26296             }
26297             
26298         });
26299     },
26300     
26301     fromLatLngToContainerPixel: function(event)
26302     {
26303         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26304     },
26305     
26306     isApplied: function() 
26307     {
26308         return this.getGmapContext() == false ? false : true;
26309     },
26310     
26311     getGmapContext: function() 
26312     {
26313         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26314     },
26315     
26316     GMapContext: function() 
26317     {
26318         var position = new google.maps.LatLng(this.latitude, this.longitude);
26319         
26320         var _map = new google.maps.Map(this.el.dom, {
26321             center: position,
26322             zoom: this.zoom,
26323             mapTypeId: this.mapTypeId,
26324             mapTypeControl: this.mapTypeControl,
26325             disableDoubleClickZoom: this.disableDoubleClickZoom,
26326             scrollwheel: this.scrollwheel,
26327             streetViewControl: this.streetViewControl,
26328             locationName: this.locationName,
26329             draggable: this.draggable,
26330             enableAutocomplete: this.enableAutocomplete,
26331             enableReverseGeocode: this.enableReverseGeocode
26332         });
26333         
26334         var _marker = new google.maps.Marker({
26335             position: position,
26336             map: _map,
26337             title: this.markerTitle,
26338             draggable: this.draggable
26339         });
26340         
26341         return {
26342             map: _map,
26343             marker: _marker,
26344             circle: null,
26345             location: position,
26346             radius: this.radius,
26347             locationName: this.locationName,
26348             addressComponents: {
26349                 formatted_address: null,
26350                 addressLine1: null,
26351                 addressLine2: null,
26352                 streetName: null,
26353                 streetNumber: null,
26354                 city: null,
26355                 district: null,
26356                 state: null,
26357                 stateOrProvince: null
26358             },
26359             settings: this,
26360             domContainer: this.el.dom,
26361             geodecoder: new google.maps.Geocoder()
26362         };
26363     },
26364     
26365     drawCircle: function(center, radius, options) 
26366     {
26367         if (this.gMapContext.circle != null) {
26368             this.gMapContext.circle.setMap(null);
26369         }
26370         if (radius > 0) {
26371             radius *= 1;
26372             options = Roo.apply({}, options, {
26373                 strokeColor: "#0000FF",
26374                 strokeOpacity: .35,
26375                 strokeWeight: 2,
26376                 fillColor: "#0000FF",
26377                 fillOpacity: .2
26378             });
26379             
26380             options.map = this.gMapContext.map;
26381             options.radius = radius;
26382             options.center = center;
26383             this.gMapContext.circle = new google.maps.Circle(options);
26384             return this.gMapContext.circle;
26385         }
26386         
26387         return null;
26388     },
26389     
26390     setPosition: function(location) 
26391     {
26392         this.gMapContext.location = location;
26393         this.gMapContext.marker.setPosition(location);
26394         this.gMapContext.map.panTo(location);
26395         this.drawCircle(location, this.gMapContext.radius, {});
26396         
26397         var _this = this;
26398         
26399         if (this.gMapContext.settings.enableReverseGeocode) {
26400             this.gMapContext.geodecoder.geocode({
26401                 latLng: this.gMapContext.location
26402             }, function(results, status) {
26403                 
26404                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26405                     _this.gMapContext.locationName = results[0].formatted_address;
26406                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26407                     
26408                     _this.fireEvent('positionchanged', this, location);
26409                 }
26410             });
26411             
26412             return;
26413         }
26414         
26415         this.fireEvent('positionchanged', this, location);
26416     },
26417     
26418     resize: function()
26419     {
26420         google.maps.event.trigger(this.gMapContext.map, "resize");
26421         
26422         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26423         
26424         this.fireEvent('resize', this);
26425     },
26426     
26427     setPositionByLatLng: function(latitude, longitude)
26428     {
26429         this.setPosition(new google.maps.LatLng(latitude, longitude));
26430     },
26431     
26432     getCurrentPosition: function() 
26433     {
26434         return {
26435             latitude: this.gMapContext.location.lat(),
26436             longitude: this.gMapContext.location.lng()
26437         };
26438     },
26439     
26440     getAddressName: function() 
26441     {
26442         return this.gMapContext.locationName;
26443     },
26444     
26445     getAddressComponents: function() 
26446     {
26447         return this.gMapContext.addressComponents;
26448     },
26449     
26450     address_component_from_google_geocode: function(address_components) 
26451     {
26452         var result = {};
26453         
26454         for (var i = 0; i < address_components.length; i++) {
26455             var component = address_components[i];
26456             if (component.types.indexOf("postal_code") >= 0) {
26457                 result.postalCode = component.short_name;
26458             } else if (component.types.indexOf("street_number") >= 0) {
26459                 result.streetNumber = component.short_name;
26460             } else if (component.types.indexOf("route") >= 0) {
26461                 result.streetName = component.short_name;
26462             } else if (component.types.indexOf("neighborhood") >= 0) {
26463                 result.city = component.short_name;
26464             } else if (component.types.indexOf("locality") >= 0) {
26465                 result.city = component.short_name;
26466             } else if (component.types.indexOf("sublocality") >= 0) {
26467                 result.district = component.short_name;
26468             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26469                 result.stateOrProvince = component.short_name;
26470             } else if (component.types.indexOf("country") >= 0) {
26471                 result.country = component.short_name;
26472             }
26473         }
26474         
26475         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26476         result.addressLine2 = "";
26477         return result;
26478     },
26479     
26480     setZoomLevel: function(zoom)
26481     {
26482         this.gMapContext.map.setZoom(zoom);
26483     },
26484     
26485     show: function()
26486     {
26487         if(!this.el){
26488             return;
26489         }
26490         
26491         this.el.show();
26492         
26493         this.resize();
26494         
26495         this.fireEvent('show', this);
26496     },
26497     
26498     hide: function()
26499     {
26500         if(!this.el){
26501             return;
26502         }
26503         
26504         this.el.hide();
26505         
26506         this.fireEvent('hide', this);
26507     }
26508     
26509 });
26510
26511 Roo.apply(Roo.bootstrap.LocationPicker, {
26512     
26513     OverlayView : function(map, options)
26514     {
26515         options = options || {};
26516         
26517         this.setMap(map);
26518     }
26519     
26520     
26521 });/*
26522  * - LGPL
26523  *
26524  * Alert
26525  * 
26526  */
26527
26528 /**
26529  * @class Roo.bootstrap.Alert
26530  * @extends Roo.bootstrap.Component
26531  * Bootstrap Alert class
26532  * @cfg {String} title The title of alert
26533  * @cfg {String} html The content of alert
26534  * @cfg {String} weight (  success | info | warning | danger )
26535  * @cfg {String} faicon font-awesomeicon
26536  * 
26537  * @constructor
26538  * Create a new alert
26539  * @param {Object} config The config object
26540  */
26541
26542
26543 Roo.bootstrap.Alert = function(config){
26544     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26545     
26546 };
26547
26548 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26549     
26550     title: '',
26551     html: '',
26552     weight: false,
26553     faicon: false,
26554     
26555     getAutoCreate : function()
26556     {
26557         
26558         var cfg = {
26559             tag : 'div',
26560             cls : 'alert',
26561             cn : [
26562                 {
26563                     tag : 'i',
26564                     cls : 'roo-alert-icon'
26565                     
26566                 },
26567                 {
26568                     tag : 'b',
26569                     cls : 'roo-alert-title',
26570                     html : this.title
26571                 },
26572                 {
26573                     tag : 'span',
26574                     cls : 'roo-alert-text',
26575                     html : this.html
26576                 }
26577             ]
26578         };
26579         
26580         if(this.faicon){
26581             cfg.cn[0].cls += ' fa ' + this.faicon;
26582         }
26583         
26584         if(this.weight){
26585             cfg.cls += ' alert-' + this.weight;
26586         }
26587         
26588         return cfg;
26589     },
26590     
26591     initEvents: function() 
26592     {
26593         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26594     },
26595     
26596     setTitle : function(str)
26597     {
26598         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26599     },
26600     
26601     setText : function(str)
26602     {
26603         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26604     },
26605     
26606     setWeight : function(weight)
26607     {
26608         if(this.weight){
26609             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26610         }
26611         
26612         this.weight = weight;
26613         
26614         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26615     },
26616     
26617     setIcon : function(icon)
26618     {
26619         if(this.faicon){
26620             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26621         }
26622         
26623         this.faicon = icon;
26624         
26625         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26626     },
26627     
26628     hide: function() 
26629     {
26630         this.el.hide();   
26631     },
26632     
26633     show: function() 
26634     {  
26635         this.el.show();   
26636     }
26637     
26638 });
26639
26640  
26641 /*
26642 * Licence: LGPL
26643 */
26644
26645 /**
26646  * @class Roo.bootstrap.UploadCropbox
26647  * @extends Roo.bootstrap.Component
26648  * Bootstrap UploadCropbox class
26649  * @cfg {String} emptyText show when image has been loaded
26650  * @cfg {String} rotateNotify show when image too small to rotate
26651  * @cfg {Number} errorTimeout default 3000
26652  * @cfg {Number} minWidth default 300
26653  * @cfg {Number} minHeight default 300
26654  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26655  * @cfg {Boolean} isDocument (true|false) default false
26656  * @cfg {String} url action url
26657  * @cfg {String} paramName default 'imageUpload'
26658  * @cfg {String} method default POST
26659  * @cfg {Boolean} loadMask (true|false) default true
26660  * @cfg {Boolean} loadingText default 'Loading...'
26661  * 
26662  * @constructor
26663  * Create a new UploadCropbox
26664  * @param {Object} config The config object
26665  */
26666
26667 Roo.bootstrap.UploadCropbox = function(config){
26668     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26669     
26670     this.addEvents({
26671         /**
26672          * @event beforeselectfile
26673          * Fire before select file
26674          * @param {Roo.bootstrap.UploadCropbox} this
26675          */
26676         "beforeselectfile" : true,
26677         /**
26678          * @event initial
26679          * Fire after initEvent
26680          * @param {Roo.bootstrap.UploadCropbox} this
26681          */
26682         "initial" : true,
26683         /**
26684          * @event crop
26685          * Fire after initEvent
26686          * @param {Roo.bootstrap.UploadCropbox} this
26687          * @param {String} data
26688          */
26689         "crop" : true,
26690         /**
26691          * @event prepare
26692          * Fire when preparing the file data
26693          * @param {Roo.bootstrap.UploadCropbox} this
26694          * @param {Object} file
26695          */
26696         "prepare" : true,
26697         /**
26698          * @event exception
26699          * Fire when get exception
26700          * @param {Roo.bootstrap.UploadCropbox} this
26701          * @param {XMLHttpRequest} xhr
26702          */
26703         "exception" : true,
26704         /**
26705          * @event beforeloadcanvas
26706          * Fire before load the canvas
26707          * @param {Roo.bootstrap.UploadCropbox} this
26708          * @param {String} src
26709          */
26710         "beforeloadcanvas" : true,
26711         /**
26712          * @event trash
26713          * Fire when trash image
26714          * @param {Roo.bootstrap.UploadCropbox} this
26715          */
26716         "trash" : true,
26717         /**
26718          * @event download
26719          * Fire when download the image
26720          * @param {Roo.bootstrap.UploadCropbox} this
26721          */
26722         "download" : true,
26723         /**
26724          * @event footerbuttonclick
26725          * Fire when footerbuttonclick
26726          * @param {Roo.bootstrap.UploadCropbox} this
26727          * @param {String} type
26728          */
26729         "footerbuttonclick" : true,
26730         /**
26731          * @event resize
26732          * Fire when resize
26733          * @param {Roo.bootstrap.UploadCropbox} this
26734          */
26735         "resize" : true,
26736         /**
26737          * @event rotate
26738          * Fire when rotate the image
26739          * @param {Roo.bootstrap.UploadCropbox} this
26740          * @param {String} pos
26741          */
26742         "rotate" : true,
26743         /**
26744          * @event inspect
26745          * Fire when inspect the file
26746          * @param {Roo.bootstrap.UploadCropbox} this
26747          * @param {Object} file
26748          */
26749         "inspect" : true,
26750         /**
26751          * @event upload
26752          * Fire when xhr upload the file
26753          * @param {Roo.bootstrap.UploadCropbox} this
26754          * @param {Object} data
26755          */
26756         "upload" : true,
26757         /**
26758          * @event arrange
26759          * Fire when arrange the file data
26760          * @param {Roo.bootstrap.UploadCropbox} this
26761          * @param {Object} formData
26762          */
26763         "arrange" : true
26764     });
26765     
26766     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26767 };
26768
26769 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
26770     
26771     emptyText : 'Click to upload image',
26772     rotateNotify : 'Image is too small to rotate',
26773     errorTimeout : 3000,
26774     scale : 0,
26775     baseScale : 1,
26776     rotate : 0,
26777     dragable : false,
26778     pinching : false,
26779     mouseX : 0,
26780     mouseY : 0,
26781     cropData : false,
26782     minWidth : 300,
26783     minHeight : 300,
26784     file : false,
26785     exif : {},
26786     baseRotate : 1,
26787     cropType : 'image/jpeg',
26788     buttons : false,
26789     canvasLoaded : false,
26790     isDocument : false,
26791     method : 'POST',
26792     paramName : 'imageUpload',
26793     loadMask : true,
26794     loadingText : 'Loading...',
26795     maskEl : false,
26796     
26797     getAutoCreate : function()
26798     {
26799         var cfg = {
26800             tag : 'div',
26801             cls : 'roo-upload-cropbox',
26802             cn : [
26803                 {
26804                     tag : 'input',
26805                     cls : 'roo-upload-cropbox-selector',
26806                     type : 'file'
26807                 },
26808                 {
26809                     tag : 'div',
26810                     cls : 'roo-upload-cropbox-body',
26811                     style : 'cursor:pointer',
26812                     cn : [
26813                         {
26814                             tag : 'div',
26815                             cls : 'roo-upload-cropbox-preview'
26816                         },
26817                         {
26818                             tag : 'div',
26819                             cls : 'roo-upload-cropbox-thumb'
26820                         },
26821                         {
26822                             tag : 'div',
26823                             cls : 'roo-upload-cropbox-empty-notify',
26824                             html : this.emptyText
26825                         },
26826                         {
26827                             tag : 'div',
26828                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
26829                             html : this.rotateNotify
26830                         }
26831                     ]
26832                 },
26833                 {
26834                     tag : 'div',
26835                     cls : 'roo-upload-cropbox-footer',
26836                     cn : {
26837                         tag : 'div',
26838                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
26839                         cn : []
26840                     }
26841                 }
26842             ]
26843         };
26844         
26845         return cfg;
26846     },
26847     
26848     onRender : function(ct, position)
26849     {
26850         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
26851         
26852         if (this.buttons.length) {
26853             
26854             Roo.each(this.buttons, function(bb) {
26855                 
26856                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
26857                 
26858                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
26859                 
26860             }, this);
26861         }
26862         
26863         if(this.loadMask){
26864             this.maskEl = this.el;
26865         }
26866     },
26867     
26868     initEvents : function()
26869     {
26870         this.urlAPI = (window.createObjectURL && window) || 
26871                                 (window.URL && URL.revokeObjectURL && URL) || 
26872                                 (window.webkitURL && webkitURL);
26873                         
26874         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
26875         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26876         
26877         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
26878         this.selectorEl.hide();
26879         
26880         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
26881         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26882         
26883         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
26884         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26885         this.thumbEl.hide();
26886         
26887         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
26888         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26889         
26890         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
26891         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26892         this.errorEl.hide();
26893         
26894         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
26895         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26896         this.footerEl.hide();
26897         
26898         this.setThumbBoxSize();
26899         
26900         this.bind();
26901         
26902         this.resize();
26903         
26904         this.fireEvent('initial', this);
26905     },
26906
26907     bind : function()
26908     {
26909         var _this = this;
26910         
26911         window.addEventListener("resize", function() { _this.resize(); } );
26912         
26913         this.bodyEl.on('click', this.beforeSelectFile, this);
26914         
26915         if(Roo.isTouch){
26916             this.bodyEl.on('touchstart', this.onTouchStart, this);
26917             this.bodyEl.on('touchmove', this.onTouchMove, this);
26918             this.bodyEl.on('touchend', this.onTouchEnd, this);
26919         }
26920         
26921         if(!Roo.isTouch){
26922             this.bodyEl.on('mousedown', this.onMouseDown, this);
26923             this.bodyEl.on('mousemove', this.onMouseMove, this);
26924             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
26925             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
26926             Roo.get(document).on('mouseup', this.onMouseUp, this);
26927         }
26928         
26929         this.selectorEl.on('change', this.onFileSelected, this);
26930     },
26931     
26932     reset : function()
26933     {    
26934         this.scale = 0;
26935         this.baseScale = 1;
26936         this.rotate = 0;
26937         this.baseRotate = 1;
26938         this.dragable = false;
26939         this.pinching = false;
26940         this.mouseX = 0;
26941         this.mouseY = 0;
26942         this.cropData = false;
26943         this.notifyEl.dom.innerHTML = this.emptyText;
26944         
26945         this.selectorEl.dom.value = '';
26946         
26947     },
26948     
26949     resize : function()
26950     {
26951         if(this.fireEvent('resize', this) != false){
26952             this.setThumbBoxPosition();
26953             this.setCanvasPosition();
26954         }
26955     },
26956     
26957     onFooterButtonClick : function(e, el, o, type)
26958     {
26959         switch (type) {
26960             case 'rotate-left' :
26961                 this.onRotateLeft(e);
26962                 break;
26963             case 'rotate-right' :
26964                 this.onRotateRight(e);
26965                 break;
26966             case 'picture' :
26967                 this.beforeSelectFile(e);
26968                 break;
26969             case 'trash' :
26970                 this.trash(e);
26971                 break;
26972             case 'crop' :
26973                 this.crop(e);
26974                 break;
26975             case 'download' :
26976                 this.download(e);
26977                 break;
26978             default :
26979                 break;
26980         }
26981         
26982         this.fireEvent('footerbuttonclick', this, type);
26983     },
26984     
26985     beforeSelectFile : function(e)
26986     {
26987         e.preventDefault();
26988         
26989         if(this.fireEvent('beforeselectfile', this) != false){
26990             this.selectorEl.dom.click();
26991         }
26992     },
26993     
26994     onFileSelected : function(e)
26995     {
26996         e.preventDefault();
26997         
26998         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26999             return;
27000         }
27001         
27002         var file = this.selectorEl.dom.files[0];
27003         
27004         if(this.fireEvent('inspect', this, file) != false){
27005             this.prepare(file);
27006         }
27007         
27008     },
27009     
27010     trash : function(e)
27011     {
27012         this.fireEvent('trash', this);
27013     },
27014     
27015     download : function(e)
27016     {
27017         this.fireEvent('download', this);
27018     },
27019     
27020     loadCanvas : function(src)
27021     {   
27022         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27023             
27024             this.reset();
27025             
27026             this.imageEl = document.createElement('img');
27027             
27028             var _this = this;
27029             
27030             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27031             
27032             this.imageEl.src = src;
27033         }
27034     },
27035     
27036     onLoadCanvas : function()
27037     {   
27038         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27039         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27040         
27041         this.bodyEl.un('click', this.beforeSelectFile, this);
27042         
27043         this.notifyEl.hide();
27044         this.thumbEl.show();
27045         this.footerEl.show();
27046         
27047         this.baseRotateLevel();
27048         
27049         if(this.isDocument){
27050             this.setThumbBoxSize();
27051         }
27052         
27053         this.setThumbBoxPosition();
27054         
27055         this.baseScaleLevel();
27056         
27057         this.draw();
27058         
27059         this.resize();
27060         
27061         this.canvasLoaded = true;
27062         
27063         if(this.loadMask){
27064             this.maskEl.unmask();
27065         }
27066         
27067     },
27068     
27069     setCanvasPosition : function()
27070     {   
27071         if(!this.canvasEl){
27072             return;
27073         }
27074         
27075         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27076         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27077         
27078         this.previewEl.setLeft(pw);
27079         this.previewEl.setTop(ph);
27080         
27081     },
27082     
27083     onMouseDown : function(e)
27084     {   
27085         e.stopEvent();
27086         
27087         this.dragable = true;
27088         this.pinching = false;
27089         
27090         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27091             this.dragable = false;
27092             return;
27093         }
27094         
27095         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27096         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27097         
27098     },
27099     
27100     onMouseMove : function(e)
27101     {   
27102         e.stopEvent();
27103         
27104         if(!this.canvasLoaded){
27105             return;
27106         }
27107         
27108         if (!this.dragable){
27109             return;
27110         }
27111         
27112         var minX = Math.ceil(this.thumbEl.getLeft(true));
27113         var minY = Math.ceil(this.thumbEl.getTop(true));
27114         
27115         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27116         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27117         
27118         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27119         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27120         
27121         x = x - this.mouseX;
27122         y = y - this.mouseY;
27123         
27124         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27125         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27126         
27127         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27128         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27129         
27130         this.previewEl.setLeft(bgX);
27131         this.previewEl.setTop(bgY);
27132         
27133         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27134         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27135     },
27136     
27137     onMouseUp : function(e)
27138     {   
27139         e.stopEvent();
27140         
27141         this.dragable = false;
27142     },
27143     
27144     onMouseWheel : function(e)
27145     {   
27146         e.stopEvent();
27147         
27148         this.startScale = this.scale;
27149         
27150         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27151         
27152         if(!this.zoomable()){
27153             this.scale = this.startScale;
27154             return;
27155         }
27156         
27157         this.draw();
27158         
27159         return;
27160     },
27161     
27162     zoomable : function()
27163     {
27164         var minScale = this.thumbEl.getWidth() / this.minWidth;
27165         
27166         if(this.minWidth < this.minHeight){
27167             minScale = this.thumbEl.getHeight() / this.minHeight;
27168         }
27169         
27170         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27171         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27172         
27173         if(
27174                 this.isDocument &&
27175                 (this.rotate == 0 || this.rotate == 180) && 
27176                 (
27177                     width > this.imageEl.OriginWidth || 
27178                     height > this.imageEl.OriginHeight ||
27179                     (width < this.minWidth && height < this.minHeight)
27180                 )
27181         ){
27182             return false;
27183         }
27184         
27185         if(
27186                 this.isDocument &&
27187                 (this.rotate == 90 || this.rotate == 270) && 
27188                 (
27189                     width > this.imageEl.OriginWidth || 
27190                     height > this.imageEl.OriginHeight ||
27191                     (width < this.minHeight && height < this.minWidth)
27192                 )
27193         ){
27194             return false;
27195         }
27196         
27197         if(
27198                 !this.isDocument &&
27199                 (this.rotate == 0 || this.rotate == 180) && 
27200                 (
27201                     width < this.minWidth || 
27202                     width > this.imageEl.OriginWidth || 
27203                     height < this.minHeight || 
27204                     height > this.imageEl.OriginHeight
27205                 )
27206         ){
27207             return false;
27208         }
27209         
27210         if(
27211                 !this.isDocument &&
27212                 (this.rotate == 90 || this.rotate == 270) && 
27213                 (
27214                     width < this.minHeight || 
27215                     width > this.imageEl.OriginWidth || 
27216                     height < this.minWidth || 
27217                     height > this.imageEl.OriginHeight
27218                 )
27219         ){
27220             return false;
27221         }
27222         
27223         return true;
27224         
27225     },
27226     
27227     onRotateLeft : function(e)
27228     {   
27229         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27230             
27231             var minScale = this.thumbEl.getWidth() / this.minWidth;
27232             
27233             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27234             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27235             
27236             this.startScale = this.scale;
27237             
27238             while (this.getScaleLevel() < minScale){
27239             
27240                 this.scale = this.scale + 1;
27241                 
27242                 if(!this.zoomable()){
27243                     break;
27244                 }
27245                 
27246                 if(
27247                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27248                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27249                 ){
27250                     continue;
27251                 }
27252                 
27253                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27254
27255                 this.draw();
27256                 
27257                 return;
27258             }
27259             
27260             this.scale = this.startScale;
27261             
27262             this.onRotateFail();
27263             
27264             return false;
27265         }
27266         
27267         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27268
27269         if(this.isDocument){
27270             this.setThumbBoxSize();
27271             this.setThumbBoxPosition();
27272             this.setCanvasPosition();
27273         }
27274         
27275         this.draw();
27276         
27277         this.fireEvent('rotate', this, 'left');
27278         
27279     },
27280     
27281     onRotateRight : function(e)
27282     {
27283         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27284             
27285             var minScale = this.thumbEl.getWidth() / this.minWidth;
27286         
27287             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27288             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27289             
27290             this.startScale = this.scale;
27291             
27292             while (this.getScaleLevel() < minScale){
27293             
27294                 this.scale = this.scale + 1;
27295                 
27296                 if(!this.zoomable()){
27297                     break;
27298                 }
27299                 
27300                 if(
27301                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27302                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27303                 ){
27304                     continue;
27305                 }
27306                 
27307                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27308
27309                 this.draw();
27310                 
27311                 return;
27312             }
27313             
27314             this.scale = this.startScale;
27315             
27316             this.onRotateFail();
27317             
27318             return false;
27319         }
27320         
27321         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27322
27323         if(this.isDocument){
27324             this.setThumbBoxSize();
27325             this.setThumbBoxPosition();
27326             this.setCanvasPosition();
27327         }
27328         
27329         this.draw();
27330         
27331         this.fireEvent('rotate', this, 'right');
27332     },
27333     
27334     onRotateFail : function()
27335     {
27336         this.errorEl.show(true);
27337         
27338         var _this = this;
27339         
27340         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27341     },
27342     
27343     draw : function()
27344     {
27345         this.previewEl.dom.innerHTML = '';
27346         
27347         var canvasEl = document.createElement("canvas");
27348         
27349         var contextEl = canvasEl.getContext("2d");
27350         
27351         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27352         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27353         var center = this.imageEl.OriginWidth / 2;
27354         
27355         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27356             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27357             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27358             center = this.imageEl.OriginHeight / 2;
27359         }
27360         
27361         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27362         
27363         contextEl.translate(center, center);
27364         contextEl.rotate(this.rotate * Math.PI / 180);
27365
27366         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27367         
27368         this.canvasEl = document.createElement("canvas");
27369         
27370         this.contextEl = this.canvasEl.getContext("2d");
27371         
27372         switch (this.rotate) {
27373             case 0 :
27374                 
27375                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27376                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27377                 
27378                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27379                 
27380                 break;
27381             case 90 : 
27382                 
27383                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27384                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27385                 
27386                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27387                     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);
27388                     break;
27389                 }
27390                 
27391                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27392                 
27393                 break;
27394             case 180 :
27395                 
27396                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27397                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27398                 
27399                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27400                     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);
27401                     break;
27402                 }
27403                 
27404                 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);
27405                 
27406                 break;
27407             case 270 :
27408                 
27409                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27410                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27411         
27412                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27413                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27414                     break;
27415                 }
27416                 
27417                 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);
27418                 
27419                 break;
27420             default : 
27421                 break;
27422         }
27423         
27424         this.previewEl.appendChild(this.canvasEl);
27425         
27426         this.setCanvasPosition();
27427     },
27428     
27429     crop : function()
27430     {
27431         if(!this.canvasLoaded){
27432             return;
27433         }
27434         
27435         var imageCanvas = document.createElement("canvas");
27436         
27437         var imageContext = imageCanvas.getContext("2d");
27438         
27439         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27440         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27441         
27442         var center = imageCanvas.width / 2;
27443         
27444         imageContext.translate(center, center);
27445         
27446         imageContext.rotate(this.rotate * Math.PI / 180);
27447         
27448         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27449         
27450         var canvas = document.createElement("canvas");
27451         
27452         var context = canvas.getContext("2d");
27453                 
27454         canvas.width = this.minWidth;
27455         canvas.height = this.minHeight;
27456
27457         switch (this.rotate) {
27458             case 0 :
27459                 
27460                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27461                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27462                 
27463                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27464                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27465                 
27466                 var targetWidth = this.minWidth - 2 * x;
27467                 var targetHeight = this.minHeight - 2 * y;
27468                 
27469                 var scale = 1;
27470                 
27471                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27472                     scale = targetWidth / width;
27473                 }
27474                 
27475                 if(x > 0 && y == 0){
27476                     scale = targetHeight / height;
27477                 }
27478                 
27479                 if(x > 0 && y > 0){
27480                     scale = targetWidth / width;
27481                     
27482                     if(width < height){
27483                         scale = targetHeight / height;
27484                     }
27485                 }
27486                 
27487                 context.scale(scale, scale);
27488                 
27489                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27490                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27491
27492                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27493                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27494
27495                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27496                 
27497                 break;
27498             case 90 : 
27499                 
27500                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27501                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27502                 
27503                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27504                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27505                 
27506                 var targetWidth = this.minWidth - 2 * x;
27507                 var targetHeight = this.minHeight - 2 * y;
27508                 
27509                 var scale = 1;
27510                 
27511                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27512                     scale = targetWidth / width;
27513                 }
27514                 
27515                 if(x > 0 && y == 0){
27516                     scale = targetHeight / height;
27517                 }
27518                 
27519                 if(x > 0 && y > 0){
27520                     scale = targetWidth / width;
27521                     
27522                     if(width < height){
27523                         scale = targetHeight / height;
27524                     }
27525                 }
27526                 
27527                 context.scale(scale, scale);
27528                 
27529                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27530                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27531
27532                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27533                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27534                 
27535                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27536                 
27537                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27538                 
27539                 break;
27540             case 180 :
27541                 
27542                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27543                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27544                 
27545                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27546                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27547                 
27548                 var targetWidth = this.minWidth - 2 * x;
27549                 var targetHeight = this.minHeight - 2 * y;
27550                 
27551                 var scale = 1;
27552                 
27553                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27554                     scale = targetWidth / width;
27555                 }
27556                 
27557                 if(x > 0 && y == 0){
27558                     scale = targetHeight / height;
27559                 }
27560                 
27561                 if(x > 0 && y > 0){
27562                     scale = targetWidth / width;
27563                     
27564                     if(width < height){
27565                         scale = targetHeight / height;
27566                     }
27567                 }
27568                 
27569                 context.scale(scale, scale);
27570                 
27571                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27572                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27573
27574                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27575                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27576
27577                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27578                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27579                 
27580                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27581                 
27582                 break;
27583             case 270 :
27584                 
27585                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27586                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27587                 
27588                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27589                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27590                 
27591                 var targetWidth = this.minWidth - 2 * x;
27592                 var targetHeight = this.minHeight - 2 * y;
27593                 
27594                 var scale = 1;
27595                 
27596                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27597                     scale = targetWidth / width;
27598                 }
27599                 
27600                 if(x > 0 && y == 0){
27601                     scale = targetHeight / height;
27602                 }
27603                 
27604                 if(x > 0 && y > 0){
27605                     scale = targetWidth / width;
27606                     
27607                     if(width < height){
27608                         scale = targetHeight / height;
27609                     }
27610                 }
27611                 
27612                 context.scale(scale, scale);
27613                 
27614                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27615                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27616
27617                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27618                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27619                 
27620                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27621                 
27622                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27623                 
27624                 break;
27625             default : 
27626                 break;
27627         }
27628         
27629         this.cropData = canvas.toDataURL(this.cropType);
27630         
27631         if(this.fireEvent('crop', this, this.cropData) !== false){
27632             this.process(this.file, this.cropData);
27633         }
27634         
27635         return;
27636         
27637     },
27638     
27639     setThumbBoxSize : function()
27640     {
27641         var width, height;
27642         
27643         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27644             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27645             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27646             
27647             this.minWidth = width;
27648             this.minHeight = height;
27649             
27650             if(this.rotate == 90 || this.rotate == 270){
27651                 this.minWidth = height;
27652                 this.minHeight = width;
27653             }
27654         }
27655         
27656         height = 300;
27657         width = Math.ceil(this.minWidth * height / this.minHeight);
27658         
27659         if(this.minWidth > this.minHeight){
27660             width = 300;
27661             height = Math.ceil(this.minHeight * width / this.minWidth);
27662         }
27663         
27664         this.thumbEl.setStyle({
27665             width : width + 'px',
27666             height : height + 'px'
27667         });
27668
27669         return;
27670             
27671     },
27672     
27673     setThumbBoxPosition : function()
27674     {
27675         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27676         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27677         
27678         this.thumbEl.setLeft(x);
27679         this.thumbEl.setTop(y);
27680         
27681     },
27682     
27683     baseRotateLevel : function()
27684     {
27685         this.baseRotate = 1;
27686         
27687         if(
27688                 typeof(this.exif) != 'undefined' &&
27689                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27690                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27691         ){
27692             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27693         }
27694         
27695         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27696         
27697     },
27698     
27699     baseScaleLevel : function()
27700     {
27701         var width, height;
27702         
27703         if(this.isDocument){
27704             
27705             if(this.baseRotate == 6 || this.baseRotate == 8){
27706             
27707                 height = this.thumbEl.getHeight();
27708                 this.baseScale = height / this.imageEl.OriginWidth;
27709
27710                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27711                     width = this.thumbEl.getWidth();
27712                     this.baseScale = width / this.imageEl.OriginHeight;
27713                 }
27714
27715                 return;
27716             }
27717
27718             height = this.thumbEl.getHeight();
27719             this.baseScale = height / this.imageEl.OriginHeight;
27720
27721             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27722                 width = this.thumbEl.getWidth();
27723                 this.baseScale = width / this.imageEl.OriginWidth;
27724             }
27725
27726             return;
27727         }
27728         
27729         if(this.baseRotate == 6 || this.baseRotate == 8){
27730             
27731             width = this.thumbEl.getHeight();
27732             this.baseScale = width / this.imageEl.OriginHeight;
27733             
27734             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27735                 height = this.thumbEl.getWidth();
27736                 this.baseScale = height / this.imageEl.OriginHeight;
27737             }
27738             
27739             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27740                 height = this.thumbEl.getWidth();
27741                 this.baseScale = height / this.imageEl.OriginHeight;
27742                 
27743                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27744                     width = this.thumbEl.getHeight();
27745                     this.baseScale = width / this.imageEl.OriginWidth;
27746                 }
27747             }
27748             
27749             return;
27750         }
27751         
27752         width = this.thumbEl.getWidth();
27753         this.baseScale = width / this.imageEl.OriginWidth;
27754         
27755         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27756             height = this.thumbEl.getHeight();
27757             this.baseScale = height / this.imageEl.OriginHeight;
27758         }
27759         
27760         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27761             
27762             height = this.thumbEl.getHeight();
27763             this.baseScale = height / this.imageEl.OriginHeight;
27764             
27765             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27766                 width = this.thumbEl.getWidth();
27767                 this.baseScale = width / this.imageEl.OriginWidth;
27768             }
27769             
27770         }
27771         
27772         return;
27773     },
27774     
27775     getScaleLevel : function()
27776     {
27777         return this.baseScale * Math.pow(1.1, this.scale);
27778     },
27779     
27780     onTouchStart : function(e)
27781     {
27782         if(!this.canvasLoaded){
27783             this.beforeSelectFile(e);
27784             return;
27785         }
27786         
27787         var touches = e.browserEvent.touches;
27788         
27789         if(!touches){
27790             return;
27791         }
27792         
27793         if(touches.length == 1){
27794             this.onMouseDown(e);
27795             return;
27796         }
27797         
27798         if(touches.length != 2){
27799             return;
27800         }
27801         
27802         var coords = [];
27803         
27804         for(var i = 0, finger; finger = touches[i]; i++){
27805             coords.push(finger.pageX, finger.pageY);
27806         }
27807         
27808         var x = Math.pow(coords[0] - coords[2], 2);
27809         var y = Math.pow(coords[1] - coords[3], 2);
27810         
27811         this.startDistance = Math.sqrt(x + y);
27812         
27813         this.startScale = this.scale;
27814         
27815         this.pinching = true;
27816         this.dragable = false;
27817         
27818     },
27819     
27820     onTouchMove : function(e)
27821     {
27822         if(!this.pinching && !this.dragable){
27823             return;
27824         }
27825         
27826         var touches = e.browserEvent.touches;
27827         
27828         if(!touches){
27829             return;
27830         }
27831         
27832         if(this.dragable){
27833             this.onMouseMove(e);
27834             return;
27835         }
27836         
27837         var coords = [];
27838         
27839         for(var i = 0, finger; finger = touches[i]; i++){
27840             coords.push(finger.pageX, finger.pageY);
27841         }
27842         
27843         var x = Math.pow(coords[0] - coords[2], 2);
27844         var y = Math.pow(coords[1] - coords[3], 2);
27845         
27846         this.endDistance = Math.sqrt(x + y);
27847         
27848         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
27849         
27850         if(!this.zoomable()){
27851             this.scale = this.startScale;
27852             return;
27853         }
27854         
27855         this.draw();
27856         
27857     },
27858     
27859     onTouchEnd : function(e)
27860     {
27861         this.pinching = false;
27862         this.dragable = false;
27863         
27864     },
27865     
27866     process : function(file, crop)
27867     {
27868         if(this.loadMask){
27869             this.maskEl.mask(this.loadingText);
27870         }
27871         
27872         this.xhr = new XMLHttpRequest();
27873         
27874         file.xhr = this.xhr;
27875
27876         this.xhr.open(this.method, this.url, true);
27877         
27878         var headers = {
27879             "Accept": "application/json",
27880             "Cache-Control": "no-cache",
27881             "X-Requested-With": "XMLHttpRequest"
27882         };
27883         
27884         for (var headerName in headers) {
27885             var headerValue = headers[headerName];
27886             if (headerValue) {
27887                 this.xhr.setRequestHeader(headerName, headerValue);
27888             }
27889         }
27890         
27891         var _this = this;
27892         
27893         this.xhr.onload = function()
27894         {
27895             _this.xhrOnLoad(_this.xhr);
27896         }
27897         
27898         this.xhr.onerror = function()
27899         {
27900             _this.xhrOnError(_this.xhr);
27901         }
27902         
27903         var formData = new FormData();
27904
27905         formData.append('returnHTML', 'NO');
27906         
27907         if(crop){
27908             formData.append('crop', crop);
27909         }
27910         
27911         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
27912             formData.append(this.paramName, file, file.name);
27913         }
27914         
27915         if(typeof(file.filename) != 'undefined'){
27916             formData.append('filename', file.filename);
27917         }
27918         
27919         if(typeof(file.mimetype) != 'undefined'){
27920             formData.append('mimetype', file.mimetype);
27921         }
27922         
27923         if(this.fireEvent('arrange', this, formData) != false){
27924             this.xhr.send(formData);
27925         };
27926     },
27927     
27928     xhrOnLoad : function(xhr)
27929     {
27930         if(this.loadMask){
27931             this.maskEl.unmask();
27932         }
27933         
27934         if (xhr.readyState !== 4) {
27935             this.fireEvent('exception', this, xhr);
27936             return;
27937         }
27938
27939         var response = Roo.decode(xhr.responseText);
27940         
27941         if(!response.success){
27942             this.fireEvent('exception', this, xhr);
27943             return;
27944         }
27945         
27946         var response = Roo.decode(xhr.responseText);
27947         
27948         this.fireEvent('upload', this, response);
27949         
27950     },
27951     
27952     xhrOnError : function()
27953     {
27954         if(this.loadMask){
27955             this.maskEl.unmask();
27956         }
27957         
27958         Roo.log('xhr on error');
27959         
27960         var response = Roo.decode(xhr.responseText);
27961           
27962         Roo.log(response);
27963         
27964     },
27965     
27966     prepare : function(file)
27967     {   
27968         if(this.loadMask){
27969             this.maskEl.mask(this.loadingText);
27970         }
27971         
27972         this.file = false;
27973         this.exif = {};
27974         
27975         if(typeof(file) === 'string'){
27976             this.loadCanvas(file);
27977             return;
27978         }
27979         
27980         if(!file || !this.urlAPI){
27981             return;
27982         }
27983         
27984         this.file = file;
27985         this.cropType = file.type;
27986         
27987         var _this = this;
27988         
27989         if(this.fireEvent('prepare', this, this.file) != false){
27990             
27991             var reader = new FileReader();
27992             
27993             reader.onload = function (e) {
27994                 if (e.target.error) {
27995                     Roo.log(e.target.error);
27996                     return;
27997                 }
27998                 
27999                 var buffer = e.target.result,
28000                     dataView = new DataView(buffer),
28001                     offset = 2,
28002                     maxOffset = dataView.byteLength - 4,
28003                     markerBytes,
28004                     markerLength;
28005                 
28006                 if (dataView.getUint16(0) === 0xffd8) {
28007                     while (offset < maxOffset) {
28008                         markerBytes = dataView.getUint16(offset);
28009                         
28010                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
28011                             markerLength = dataView.getUint16(offset + 2) + 2;
28012                             if (offset + markerLength > dataView.byteLength) {
28013                                 Roo.log('Invalid meta data: Invalid segment size.');
28014                                 break;
28015                             }
28016                             
28017                             if(markerBytes == 0xffe1){
28018                                 _this.parseExifData(
28019                                     dataView,
28020                                     offset,
28021                                     markerLength
28022                                 );
28023                             }
28024                             
28025                             offset += markerLength;
28026                             
28027                             continue;
28028                         }
28029                         
28030                         break;
28031                     }
28032                     
28033                 }
28034                 
28035                 var url = _this.urlAPI.createObjectURL(_this.file);
28036                 
28037                 _this.loadCanvas(url);
28038                 
28039                 return;
28040             }
28041             
28042             reader.readAsArrayBuffer(this.file);
28043             
28044         }
28045         
28046     },
28047     
28048     parseExifData : function(dataView, offset, length)
28049     {
28050         var tiffOffset = offset + 10,
28051             littleEndian,
28052             dirOffset;
28053     
28054         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28055             // No Exif data, might be XMP data instead
28056             return;
28057         }
28058         
28059         // Check for the ASCII code for "Exif" (0x45786966):
28060         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28061             // No Exif data, might be XMP data instead
28062             return;
28063         }
28064         if (tiffOffset + 8 > dataView.byteLength) {
28065             Roo.log('Invalid Exif data: Invalid segment size.');
28066             return;
28067         }
28068         // Check for the two null bytes:
28069         if (dataView.getUint16(offset + 8) !== 0x0000) {
28070             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28071             return;
28072         }
28073         // Check the byte alignment:
28074         switch (dataView.getUint16(tiffOffset)) {
28075         case 0x4949:
28076             littleEndian = true;
28077             break;
28078         case 0x4D4D:
28079             littleEndian = false;
28080             break;
28081         default:
28082             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28083             return;
28084         }
28085         // Check for the TIFF tag marker (0x002A):
28086         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28087             Roo.log('Invalid Exif data: Missing TIFF marker.');
28088             return;
28089         }
28090         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28091         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28092         
28093         this.parseExifTags(
28094             dataView,
28095             tiffOffset,
28096             tiffOffset + dirOffset,
28097             littleEndian
28098         );
28099     },
28100     
28101     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28102     {
28103         var tagsNumber,
28104             dirEndOffset,
28105             i;
28106         if (dirOffset + 6 > dataView.byteLength) {
28107             Roo.log('Invalid Exif data: Invalid directory offset.');
28108             return;
28109         }
28110         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28111         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28112         if (dirEndOffset + 4 > dataView.byteLength) {
28113             Roo.log('Invalid Exif data: Invalid directory size.');
28114             return;
28115         }
28116         for (i = 0; i < tagsNumber; i += 1) {
28117             this.parseExifTag(
28118                 dataView,
28119                 tiffOffset,
28120                 dirOffset + 2 + 12 * i, // tag offset
28121                 littleEndian
28122             );
28123         }
28124         // Return the offset to the next directory:
28125         return dataView.getUint32(dirEndOffset, littleEndian);
28126     },
28127     
28128     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28129     {
28130         var tag = dataView.getUint16(offset, littleEndian);
28131         
28132         this.exif[tag] = this.getExifValue(
28133             dataView,
28134             tiffOffset,
28135             offset,
28136             dataView.getUint16(offset + 2, littleEndian), // tag type
28137             dataView.getUint32(offset + 4, littleEndian), // tag length
28138             littleEndian
28139         );
28140     },
28141     
28142     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28143     {
28144         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28145             tagSize,
28146             dataOffset,
28147             values,
28148             i,
28149             str,
28150             c;
28151     
28152         if (!tagType) {
28153             Roo.log('Invalid Exif data: Invalid tag type.');
28154             return;
28155         }
28156         
28157         tagSize = tagType.size * length;
28158         // Determine if the value is contained in the dataOffset bytes,
28159         // or if the value at the dataOffset is a pointer to the actual data:
28160         dataOffset = tagSize > 4 ?
28161                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28162         if (dataOffset + tagSize > dataView.byteLength) {
28163             Roo.log('Invalid Exif data: Invalid data offset.');
28164             return;
28165         }
28166         if (length === 1) {
28167             return tagType.getValue(dataView, dataOffset, littleEndian);
28168         }
28169         values = [];
28170         for (i = 0; i < length; i += 1) {
28171             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28172         }
28173         
28174         if (tagType.ascii) {
28175             str = '';
28176             // Concatenate the chars:
28177             for (i = 0; i < values.length; i += 1) {
28178                 c = values[i];
28179                 // Ignore the terminating NULL byte(s):
28180                 if (c === '\u0000') {
28181                     break;
28182                 }
28183                 str += c;
28184             }
28185             return str;
28186         }
28187         return values;
28188     }
28189     
28190 });
28191
28192 Roo.apply(Roo.bootstrap.UploadCropbox, {
28193     tags : {
28194         'Orientation': 0x0112
28195     },
28196     
28197     Orientation: {
28198             1: 0, //'top-left',
28199 //            2: 'top-right',
28200             3: 180, //'bottom-right',
28201 //            4: 'bottom-left',
28202 //            5: 'left-top',
28203             6: 90, //'right-top',
28204 //            7: 'right-bottom',
28205             8: 270 //'left-bottom'
28206     },
28207     
28208     exifTagTypes : {
28209         // byte, 8-bit unsigned int:
28210         1: {
28211             getValue: function (dataView, dataOffset) {
28212                 return dataView.getUint8(dataOffset);
28213             },
28214             size: 1
28215         },
28216         // ascii, 8-bit byte:
28217         2: {
28218             getValue: function (dataView, dataOffset) {
28219                 return String.fromCharCode(dataView.getUint8(dataOffset));
28220             },
28221             size: 1,
28222             ascii: true
28223         },
28224         // short, 16 bit int:
28225         3: {
28226             getValue: function (dataView, dataOffset, littleEndian) {
28227                 return dataView.getUint16(dataOffset, littleEndian);
28228             },
28229             size: 2
28230         },
28231         // long, 32 bit int:
28232         4: {
28233             getValue: function (dataView, dataOffset, littleEndian) {
28234                 return dataView.getUint32(dataOffset, littleEndian);
28235             },
28236             size: 4
28237         },
28238         // rational = two long values, first is numerator, second is denominator:
28239         5: {
28240             getValue: function (dataView, dataOffset, littleEndian) {
28241                 return dataView.getUint32(dataOffset, littleEndian) /
28242                     dataView.getUint32(dataOffset + 4, littleEndian);
28243             },
28244             size: 8
28245         },
28246         // slong, 32 bit signed int:
28247         9: {
28248             getValue: function (dataView, dataOffset, littleEndian) {
28249                 return dataView.getInt32(dataOffset, littleEndian);
28250             },
28251             size: 4
28252         },
28253         // srational, two slongs, first is numerator, second is denominator:
28254         10: {
28255             getValue: function (dataView, dataOffset, littleEndian) {
28256                 return dataView.getInt32(dataOffset, littleEndian) /
28257                     dataView.getInt32(dataOffset + 4, littleEndian);
28258             },
28259             size: 8
28260         }
28261     },
28262     
28263     footer : {
28264         STANDARD : [
28265             {
28266                 tag : 'div',
28267                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28268                 action : 'rotate-left',
28269                 cn : [
28270                     {
28271                         tag : 'button',
28272                         cls : 'btn btn-default',
28273                         html : '<i class="fa fa-undo"></i>'
28274                     }
28275                 ]
28276             },
28277             {
28278                 tag : 'div',
28279                 cls : 'btn-group roo-upload-cropbox-picture',
28280                 action : 'picture',
28281                 cn : [
28282                     {
28283                         tag : 'button',
28284                         cls : 'btn btn-default',
28285                         html : '<i class="fa fa-picture-o"></i>'
28286                     }
28287                 ]
28288             },
28289             {
28290                 tag : 'div',
28291                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28292                 action : 'rotate-right',
28293                 cn : [
28294                     {
28295                         tag : 'button',
28296                         cls : 'btn btn-default',
28297                         html : '<i class="fa fa-repeat"></i>'
28298                     }
28299                 ]
28300             }
28301         ],
28302         DOCUMENT : [
28303             {
28304                 tag : 'div',
28305                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28306                 action : 'rotate-left',
28307                 cn : [
28308                     {
28309                         tag : 'button',
28310                         cls : 'btn btn-default',
28311                         html : '<i class="fa fa-undo"></i>'
28312                     }
28313                 ]
28314             },
28315             {
28316                 tag : 'div',
28317                 cls : 'btn-group roo-upload-cropbox-download',
28318                 action : 'download',
28319                 cn : [
28320                     {
28321                         tag : 'button',
28322                         cls : 'btn btn-default',
28323                         html : '<i class="fa fa-download"></i>'
28324                     }
28325                 ]
28326             },
28327             {
28328                 tag : 'div',
28329                 cls : 'btn-group roo-upload-cropbox-crop',
28330                 action : 'crop',
28331                 cn : [
28332                     {
28333                         tag : 'button',
28334                         cls : 'btn btn-default',
28335                         html : '<i class="fa fa-crop"></i>'
28336                     }
28337                 ]
28338             },
28339             {
28340                 tag : 'div',
28341                 cls : 'btn-group roo-upload-cropbox-trash',
28342                 action : 'trash',
28343                 cn : [
28344                     {
28345                         tag : 'button',
28346                         cls : 'btn btn-default',
28347                         html : '<i class="fa fa-trash"></i>'
28348                     }
28349                 ]
28350             },
28351             {
28352                 tag : 'div',
28353                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28354                 action : 'rotate-right',
28355                 cn : [
28356                     {
28357                         tag : 'button',
28358                         cls : 'btn btn-default',
28359                         html : '<i class="fa fa-repeat"></i>'
28360                     }
28361                 ]
28362             }
28363         ],
28364         ROTATOR : [
28365             {
28366                 tag : 'div',
28367                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28368                 action : 'rotate-left',
28369                 cn : [
28370                     {
28371                         tag : 'button',
28372                         cls : 'btn btn-default',
28373                         html : '<i class="fa fa-undo"></i>'
28374                     }
28375                 ]
28376             },
28377             {
28378                 tag : 'div',
28379                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28380                 action : 'rotate-right',
28381                 cn : [
28382                     {
28383                         tag : 'button',
28384                         cls : 'btn btn-default',
28385                         html : '<i class="fa fa-repeat"></i>'
28386                     }
28387                 ]
28388             }
28389         ]
28390     }
28391 });
28392
28393 /*
28394 * Licence: LGPL
28395 */
28396
28397 /**
28398  * @class Roo.bootstrap.DocumentManager
28399  * @extends Roo.bootstrap.Component
28400  * Bootstrap DocumentManager class
28401  * @cfg {String} paramName default 'imageUpload'
28402  * @cfg {String} toolTipName default 'filename'
28403  * @cfg {String} method default POST
28404  * @cfg {String} url action url
28405  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28406  * @cfg {Boolean} multiple multiple upload default true
28407  * @cfg {Number} thumbSize default 300
28408  * @cfg {String} fieldLabel
28409  * @cfg {Number} labelWidth default 4
28410  * @cfg {String} labelAlign (left|top) default left
28411  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28412 * @cfg {Number} labellg set the width of label (1-12)
28413  * @cfg {Number} labelmd set the width of label (1-12)
28414  * @cfg {Number} labelsm set the width of label (1-12)
28415  * @cfg {Number} labelxs set the width of label (1-12)
28416  * 
28417  * @constructor
28418  * Create a new DocumentManager
28419  * @param {Object} config The config object
28420  */
28421
28422 Roo.bootstrap.DocumentManager = function(config){
28423     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28424     
28425     this.files = [];
28426     this.delegates = [];
28427     
28428     this.addEvents({
28429         /**
28430          * @event initial
28431          * Fire when initial the DocumentManager
28432          * @param {Roo.bootstrap.DocumentManager} this
28433          */
28434         "initial" : true,
28435         /**
28436          * @event inspect
28437          * inspect selected file
28438          * @param {Roo.bootstrap.DocumentManager} this
28439          * @param {File} file
28440          */
28441         "inspect" : true,
28442         /**
28443          * @event exception
28444          * Fire when xhr load exception
28445          * @param {Roo.bootstrap.DocumentManager} this
28446          * @param {XMLHttpRequest} xhr
28447          */
28448         "exception" : true,
28449         /**
28450          * @event afterupload
28451          * Fire when xhr load exception
28452          * @param {Roo.bootstrap.DocumentManager} this
28453          * @param {XMLHttpRequest} xhr
28454          */
28455         "afterupload" : true,
28456         /**
28457          * @event prepare
28458          * prepare the form data
28459          * @param {Roo.bootstrap.DocumentManager} this
28460          * @param {Object} formData
28461          */
28462         "prepare" : true,
28463         /**
28464          * @event remove
28465          * Fire when remove the file
28466          * @param {Roo.bootstrap.DocumentManager} this
28467          * @param {Object} file
28468          */
28469         "remove" : true,
28470         /**
28471          * @event refresh
28472          * Fire after refresh the file
28473          * @param {Roo.bootstrap.DocumentManager} this
28474          */
28475         "refresh" : true,
28476         /**
28477          * @event click
28478          * Fire after click the image
28479          * @param {Roo.bootstrap.DocumentManager} this
28480          * @param {Object} file
28481          */
28482         "click" : true,
28483         /**
28484          * @event edit
28485          * Fire when upload a image and editable set to true
28486          * @param {Roo.bootstrap.DocumentManager} this
28487          * @param {Object} file
28488          */
28489         "edit" : true,
28490         /**
28491          * @event beforeselectfile
28492          * Fire before select file
28493          * @param {Roo.bootstrap.DocumentManager} this
28494          */
28495         "beforeselectfile" : true,
28496         /**
28497          * @event process
28498          * Fire before process file
28499          * @param {Roo.bootstrap.DocumentManager} this
28500          * @param {Object} file
28501          */
28502         "process" : true,
28503         /**
28504          * @event previewrendered
28505          * Fire when preview rendered
28506          * @param {Roo.bootstrap.DocumentManager} this
28507          * @param {Object} file
28508          */
28509         "previewrendered" : true
28510         
28511     });
28512 };
28513
28514 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28515     
28516     boxes : 0,
28517     inputName : '',
28518     thumbSize : 300,
28519     multiple : true,
28520     files : false,
28521     method : 'POST',
28522     url : '',
28523     paramName : 'imageUpload',
28524     toolTipName : 'filename',
28525     fieldLabel : '',
28526     labelWidth : 4,
28527     labelAlign : 'left',
28528     editable : true,
28529     delegates : false,
28530     xhr : false, 
28531     
28532     labellg : 0,
28533     labelmd : 0,
28534     labelsm : 0,
28535     labelxs : 0,
28536     
28537     getAutoCreate : function()
28538     {   
28539         var managerWidget = {
28540             tag : 'div',
28541             cls : 'roo-document-manager',
28542             cn : [
28543                 {
28544                     tag : 'input',
28545                     cls : 'roo-document-manager-selector',
28546                     type : 'file'
28547                 },
28548                 {
28549                     tag : 'div',
28550                     cls : 'roo-document-manager-uploader',
28551                     cn : [
28552                         {
28553                             tag : 'div',
28554                             cls : 'roo-document-manager-upload-btn',
28555                             html : '<i class="fa fa-plus"></i>'
28556                         }
28557                     ]
28558                     
28559                 }
28560             ]
28561         };
28562         
28563         var content = [
28564             {
28565                 tag : 'div',
28566                 cls : 'column col-md-12',
28567                 cn : managerWidget
28568             }
28569         ];
28570         
28571         if(this.fieldLabel.length){
28572             
28573             content = [
28574                 {
28575                     tag : 'div',
28576                     cls : 'column col-md-12',
28577                     html : this.fieldLabel
28578                 },
28579                 {
28580                     tag : 'div',
28581                     cls : 'column col-md-12',
28582                     cn : managerWidget
28583                 }
28584             ];
28585
28586             if(this.labelAlign == 'left'){
28587                 content = [
28588                     {
28589                         tag : 'div',
28590                         cls : 'column',
28591                         html : this.fieldLabel
28592                     },
28593                     {
28594                         tag : 'div',
28595                         cls : 'column',
28596                         cn : managerWidget
28597                     }
28598                 ];
28599                 
28600                 if(this.labelWidth > 12){
28601                     content[0].style = "width: " + this.labelWidth + 'px';
28602                 }
28603
28604                 if(this.labelWidth < 13 && this.labelmd == 0){
28605                     this.labelmd = this.labelWidth;
28606                 }
28607
28608                 if(this.labellg > 0){
28609                     content[0].cls += ' col-lg-' + this.labellg;
28610                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28611                 }
28612
28613                 if(this.labelmd > 0){
28614                     content[0].cls += ' col-md-' + this.labelmd;
28615                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28616                 }
28617
28618                 if(this.labelsm > 0){
28619                     content[0].cls += ' col-sm-' + this.labelsm;
28620                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28621                 }
28622
28623                 if(this.labelxs > 0){
28624                     content[0].cls += ' col-xs-' + this.labelxs;
28625                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28626                 }
28627                 
28628             }
28629         }
28630         
28631         var cfg = {
28632             tag : 'div',
28633             cls : 'row clearfix',
28634             cn : content
28635         };
28636         
28637         return cfg;
28638         
28639     },
28640     
28641     initEvents : function()
28642     {
28643         this.managerEl = this.el.select('.roo-document-manager', true).first();
28644         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28645         
28646         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28647         this.selectorEl.hide();
28648         
28649         if(this.multiple){
28650             this.selectorEl.attr('multiple', 'multiple');
28651         }
28652         
28653         this.selectorEl.on('change', this.onFileSelected, this);
28654         
28655         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28656         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28657         
28658         this.uploader.on('click', this.onUploaderClick, this);
28659         
28660         this.renderProgressDialog();
28661         
28662         var _this = this;
28663         
28664         window.addEventListener("resize", function() { _this.refresh(); } );
28665         
28666         this.fireEvent('initial', this);
28667     },
28668     
28669     renderProgressDialog : function()
28670     {
28671         var _this = this;
28672         
28673         this.progressDialog = new Roo.bootstrap.Modal({
28674             cls : 'roo-document-manager-progress-dialog',
28675             allow_close : false,
28676             title : '',
28677             buttons : [
28678                 {
28679                     name  :'cancel',
28680                     weight : 'danger',
28681                     html : 'Cancel'
28682                 }
28683             ], 
28684             listeners : { 
28685                 btnclick : function() {
28686                     _this.uploadCancel();
28687                     this.hide();
28688                 }
28689             }
28690         });
28691          
28692         this.progressDialog.render(Roo.get(document.body));
28693          
28694         this.progress = new Roo.bootstrap.Progress({
28695             cls : 'roo-document-manager-progress',
28696             active : true,
28697             striped : true
28698         });
28699         
28700         this.progress.render(this.progressDialog.getChildContainer());
28701         
28702         this.progressBar = new Roo.bootstrap.ProgressBar({
28703             cls : 'roo-document-manager-progress-bar',
28704             aria_valuenow : 0,
28705             aria_valuemin : 0,
28706             aria_valuemax : 12,
28707             panel : 'success'
28708         });
28709         
28710         this.progressBar.render(this.progress.getChildContainer());
28711     },
28712     
28713     onUploaderClick : function(e)
28714     {
28715         e.preventDefault();
28716      
28717         if(this.fireEvent('beforeselectfile', this) != false){
28718             this.selectorEl.dom.click();
28719         }
28720         
28721     },
28722     
28723     onFileSelected : function(e)
28724     {
28725         e.preventDefault();
28726         
28727         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28728             return;
28729         }
28730         
28731         Roo.each(this.selectorEl.dom.files, function(file){
28732             if(this.fireEvent('inspect', this, file) != false){
28733                 this.files.push(file);
28734             }
28735         }, this);
28736         
28737         this.queue();
28738         
28739     },
28740     
28741     queue : function()
28742     {
28743         this.selectorEl.dom.value = '';
28744         
28745         if(!this.files || !this.files.length){
28746             return;
28747         }
28748         
28749         if(this.boxes > 0 && this.files.length > this.boxes){
28750             this.files = this.files.slice(0, this.boxes);
28751         }
28752         
28753         this.uploader.show();
28754         
28755         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28756             this.uploader.hide();
28757         }
28758         
28759         var _this = this;
28760         
28761         var files = [];
28762         
28763         var docs = [];
28764         
28765         Roo.each(this.files, function(file){
28766             
28767             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28768                 var f = this.renderPreview(file);
28769                 files.push(f);
28770                 return;
28771             }
28772             
28773             if(file.type.indexOf('image') != -1){
28774                 this.delegates.push(
28775                     (function(){
28776                         _this.process(file);
28777                     }).createDelegate(this)
28778                 );
28779         
28780                 return;
28781             }
28782             
28783             docs.push(
28784                 (function(){
28785                     _this.process(file);
28786                 }).createDelegate(this)
28787             );
28788             
28789         }, this);
28790         
28791         this.files = files;
28792         
28793         this.delegates = this.delegates.concat(docs);
28794         
28795         if(!this.delegates.length){
28796             this.refresh();
28797             return;
28798         }
28799         
28800         this.progressBar.aria_valuemax = this.delegates.length;
28801         
28802         this.arrange();
28803         
28804         return;
28805     },
28806     
28807     arrange : function()
28808     {
28809         if(!this.delegates.length){
28810             this.progressDialog.hide();
28811             this.refresh();
28812             return;
28813         }
28814         
28815         var delegate = this.delegates.shift();
28816         
28817         this.progressDialog.show();
28818         
28819         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
28820         
28821         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
28822         
28823         delegate();
28824     },
28825     
28826     refresh : function()
28827     {
28828         this.uploader.show();
28829         
28830         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28831             this.uploader.hide();
28832         }
28833         
28834         Roo.isTouch ? this.closable(false) : this.closable(true);
28835         
28836         this.fireEvent('refresh', this);
28837     },
28838     
28839     onRemove : function(e, el, o)
28840     {
28841         e.preventDefault();
28842         
28843         this.fireEvent('remove', this, o);
28844         
28845     },
28846     
28847     remove : function(o)
28848     {
28849         var files = [];
28850         
28851         Roo.each(this.files, function(file){
28852             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
28853                 files.push(file);
28854                 return;
28855             }
28856
28857             o.target.remove();
28858
28859         }, this);
28860         
28861         this.files = files;
28862         
28863         this.refresh();
28864     },
28865     
28866     clear : function()
28867     {
28868         Roo.each(this.files, function(file){
28869             if(!file.target){
28870                 return;
28871             }
28872             
28873             file.target.remove();
28874
28875         }, this);
28876         
28877         this.files = [];
28878         
28879         this.refresh();
28880     },
28881     
28882     onClick : function(e, el, o)
28883     {
28884         e.preventDefault();
28885         
28886         this.fireEvent('click', this, o);
28887         
28888     },
28889     
28890     closable : function(closable)
28891     {
28892         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
28893             
28894             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28895             
28896             if(closable){
28897                 el.show();
28898                 return;
28899             }
28900             
28901             el.hide();
28902             
28903         }, this);
28904     },
28905     
28906     xhrOnLoad : function(xhr)
28907     {
28908         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28909             el.remove();
28910         }, this);
28911         
28912         if (xhr.readyState !== 4) {
28913             this.arrange();
28914             this.fireEvent('exception', this, xhr);
28915             return;
28916         }
28917
28918         var response = Roo.decode(xhr.responseText);
28919         
28920         if(!response.success){
28921             this.arrange();
28922             this.fireEvent('exception', this, xhr);
28923             return;
28924         }
28925         
28926         var file = this.renderPreview(response.data);
28927         
28928         this.files.push(file);
28929         
28930         this.arrange();
28931         
28932         this.fireEvent('afterupload', this, xhr);
28933         
28934     },
28935     
28936     xhrOnError : function(xhr)
28937     {
28938         Roo.log('xhr on error');
28939         
28940         var response = Roo.decode(xhr.responseText);
28941           
28942         Roo.log(response);
28943         
28944         this.arrange();
28945     },
28946     
28947     process : function(file)
28948     {
28949         if(this.fireEvent('process', this, file) !== false){
28950             if(this.editable && file.type.indexOf('image') != -1){
28951                 this.fireEvent('edit', this, file);
28952                 return;
28953             }
28954
28955             this.uploadStart(file, false);
28956
28957             return;
28958         }
28959         
28960     },
28961     
28962     uploadStart : function(file, crop)
28963     {
28964         this.xhr = new XMLHttpRequest();
28965         
28966         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28967             this.arrange();
28968             return;
28969         }
28970         
28971         file.xhr = this.xhr;
28972             
28973         this.managerEl.createChild({
28974             tag : 'div',
28975             cls : 'roo-document-manager-loading',
28976             cn : [
28977                 {
28978                     tag : 'div',
28979                     tooltip : file.name,
28980                     cls : 'roo-document-manager-thumb',
28981                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28982                 }
28983             ]
28984
28985         });
28986
28987         this.xhr.open(this.method, this.url, true);
28988         
28989         var headers = {
28990             "Accept": "application/json",
28991             "Cache-Control": "no-cache",
28992             "X-Requested-With": "XMLHttpRequest"
28993         };
28994         
28995         for (var headerName in headers) {
28996             var headerValue = headers[headerName];
28997             if (headerValue) {
28998                 this.xhr.setRequestHeader(headerName, headerValue);
28999             }
29000         }
29001         
29002         var _this = this;
29003         
29004         this.xhr.onload = function()
29005         {
29006             _this.xhrOnLoad(_this.xhr);
29007         }
29008         
29009         this.xhr.onerror = function()
29010         {
29011             _this.xhrOnError(_this.xhr);
29012         }
29013         
29014         var formData = new FormData();
29015
29016         formData.append('returnHTML', 'NO');
29017         
29018         if(crop){
29019             formData.append('crop', crop);
29020         }
29021         
29022         formData.append(this.paramName, file, file.name);
29023         
29024         var options = {
29025             file : file, 
29026             manually : false
29027         };
29028         
29029         if(this.fireEvent('prepare', this, formData, options) != false){
29030             
29031             if(options.manually){
29032                 return;
29033             }
29034             
29035             this.xhr.send(formData);
29036             return;
29037         };
29038         
29039         this.uploadCancel();
29040     },
29041     
29042     uploadCancel : function()
29043     {
29044         if (this.xhr) {
29045             this.xhr.abort();
29046         }
29047         
29048         this.delegates = [];
29049         
29050         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29051             el.remove();
29052         }, this);
29053         
29054         this.arrange();
29055     },
29056     
29057     renderPreview : function(file)
29058     {
29059         if(typeof(file.target) != 'undefined' && file.target){
29060             return file;
29061         }
29062         
29063         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29064         
29065         var previewEl = this.managerEl.createChild({
29066             tag : 'div',
29067             cls : 'roo-document-manager-preview',
29068             cn : [
29069                 {
29070                     tag : 'div',
29071                     tooltip : file[this.toolTipName],
29072                     cls : 'roo-document-manager-thumb',
29073                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29074                 },
29075                 {
29076                     tag : 'button',
29077                     cls : 'close',
29078                     html : '<i class="fa fa-times-circle"></i>'
29079                 }
29080             ]
29081         });
29082
29083         var close = previewEl.select('button.close', true).first();
29084
29085         close.on('click', this.onRemove, this, file);
29086
29087         file.target = previewEl;
29088
29089         var image = previewEl.select('img', true).first();
29090         
29091         var _this = this;
29092         
29093         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29094         
29095         image.on('click', this.onClick, this, file);
29096         
29097         this.fireEvent('previewrendered', this, file);
29098         
29099         return file;
29100         
29101     },
29102     
29103     onPreviewLoad : function(file, image)
29104     {
29105         if(typeof(file.target) == 'undefined' || !file.target){
29106             return;
29107         }
29108         
29109         var width = image.dom.naturalWidth || image.dom.width;
29110         var height = image.dom.naturalHeight || image.dom.height;
29111         
29112         if(width > height){
29113             file.target.addClass('wide');
29114             return;
29115         }
29116         
29117         file.target.addClass('tall');
29118         return;
29119         
29120     },
29121     
29122     uploadFromSource : function(file, crop)
29123     {
29124         this.xhr = new XMLHttpRequest();
29125         
29126         this.managerEl.createChild({
29127             tag : 'div',
29128             cls : 'roo-document-manager-loading',
29129             cn : [
29130                 {
29131                     tag : 'div',
29132                     tooltip : file.name,
29133                     cls : 'roo-document-manager-thumb',
29134                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29135                 }
29136             ]
29137
29138         });
29139
29140         this.xhr.open(this.method, this.url, true);
29141         
29142         var headers = {
29143             "Accept": "application/json",
29144             "Cache-Control": "no-cache",
29145             "X-Requested-With": "XMLHttpRequest"
29146         };
29147         
29148         for (var headerName in headers) {
29149             var headerValue = headers[headerName];
29150             if (headerValue) {
29151                 this.xhr.setRequestHeader(headerName, headerValue);
29152             }
29153         }
29154         
29155         var _this = this;
29156         
29157         this.xhr.onload = function()
29158         {
29159             _this.xhrOnLoad(_this.xhr);
29160         }
29161         
29162         this.xhr.onerror = function()
29163         {
29164             _this.xhrOnError(_this.xhr);
29165         }
29166         
29167         var formData = new FormData();
29168
29169         formData.append('returnHTML', 'NO');
29170         
29171         formData.append('crop', crop);
29172         
29173         if(typeof(file.filename) != 'undefined'){
29174             formData.append('filename', file.filename);
29175         }
29176         
29177         if(typeof(file.mimetype) != 'undefined'){
29178             formData.append('mimetype', file.mimetype);
29179         }
29180         
29181         Roo.log(formData);
29182         
29183         if(this.fireEvent('prepare', this, formData) != false){
29184             this.xhr.send(formData);
29185         };
29186     }
29187 });
29188
29189 /*
29190 * Licence: LGPL
29191 */
29192
29193 /**
29194  * @class Roo.bootstrap.DocumentViewer
29195  * @extends Roo.bootstrap.Component
29196  * Bootstrap DocumentViewer class
29197  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29198  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29199  * 
29200  * @constructor
29201  * Create a new DocumentViewer
29202  * @param {Object} config The config object
29203  */
29204
29205 Roo.bootstrap.DocumentViewer = function(config){
29206     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29207     
29208     this.addEvents({
29209         /**
29210          * @event initial
29211          * Fire after initEvent
29212          * @param {Roo.bootstrap.DocumentViewer} this
29213          */
29214         "initial" : true,
29215         /**
29216          * @event click
29217          * Fire after click
29218          * @param {Roo.bootstrap.DocumentViewer} this
29219          */
29220         "click" : true,
29221         /**
29222          * @event download
29223          * Fire after download button
29224          * @param {Roo.bootstrap.DocumentViewer} this
29225          */
29226         "download" : true,
29227         /**
29228          * @event trash
29229          * Fire after trash button
29230          * @param {Roo.bootstrap.DocumentViewer} this
29231          */
29232         "trash" : true
29233         
29234     });
29235 };
29236
29237 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29238     
29239     showDownload : true,
29240     
29241     showTrash : true,
29242     
29243     getAutoCreate : function()
29244     {
29245         var cfg = {
29246             tag : 'div',
29247             cls : 'roo-document-viewer',
29248             cn : [
29249                 {
29250                     tag : 'div',
29251                     cls : 'roo-document-viewer-body',
29252                     cn : [
29253                         {
29254                             tag : 'div',
29255                             cls : 'roo-document-viewer-thumb',
29256                             cn : [
29257                                 {
29258                                     tag : 'img',
29259                                     cls : 'roo-document-viewer-image'
29260                                 }
29261                             ]
29262                         }
29263                     ]
29264                 },
29265                 {
29266                     tag : 'div',
29267                     cls : 'roo-document-viewer-footer',
29268                     cn : {
29269                         tag : 'div',
29270                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29271                         cn : [
29272                             {
29273                                 tag : 'div',
29274                                 cls : 'btn-group roo-document-viewer-download',
29275                                 cn : [
29276                                     {
29277                                         tag : 'button',
29278                                         cls : 'btn btn-default',
29279                                         html : '<i class="fa fa-download"></i>'
29280                                     }
29281                                 ]
29282                             },
29283                             {
29284                                 tag : 'div',
29285                                 cls : 'btn-group roo-document-viewer-trash',
29286                                 cn : [
29287                                     {
29288                                         tag : 'button',
29289                                         cls : 'btn btn-default',
29290                                         html : '<i class="fa fa-trash"></i>'
29291                                     }
29292                                 ]
29293                             }
29294                         ]
29295                     }
29296                 }
29297             ]
29298         };
29299         
29300         return cfg;
29301     },
29302     
29303     initEvents : function()
29304     {
29305         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29306         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29307         
29308         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29309         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29310         
29311         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29312         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29313         
29314         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29315         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29316         
29317         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29318         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29319         
29320         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29321         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29322         
29323         this.bodyEl.on('click', this.onClick, this);
29324         this.downloadBtn.on('click', this.onDownload, this);
29325         this.trashBtn.on('click', this.onTrash, this);
29326         
29327         this.downloadBtn.hide();
29328         this.trashBtn.hide();
29329         
29330         if(this.showDownload){
29331             this.downloadBtn.show();
29332         }
29333         
29334         if(this.showTrash){
29335             this.trashBtn.show();
29336         }
29337         
29338         if(!this.showDownload && !this.showTrash) {
29339             this.footerEl.hide();
29340         }
29341         
29342     },
29343     
29344     initial : function()
29345     {
29346         this.fireEvent('initial', this);
29347         
29348     },
29349     
29350     onClick : function(e)
29351     {
29352         e.preventDefault();
29353         
29354         this.fireEvent('click', this);
29355     },
29356     
29357     onDownload : function(e)
29358     {
29359         e.preventDefault();
29360         
29361         this.fireEvent('download', this);
29362     },
29363     
29364     onTrash : function(e)
29365     {
29366         e.preventDefault();
29367         
29368         this.fireEvent('trash', this);
29369     }
29370     
29371 });
29372 /*
29373  * - LGPL
29374  *
29375  * nav progress bar
29376  * 
29377  */
29378
29379 /**
29380  * @class Roo.bootstrap.NavProgressBar
29381  * @extends Roo.bootstrap.Component
29382  * Bootstrap NavProgressBar class
29383  * 
29384  * @constructor
29385  * Create a new nav progress bar
29386  * @param {Object} config The config object
29387  */
29388
29389 Roo.bootstrap.NavProgressBar = function(config){
29390     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29391
29392     this.bullets = this.bullets || [];
29393    
29394 //    Roo.bootstrap.NavProgressBar.register(this);
29395      this.addEvents({
29396         /**
29397              * @event changed
29398              * Fires when the active item changes
29399              * @param {Roo.bootstrap.NavProgressBar} this
29400              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29401              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29402          */
29403         'changed': true
29404      });
29405     
29406 };
29407
29408 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29409     
29410     bullets : [],
29411     barItems : [],
29412     
29413     getAutoCreate : function()
29414     {
29415         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29416         
29417         cfg = {
29418             tag : 'div',
29419             cls : 'roo-navigation-bar-group',
29420             cn : [
29421                 {
29422                     tag : 'div',
29423                     cls : 'roo-navigation-top-bar'
29424                 },
29425                 {
29426                     tag : 'div',
29427                     cls : 'roo-navigation-bullets-bar',
29428                     cn : [
29429                         {
29430                             tag : 'ul',
29431                             cls : 'roo-navigation-bar'
29432                         }
29433                     ]
29434                 },
29435                 
29436                 {
29437                     tag : 'div',
29438                     cls : 'roo-navigation-bottom-bar'
29439                 }
29440             ]
29441             
29442         };
29443         
29444         return cfg;
29445         
29446     },
29447     
29448     initEvents: function() 
29449     {
29450         
29451     },
29452     
29453     onRender : function(ct, position) 
29454     {
29455         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29456         
29457         if(this.bullets.length){
29458             Roo.each(this.bullets, function(b){
29459                this.addItem(b);
29460             }, this);
29461         }
29462         
29463         this.format();
29464         
29465     },
29466     
29467     addItem : function(cfg)
29468     {
29469         var item = new Roo.bootstrap.NavProgressItem(cfg);
29470         
29471         item.parentId = this.id;
29472         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29473         
29474         if(cfg.html){
29475             var top = new Roo.bootstrap.Element({
29476                 tag : 'div',
29477                 cls : 'roo-navigation-bar-text'
29478             });
29479             
29480             var bottom = new Roo.bootstrap.Element({
29481                 tag : 'div',
29482                 cls : 'roo-navigation-bar-text'
29483             });
29484             
29485             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29486             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29487             
29488             var topText = new Roo.bootstrap.Element({
29489                 tag : 'span',
29490                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29491             });
29492             
29493             var bottomText = new Roo.bootstrap.Element({
29494                 tag : 'span',
29495                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29496             });
29497             
29498             topText.onRender(top.el, null);
29499             bottomText.onRender(bottom.el, null);
29500             
29501             item.topEl = top;
29502             item.bottomEl = bottom;
29503         }
29504         
29505         this.barItems.push(item);
29506         
29507         return item;
29508     },
29509     
29510     getActive : function()
29511     {
29512         var active = false;
29513         
29514         Roo.each(this.barItems, function(v){
29515             
29516             if (!v.isActive()) {
29517                 return;
29518             }
29519             
29520             active = v;
29521             return false;
29522             
29523         });
29524         
29525         return active;
29526     },
29527     
29528     setActiveItem : function(item)
29529     {
29530         var prev = false;
29531         
29532         Roo.each(this.barItems, function(v){
29533             if (v.rid == item.rid) {
29534                 return ;
29535             }
29536             
29537             if (v.isActive()) {
29538                 v.setActive(false);
29539                 prev = v;
29540             }
29541         });
29542
29543         item.setActive(true);
29544         
29545         this.fireEvent('changed', this, item, prev);
29546     },
29547     
29548     getBarItem: function(rid)
29549     {
29550         var ret = false;
29551         
29552         Roo.each(this.barItems, function(e) {
29553             if (e.rid != rid) {
29554                 return;
29555             }
29556             
29557             ret =  e;
29558             return false;
29559         });
29560         
29561         return ret;
29562     },
29563     
29564     indexOfItem : function(item)
29565     {
29566         var index = false;
29567         
29568         Roo.each(this.barItems, function(v, i){
29569             
29570             if (v.rid != item.rid) {
29571                 return;
29572             }
29573             
29574             index = i;
29575             return false
29576         });
29577         
29578         return index;
29579     },
29580     
29581     setActiveNext : function()
29582     {
29583         var i = this.indexOfItem(this.getActive());
29584         
29585         if (i > this.barItems.length) {
29586             return;
29587         }
29588         
29589         this.setActiveItem(this.barItems[i+1]);
29590     },
29591     
29592     setActivePrev : function()
29593     {
29594         var i = this.indexOfItem(this.getActive());
29595         
29596         if (i  < 1) {
29597             return;
29598         }
29599         
29600         this.setActiveItem(this.barItems[i-1]);
29601     },
29602     
29603     format : function()
29604     {
29605         if(!this.barItems.length){
29606             return;
29607         }
29608      
29609         var width = 100 / this.barItems.length;
29610         
29611         Roo.each(this.barItems, function(i){
29612             i.el.setStyle('width', width + '%');
29613             i.topEl.el.setStyle('width', width + '%');
29614             i.bottomEl.el.setStyle('width', width + '%');
29615         }, this);
29616         
29617     }
29618     
29619 });
29620 /*
29621  * - LGPL
29622  *
29623  * Nav Progress Item
29624  * 
29625  */
29626
29627 /**
29628  * @class Roo.bootstrap.NavProgressItem
29629  * @extends Roo.bootstrap.Component
29630  * Bootstrap NavProgressItem class
29631  * @cfg {String} rid the reference id
29632  * @cfg {Boolean} active (true|false) Is item active default false
29633  * @cfg {Boolean} disabled (true|false) Is item active default false
29634  * @cfg {String} html
29635  * @cfg {String} position (top|bottom) text position default bottom
29636  * @cfg {String} icon show icon instead of number
29637  * 
29638  * @constructor
29639  * Create a new NavProgressItem
29640  * @param {Object} config The config object
29641  */
29642 Roo.bootstrap.NavProgressItem = function(config){
29643     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29644     this.addEvents({
29645         // raw events
29646         /**
29647          * @event click
29648          * The raw click event for the entire grid.
29649          * @param {Roo.bootstrap.NavProgressItem} this
29650          * @param {Roo.EventObject} e
29651          */
29652         "click" : true
29653     });
29654    
29655 };
29656
29657 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29658     
29659     rid : '',
29660     active : false,
29661     disabled : false,
29662     html : '',
29663     position : 'bottom',
29664     icon : false,
29665     
29666     getAutoCreate : function()
29667     {
29668         var iconCls = 'roo-navigation-bar-item-icon';
29669         
29670         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29671         
29672         var cfg = {
29673             tag: 'li',
29674             cls: 'roo-navigation-bar-item',
29675             cn : [
29676                 {
29677                     tag : 'i',
29678                     cls : iconCls
29679                 }
29680             ]
29681         };
29682         
29683         if(this.active){
29684             cfg.cls += ' active';
29685         }
29686         if(this.disabled){
29687             cfg.cls += ' disabled';
29688         }
29689         
29690         return cfg;
29691     },
29692     
29693     disable : function()
29694     {
29695         this.setDisabled(true);
29696     },
29697     
29698     enable : function()
29699     {
29700         this.setDisabled(false);
29701     },
29702     
29703     initEvents: function() 
29704     {
29705         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29706         
29707         this.iconEl.on('click', this.onClick, this);
29708     },
29709     
29710     onClick : function(e)
29711     {
29712         e.preventDefault();
29713         
29714         if(this.disabled){
29715             return;
29716         }
29717         
29718         if(this.fireEvent('click', this, e) === false){
29719             return;
29720         };
29721         
29722         this.parent().setActiveItem(this);
29723     },
29724     
29725     isActive: function () 
29726     {
29727         return this.active;
29728     },
29729     
29730     setActive : function(state)
29731     {
29732         if(this.active == state){
29733             return;
29734         }
29735         
29736         this.active = state;
29737         
29738         if (state) {
29739             this.el.addClass('active');
29740             return;
29741         }
29742         
29743         this.el.removeClass('active');
29744         
29745         return;
29746     },
29747     
29748     setDisabled : function(state)
29749     {
29750         if(this.disabled == state){
29751             return;
29752         }
29753         
29754         this.disabled = state;
29755         
29756         if (state) {
29757             this.el.addClass('disabled');
29758             return;
29759         }
29760         
29761         this.el.removeClass('disabled');
29762     },
29763     
29764     tooltipEl : function()
29765     {
29766         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29767     }
29768 });
29769  
29770
29771  /*
29772  * - LGPL
29773  *
29774  * FieldLabel
29775  * 
29776  */
29777
29778 /**
29779  * @class Roo.bootstrap.FieldLabel
29780  * @extends Roo.bootstrap.Component
29781  * Bootstrap FieldLabel class
29782  * @cfg {String} html contents of the element
29783  * @cfg {String} tag tag of the element default label
29784  * @cfg {String} cls class of the element
29785  * @cfg {String} target label target 
29786  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29787  * @cfg {String} invalidClass default "text-warning"
29788  * @cfg {String} validClass default "text-success"
29789  * @cfg {String} iconTooltip default "This field is required"
29790  * @cfg {String} indicatorpos (left|right) default left
29791  * 
29792  * @constructor
29793  * Create a new FieldLabel
29794  * @param {Object} config The config object
29795  */
29796
29797 Roo.bootstrap.FieldLabel = function(config){
29798     Roo.bootstrap.Element.superclass.constructor.call(this, config);
29799     
29800     this.addEvents({
29801             /**
29802              * @event invalid
29803              * Fires after the field has been marked as invalid.
29804              * @param {Roo.form.FieldLabel} this
29805              * @param {String} msg The validation message
29806              */
29807             invalid : true,
29808             /**
29809              * @event valid
29810              * Fires after the field has been validated with no errors.
29811              * @param {Roo.form.FieldLabel} this
29812              */
29813             valid : true
29814         });
29815 };
29816
29817 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
29818     
29819     tag: 'label',
29820     cls: '',
29821     html: '',
29822     target: '',
29823     allowBlank : true,
29824     invalidClass : 'has-warning',
29825     validClass : 'has-success',
29826     iconTooltip : 'This field is required',
29827     indicatorpos : 'left',
29828     
29829     getAutoCreate : function(){
29830         
29831         var cfg = {
29832             tag : this.tag,
29833             cls : 'roo-bootstrap-field-label ' + this.cls,
29834             for : this.target,
29835             cn : [
29836                 {
29837                     tag : 'i',
29838                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
29839                     tooltip : this.iconTooltip
29840                 },
29841                 {
29842                     tag : 'span',
29843                     html : this.html
29844                 }
29845             ] 
29846         };
29847         
29848         if(this.indicatorpos == 'right'){
29849             var cfg = {
29850                 tag : this.tag,
29851                 cls : 'roo-bootstrap-field-label ' + this.cls,
29852                 for : this.target,
29853                 cn : [
29854                     {
29855                         tag : 'span',
29856                         html : this.html
29857                     },
29858                     {
29859                         tag : 'i',
29860                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
29861                         tooltip : this.iconTooltip
29862                     }
29863                 ] 
29864             };
29865         }
29866         
29867         return cfg;
29868     },
29869     
29870     initEvents: function() 
29871     {
29872         Roo.bootstrap.Element.superclass.initEvents.call(this);
29873         
29874         this.indicator = this.indicatorEl();
29875         
29876         if(this.indicator){
29877             this.indicator.removeClass('visible');
29878             this.indicator.addClass('invisible');
29879         }
29880         
29881         Roo.bootstrap.FieldLabel.register(this);
29882     },
29883     
29884     indicatorEl : function()
29885     {
29886         var indicator = this.el.select('i.roo-required-indicator',true).first();
29887         
29888         if(!indicator){
29889             return false;
29890         }
29891         
29892         return indicator;
29893         
29894     },
29895     
29896     /**
29897      * Mark this field as valid
29898      */
29899     markValid : function()
29900     {
29901         if(this.indicator){
29902             this.indicator.removeClass('visible');
29903             this.indicator.addClass('invisible');
29904         }
29905         
29906         this.el.removeClass(this.invalidClass);
29907         
29908         this.el.addClass(this.validClass);
29909         
29910         this.fireEvent('valid', this);
29911     },
29912     
29913     /**
29914      * Mark this field as invalid
29915      * @param {String} msg The validation message
29916      */
29917     markInvalid : function(msg)
29918     {
29919         if(this.indicator){
29920             this.indicator.removeClass('invisible');
29921             this.indicator.addClass('visible');
29922         }
29923         
29924         this.el.removeClass(this.validClass);
29925         
29926         this.el.addClass(this.invalidClass);
29927         
29928         this.fireEvent('invalid', this, msg);
29929     }
29930     
29931    
29932 });
29933
29934 Roo.apply(Roo.bootstrap.FieldLabel, {
29935     
29936     groups: {},
29937     
29938      /**
29939     * register a FieldLabel Group
29940     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
29941     */
29942     register : function(label)
29943     {
29944         if(this.groups.hasOwnProperty(label.target)){
29945             return;
29946         }
29947      
29948         this.groups[label.target] = label;
29949         
29950     },
29951     /**
29952     * fetch a FieldLabel Group based on the target
29953     * @param {string} target
29954     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
29955     */
29956     get: function(target) {
29957         if (typeof(this.groups[target]) == 'undefined') {
29958             return false;
29959         }
29960         
29961         return this.groups[target] ;
29962     }
29963 });
29964
29965  
29966
29967  /*
29968  * - LGPL
29969  *
29970  * page DateSplitField.
29971  * 
29972  */
29973
29974
29975 /**
29976  * @class Roo.bootstrap.DateSplitField
29977  * @extends Roo.bootstrap.Component
29978  * Bootstrap DateSplitField class
29979  * @cfg {string} fieldLabel - the label associated
29980  * @cfg {Number} labelWidth set the width of label (0-12)
29981  * @cfg {String} labelAlign (top|left)
29982  * @cfg {Boolean} dayAllowBlank (true|false) default false
29983  * @cfg {Boolean} monthAllowBlank (true|false) default false
29984  * @cfg {Boolean} yearAllowBlank (true|false) default false
29985  * @cfg {string} dayPlaceholder 
29986  * @cfg {string} monthPlaceholder
29987  * @cfg {string} yearPlaceholder
29988  * @cfg {string} dayFormat default 'd'
29989  * @cfg {string} monthFormat default 'm'
29990  * @cfg {string} yearFormat default 'Y'
29991  * @cfg {Number} labellg set the width of label (1-12)
29992  * @cfg {Number} labelmd set the width of label (1-12)
29993  * @cfg {Number} labelsm set the width of label (1-12)
29994  * @cfg {Number} labelxs set the width of label (1-12)
29995
29996  *     
29997  * @constructor
29998  * Create a new DateSplitField
29999  * @param {Object} config The config object
30000  */
30001
30002 Roo.bootstrap.DateSplitField = function(config){
30003     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
30004     
30005     this.addEvents({
30006         // raw events
30007          /**
30008          * @event years
30009          * getting the data of years
30010          * @param {Roo.bootstrap.DateSplitField} this
30011          * @param {Object} years
30012          */
30013         "years" : true,
30014         /**
30015          * @event days
30016          * getting the data of days
30017          * @param {Roo.bootstrap.DateSplitField} this
30018          * @param {Object} days
30019          */
30020         "days" : true,
30021         /**
30022          * @event invalid
30023          * Fires after the field has been marked as invalid.
30024          * @param {Roo.form.Field} this
30025          * @param {String} msg The validation message
30026          */
30027         invalid : true,
30028        /**
30029          * @event valid
30030          * Fires after the field has been validated with no errors.
30031          * @param {Roo.form.Field} this
30032          */
30033         valid : true
30034     });
30035 };
30036
30037 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30038     
30039     fieldLabel : '',
30040     labelAlign : 'top',
30041     labelWidth : 3,
30042     dayAllowBlank : false,
30043     monthAllowBlank : false,
30044     yearAllowBlank : false,
30045     dayPlaceholder : '',
30046     monthPlaceholder : '',
30047     yearPlaceholder : '',
30048     dayFormat : 'd',
30049     monthFormat : 'm',
30050     yearFormat : 'Y',
30051     isFormField : true,
30052     labellg : 0,
30053     labelmd : 0,
30054     labelsm : 0,
30055     labelxs : 0,
30056     
30057     getAutoCreate : function()
30058     {
30059         var cfg = {
30060             tag : 'div',
30061             cls : 'row roo-date-split-field-group',
30062             cn : [
30063                 {
30064                     tag : 'input',
30065                     type : 'hidden',
30066                     cls : 'form-hidden-field roo-date-split-field-group-value',
30067                     name : this.name
30068                 }
30069             ]
30070         };
30071         
30072         var labelCls = 'col-md-12';
30073         var contentCls = 'col-md-4';
30074         
30075         if(this.fieldLabel){
30076             
30077             var label = {
30078                 tag : 'div',
30079                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30080                 cn : [
30081                     {
30082                         tag : 'label',
30083                         html : this.fieldLabel
30084                     }
30085                 ]
30086             };
30087             
30088             if(this.labelAlign == 'left'){
30089             
30090                 if(this.labelWidth > 12){
30091                     label.style = "width: " + this.labelWidth + 'px';
30092                 }
30093
30094                 if(this.labelWidth < 13 && this.labelmd == 0){
30095                     this.labelmd = this.labelWidth;
30096                 }
30097
30098                 if(this.labellg > 0){
30099                     labelCls = ' col-lg-' + this.labellg;
30100                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30101                 }
30102
30103                 if(this.labelmd > 0){
30104                     labelCls = ' col-md-' + this.labelmd;
30105                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30106                 }
30107
30108                 if(this.labelsm > 0){
30109                     labelCls = ' col-sm-' + this.labelsm;
30110                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30111                 }
30112
30113                 if(this.labelxs > 0){
30114                     labelCls = ' col-xs-' + this.labelxs;
30115                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30116                 }
30117             }
30118             
30119             label.cls += ' ' + labelCls;
30120             
30121             cfg.cn.push(label);
30122         }
30123         
30124         Roo.each(['day', 'month', 'year'], function(t){
30125             cfg.cn.push({
30126                 tag : 'div',
30127                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30128             });
30129         }, this);
30130         
30131         return cfg;
30132     },
30133     
30134     inputEl: function ()
30135     {
30136         return this.el.select('.roo-date-split-field-group-value', true).first();
30137     },
30138     
30139     onRender : function(ct, position) 
30140     {
30141         var _this = this;
30142         
30143         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30144         
30145         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30146         
30147         this.dayField = new Roo.bootstrap.ComboBox({
30148             allowBlank : this.dayAllowBlank,
30149             alwaysQuery : true,
30150             displayField : 'value',
30151             editable : false,
30152             fieldLabel : '',
30153             forceSelection : true,
30154             mode : 'local',
30155             placeholder : this.dayPlaceholder,
30156             selectOnFocus : true,
30157             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30158             triggerAction : 'all',
30159             typeAhead : true,
30160             valueField : 'value',
30161             store : new Roo.data.SimpleStore({
30162                 data : (function() {    
30163                     var days = [];
30164                     _this.fireEvent('days', _this, days);
30165                     return days;
30166                 })(),
30167                 fields : [ 'value' ]
30168             }),
30169             listeners : {
30170                 select : function (_self, record, index)
30171                 {
30172                     _this.setValue(_this.getValue());
30173                 }
30174             }
30175         });
30176
30177         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30178         
30179         this.monthField = new Roo.bootstrap.MonthField({
30180             after : '<i class=\"fa fa-calendar\"></i>',
30181             allowBlank : this.monthAllowBlank,
30182             placeholder : this.monthPlaceholder,
30183             readOnly : true,
30184             listeners : {
30185                 render : function (_self)
30186                 {
30187                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30188                         e.preventDefault();
30189                         _self.focus();
30190                     });
30191                 },
30192                 select : function (_self, oldvalue, newvalue)
30193                 {
30194                     _this.setValue(_this.getValue());
30195                 }
30196             }
30197         });
30198         
30199         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30200         
30201         this.yearField = new Roo.bootstrap.ComboBox({
30202             allowBlank : this.yearAllowBlank,
30203             alwaysQuery : true,
30204             displayField : 'value',
30205             editable : false,
30206             fieldLabel : '',
30207             forceSelection : true,
30208             mode : 'local',
30209             placeholder : this.yearPlaceholder,
30210             selectOnFocus : true,
30211             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30212             triggerAction : 'all',
30213             typeAhead : true,
30214             valueField : 'value',
30215             store : new Roo.data.SimpleStore({
30216                 data : (function() {
30217                     var years = [];
30218                     _this.fireEvent('years', _this, years);
30219                     return years;
30220                 })(),
30221                 fields : [ 'value' ]
30222             }),
30223             listeners : {
30224                 select : function (_self, record, index)
30225                 {
30226                     _this.setValue(_this.getValue());
30227                 }
30228             }
30229         });
30230
30231         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30232     },
30233     
30234     setValue : function(v, format)
30235     {
30236         this.inputEl.dom.value = v;
30237         
30238         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30239         
30240         var d = Date.parseDate(v, f);
30241         
30242         if(!d){
30243             this.validate();
30244             return;
30245         }
30246         
30247         this.setDay(d.format(this.dayFormat));
30248         this.setMonth(d.format(this.monthFormat));
30249         this.setYear(d.format(this.yearFormat));
30250         
30251         this.validate();
30252         
30253         return;
30254     },
30255     
30256     setDay : function(v)
30257     {
30258         this.dayField.setValue(v);
30259         this.inputEl.dom.value = this.getValue();
30260         this.validate();
30261         return;
30262     },
30263     
30264     setMonth : function(v)
30265     {
30266         this.monthField.setValue(v, true);
30267         this.inputEl.dom.value = this.getValue();
30268         this.validate();
30269         return;
30270     },
30271     
30272     setYear : function(v)
30273     {
30274         this.yearField.setValue(v);
30275         this.inputEl.dom.value = this.getValue();
30276         this.validate();
30277         return;
30278     },
30279     
30280     getDay : function()
30281     {
30282         return this.dayField.getValue();
30283     },
30284     
30285     getMonth : function()
30286     {
30287         return this.monthField.getValue();
30288     },
30289     
30290     getYear : function()
30291     {
30292         return this.yearField.getValue();
30293     },
30294     
30295     getValue : function()
30296     {
30297         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30298         
30299         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30300         
30301         return date;
30302     },
30303     
30304     reset : function()
30305     {
30306         this.setDay('');
30307         this.setMonth('');
30308         this.setYear('');
30309         this.inputEl.dom.value = '';
30310         this.validate();
30311         return;
30312     },
30313     
30314     validate : function()
30315     {
30316         var d = this.dayField.validate();
30317         var m = this.monthField.validate();
30318         var y = this.yearField.validate();
30319         
30320         var valid = true;
30321         
30322         if(
30323                 (!this.dayAllowBlank && !d) ||
30324                 (!this.monthAllowBlank && !m) ||
30325                 (!this.yearAllowBlank && !y)
30326         ){
30327             valid = false;
30328         }
30329         
30330         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30331             return valid;
30332         }
30333         
30334         if(valid){
30335             this.markValid();
30336             return valid;
30337         }
30338         
30339         this.markInvalid();
30340         
30341         return valid;
30342     },
30343     
30344     markValid : function()
30345     {
30346         
30347         var label = this.el.select('label', true).first();
30348         var icon = this.el.select('i.fa-star', true).first();
30349
30350         if(label && icon){
30351             icon.remove();
30352         }
30353         
30354         this.fireEvent('valid', this);
30355     },
30356     
30357      /**
30358      * Mark this field as invalid
30359      * @param {String} msg The validation message
30360      */
30361     markInvalid : function(msg)
30362     {
30363         
30364         var label = this.el.select('label', true).first();
30365         var icon = this.el.select('i.fa-star', true).first();
30366
30367         if(label && !icon){
30368             this.el.select('.roo-date-split-field-label', true).createChild({
30369                 tag : 'i',
30370                 cls : 'text-danger fa fa-lg fa-star',
30371                 tooltip : 'This field is required',
30372                 style : 'margin-right:5px;'
30373             }, label, true);
30374         }
30375         
30376         this.fireEvent('invalid', this, msg);
30377     },
30378     
30379     clearInvalid : function()
30380     {
30381         var label = this.el.select('label', true).first();
30382         var icon = this.el.select('i.fa-star', true).first();
30383
30384         if(label && icon){
30385             icon.remove();
30386         }
30387         
30388         this.fireEvent('valid', this);
30389     },
30390     
30391     getName: function()
30392     {
30393         return this.name;
30394     }
30395     
30396 });
30397
30398  /**
30399  *
30400  * This is based on 
30401  * http://masonry.desandro.com
30402  *
30403  * The idea is to render all the bricks based on vertical width...
30404  *
30405  * The original code extends 'outlayer' - we might need to use that....
30406  * 
30407  */
30408
30409
30410 /**
30411  * @class Roo.bootstrap.LayoutMasonry
30412  * @extends Roo.bootstrap.Component
30413  * Bootstrap Layout Masonry class
30414  * 
30415  * @constructor
30416  * Create a new Element
30417  * @param {Object} config The config object
30418  */
30419
30420 Roo.bootstrap.LayoutMasonry = function(config){
30421     
30422     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30423     
30424     this.bricks = [];
30425     
30426     Roo.bootstrap.LayoutMasonry.register(this);
30427     
30428     this.addEvents({
30429         // raw events
30430         /**
30431          * @event layout
30432          * Fire after layout the items
30433          * @param {Roo.bootstrap.LayoutMasonry} this
30434          * @param {Roo.EventObject} e
30435          */
30436         "layout" : true
30437     });
30438     
30439 };
30440
30441 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30442     
30443     /**
30444      * @cfg {Boolean} isLayoutInstant = no animation?
30445      */   
30446     isLayoutInstant : false, // needed?
30447    
30448     /**
30449      * @cfg {Number} boxWidth  width of the columns
30450      */   
30451     boxWidth : 450,
30452     
30453       /**
30454      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30455      */   
30456     boxHeight : 0,
30457     
30458     /**
30459      * @cfg {Number} padWidth padding below box..
30460      */   
30461     padWidth : 10, 
30462     
30463     /**
30464      * @cfg {Number} gutter gutter width..
30465      */   
30466     gutter : 10,
30467     
30468      /**
30469      * @cfg {Number} maxCols maximum number of columns
30470      */   
30471     
30472     maxCols: 0,
30473     
30474     /**
30475      * @cfg {Boolean} isAutoInitial defalut true
30476      */   
30477     isAutoInitial : true, 
30478     
30479     containerWidth: 0,
30480     
30481     /**
30482      * @cfg {Boolean} isHorizontal defalut false
30483      */   
30484     isHorizontal : false, 
30485
30486     currentSize : null,
30487     
30488     tag: 'div',
30489     
30490     cls: '',
30491     
30492     bricks: null, //CompositeElement
30493     
30494     cols : 1,
30495     
30496     _isLayoutInited : false,
30497     
30498 //    isAlternative : false, // only use for vertical layout...
30499     
30500     /**
30501      * @cfg {Number} alternativePadWidth padding below box..
30502      */   
30503     alternativePadWidth : 50,
30504     
30505     selectedBrick : [],
30506     
30507     getAutoCreate : function(){
30508         
30509         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30510         
30511         var cfg = {
30512             tag: this.tag,
30513             cls: 'blog-masonary-wrapper ' + this.cls,
30514             cn : {
30515                 cls : 'mas-boxes masonary'
30516             }
30517         };
30518         
30519         return cfg;
30520     },
30521     
30522     getChildContainer: function( )
30523     {
30524         if (this.boxesEl) {
30525             return this.boxesEl;
30526         }
30527         
30528         this.boxesEl = this.el.select('.mas-boxes').first();
30529         
30530         return this.boxesEl;
30531     },
30532     
30533     
30534     initEvents : function()
30535     {
30536         var _this = this;
30537         
30538         if(this.isAutoInitial){
30539             Roo.log('hook children rendered');
30540             this.on('childrenrendered', function() {
30541                 Roo.log('children rendered');
30542                 _this.initial();
30543             } ,this);
30544         }
30545     },
30546     
30547     initial : function()
30548     {
30549         this.selectedBrick = [];
30550         
30551         this.currentSize = this.el.getBox(true);
30552         
30553         Roo.EventManager.onWindowResize(this.resize, this); 
30554
30555         if(!this.isAutoInitial){
30556             this.layout();
30557             return;
30558         }
30559         
30560         this.layout();
30561         
30562         return;
30563         //this.layout.defer(500,this);
30564         
30565     },
30566     
30567     resize : function()
30568     {
30569         var cs = this.el.getBox(true);
30570         
30571         if (
30572                 this.currentSize.width == cs.width && 
30573                 this.currentSize.x == cs.x && 
30574                 this.currentSize.height == cs.height && 
30575                 this.currentSize.y == cs.y 
30576         ) {
30577             Roo.log("no change in with or X or Y");
30578             return;
30579         }
30580         
30581         this.currentSize = cs;
30582         
30583         this.layout();
30584         
30585     },
30586     
30587     layout : function()
30588     {   
30589         this._resetLayout();
30590         
30591         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30592         
30593         this.layoutItems( isInstant );
30594       
30595         this._isLayoutInited = true;
30596         
30597         this.fireEvent('layout', this);
30598         
30599     },
30600     
30601     _resetLayout : function()
30602     {
30603         if(this.isHorizontal){
30604             this.horizontalMeasureColumns();
30605             return;
30606         }
30607         
30608         this.verticalMeasureColumns();
30609         
30610     },
30611     
30612     verticalMeasureColumns : function()
30613     {
30614         this.getContainerWidth();
30615         
30616 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30617 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30618 //            return;
30619 //        }
30620         
30621         var boxWidth = this.boxWidth + this.padWidth;
30622         
30623         if(this.containerWidth < this.boxWidth){
30624             boxWidth = this.containerWidth
30625         }
30626         
30627         var containerWidth = this.containerWidth;
30628         
30629         var cols = Math.floor(containerWidth / boxWidth);
30630         
30631         this.cols = Math.max( cols, 1 );
30632         
30633         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30634         
30635         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30636         
30637         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30638         
30639         this.colWidth = boxWidth + avail - this.padWidth;
30640         
30641         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30642         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30643     },
30644     
30645     horizontalMeasureColumns : function()
30646     {
30647         this.getContainerWidth();
30648         
30649         var boxWidth = this.boxWidth;
30650         
30651         if(this.containerWidth < boxWidth){
30652             boxWidth = this.containerWidth;
30653         }
30654         
30655         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30656         
30657         this.el.setHeight(boxWidth);
30658         
30659     },
30660     
30661     getContainerWidth : function()
30662     {
30663         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30664     },
30665     
30666     layoutItems : function( isInstant )
30667     {
30668         Roo.log(this.bricks);
30669         
30670         var items = Roo.apply([], this.bricks);
30671         
30672         if(this.isHorizontal){
30673             this._horizontalLayoutItems( items , isInstant );
30674             return;
30675         }
30676         
30677 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30678 //            this._verticalAlternativeLayoutItems( items , isInstant );
30679 //            return;
30680 //        }
30681         
30682         this._verticalLayoutItems( items , isInstant );
30683         
30684     },
30685     
30686     _verticalLayoutItems : function ( items , isInstant)
30687     {
30688         if ( !items || !items.length ) {
30689             return;
30690         }
30691         
30692         var standard = [
30693             ['xs', 'xs', 'xs', 'tall'],
30694             ['xs', 'xs', 'tall'],
30695             ['xs', 'xs', 'sm'],
30696             ['xs', 'xs', 'xs'],
30697             ['xs', 'tall'],
30698             ['xs', 'sm'],
30699             ['xs', 'xs'],
30700             ['xs'],
30701             
30702             ['sm', 'xs', 'xs'],
30703             ['sm', 'xs'],
30704             ['sm'],
30705             
30706             ['tall', 'xs', 'xs', 'xs'],
30707             ['tall', 'xs', 'xs'],
30708             ['tall', 'xs'],
30709             ['tall']
30710             
30711         ];
30712         
30713         var queue = [];
30714         
30715         var boxes = [];
30716         
30717         var box = [];
30718         
30719         Roo.each(items, function(item, k){
30720             
30721             switch (item.size) {
30722                 // these layouts take up a full box,
30723                 case 'md' :
30724                 case 'md-left' :
30725                 case 'md-right' :
30726                 case 'wide' :
30727                     
30728                     if(box.length){
30729                         boxes.push(box);
30730                         box = [];
30731                     }
30732                     
30733                     boxes.push([item]);
30734                     
30735                     break;
30736                     
30737                 case 'xs' :
30738                 case 'sm' :
30739                 case 'tall' :
30740                     
30741                     box.push(item);
30742                     
30743                     break;
30744                 default :
30745                     break;
30746                     
30747             }
30748             
30749         }, this);
30750         
30751         if(box.length){
30752             boxes.push(box);
30753             box = [];
30754         }
30755         
30756         var filterPattern = function(box, length)
30757         {
30758             if(!box.length){
30759                 return;
30760             }
30761             
30762             var match = false;
30763             
30764             var pattern = box.slice(0, length);
30765             
30766             var format = [];
30767             
30768             Roo.each(pattern, function(i){
30769                 format.push(i.size);
30770             }, this);
30771             
30772             Roo.each(standard, function(s){
30773                 
30774                 if(String(s) != String(format)){
30775                     return;
30776                 }
30777                 
30778                 match = true;
30779                 return false;
30780                 
30781             }, this);
30782             
30783             if(!match && length == 1){
30784                 return;
30785             }
30786             
30787             if(!match){
30788                 filterPattern(box, length - 1);
30789                 return;
30790             }
30791                 
30792             queue.push(pattern);
30793
30794             box = box.slice(length, box.length);
30795
30796             filterPattern(box, 4);
30797
30798             return;
30799             
30800         }
30801         
30802         Roo.each(boxes, function(box, k){
30803             
30804             if(!box.length){
30805                 return;
30806             }
30807             
30808             if(box.length == 1){
30809                 queue.push(box);
30810                 return;
30811             }
30812             
30813             filterPattern(box, 4);
30814             
30815         }, this);
30816         
30817         this._processVerticalLayoutQueue( queue, isInstant );
30818         
30819     },
30820     
30821 //    _verticalAlternativeLayoutItems : function( items , isInstant )
30822 //    {
30823 //        if ( !items || !items.length ) {
30824 //            return;
30825 //        }
30826 //
30827 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
30828 //        
30829 //    },
30830     
30831     _horizontalLayoutItems : function ( items , isInstant)
30832     {
30833         if ( !items || !items.length || items.length < 3) {
30834             return;
30835         }
30836         
30837         items.reverse();
30838         
30839         var eItems = items.slice(0, 3);
30840         
30841         items = items.slice(3, items.length);
30842         
30843         var standard = [
30844             ['xs', 'xs', 'xs', 'wide'],
30845             ['xs', 'xs', 'wide'],
30846             ['xs', 'xs', 'sm'],
30847             ['xs', 'xs', 'xs'],
30848             ['xs', 'wide'],
30849             ['xs', 'sm'],
30850             ['xs', 'xs'],
30851             ['xs'],
30852             
30853             ['sm', 'xs', 'xs'],
30854             ['sm', 'xs'],
30855             ['sm'],
30856             
30857             ['wide', 'xs', 'xs', 'xs'],
30858             ['wide', 'xs', 'xs'],
30859             ['wide', 'xs'],
30860             ['wide'],
30861             
30862             ['wide-thin']
30863         ];
30864         
30865         var queue = [];
30866         
30867         var boxes = [];
30868         
30869         var box = [];
30870         
30871         Roo.each(items, function(item, k){
30872             
30873             switch (item.size) {
30874                 case 'md' :
30875                 case 'md-left' :
30876                 case 'md-right' :
30877                 case 'tall' :
30878                     
30879                     if(box.length){
30880                         boxes.push(box);
30881                         box = [];
30882                     }
30883                     
30884                     boxes.push([item]);
30885                     
30886                     break;
30887                     
30888                 case 'xs' :
30889                 case 'sm' :
30890                 case 'wide' :
30891                 case 'wide-thin' :
30892                     
30893                     box.push(item);
30894                     
30895                     break;
30896                 default :
30897                     break;
30898                     
30899             }
30900             
30901         }, this);
30902         
30903         if(box.length){
30904             boxes.push(box);
30905             box = [];
30906         }
30907         
30908         var filterPattern = function(box, length)
30909         {
30910             if(!box.length){
30911                 return;
30912             }
30913             
30914             var match = false;
30915             
30916             var pattern = box.slice(0, length);
30917             
30918             var format = [];
30919             
30920             Roo.each(pattern, function(i){
30921                 format.push(i.size);
30922             }, this);
30923             
30924             Roo.each(standard, function(s){
30925                 
30926                 if(String(s) != String(format)){
30927                     return;
30928                 }
30929                 
30930                 match = true;
30931                 return false;
30932                 
30933             }, this);
30934             
30935             if(!match && length == 1){
30936                 return;
30937             }
30938             
30939             if(!match){
30940                 filterPattern(box, length - 1);
30941                 return;
30942             }
30943                 
30944             queue.push(pattern);
30945
30946             box = box.slice(length, box.length);
30947
30948             filterPattern(box, 4);
30949
30950             return;
30951             
30952         }
30953         
30954         Roo.each(boxes, function(box, k){
30955             
30956             if(!box.length){
30957                 return;
30958             }
30959             
30960             if(box.length == 1){
30961                 queue.push(box);
30962                 return;
30963             }
30964             
30965             filterPattern(box, 4);
30966             
30967         }, this);
30968         
30969         
30970         var prune = [];
30971         
30972         var pos = this.el.getBox(true);
30973         
30974         var minX = pos.x;
30975         
30976         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30977         
30978         var hit_end = false;
30979         
30980         Roo.each(queue, function(box){
30981             
30982             if(hit_end){
30983                 
30984                 Roo.each(box, function(b){
30985                 
30986                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30987                     b.el.hide();
30988
30989                 }, this);
30990
30991                 return;
30992             }
30993             
30994             var mx = 0;
30995             
30996             Roo.each(box, function(b){
30997                 
30998                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
30999                 b.el.show();
31000
31001                 mx = Math.max(mx, b.x);
31002                 
31003             }, this);
31004             
31005             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
31006             
31007             if(maxX < minX){
31008                 
31009                 Roo.each(box, function(b){
31010                 
31011                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
31012                     b.el.hide();
31013                     
31014                 }, this);
31015                 
31016                 hit_end = true;
31017                 
31018                 return;
31019             }
31020             
31021             prune.push(box);
31022             
31023         }, this);
31024         
31025         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31026     },
31027     
31028     /** Sets position of item in DOM
31029     * @param {Element} item
31030     * @param {Number} x - horizontal position
31031     * @param {Number} y - vertical position
31032     * @param {Boolean} isInstant - disables transitions
31033     */
31034     _processVerticalLayoutQueue : function( queue, isInstant )
31035     {
31036         var pos = this.el.getBox(true);
31037         var x = pos.x;
31038         var y = pos.y;
31039         var maxY = [];
31040         
31041         for (var i = 0; i < this.cols; i++){
31042             maxY[i] = pos.y;
31043         }
31044         
31045         Roo.each(queue, function(box, k){
31046             
31047             var col = k % this.cols;
31048             
31049             Roo.each(box, function(b,kk){
31050                 
31051                 b.el.position('absolute');
31052                 
31053                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31054                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31055                 
31056                 if(b.size == 'md-left' || b.size == 'md-right'){
31057                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31058                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31059                 }
31060                 
31061                 b.el.setWidth(width);
31062                 b.el.setHeight(height);
31063                 // iframe?
31064                 b.el.select('iframe',true).setSize(width,height);
31065                 
31066             }, this);
31067             
31068             for (var i = 0; i < this.cols; i++){
31069                 
31070                 if(maxY[i] < maxY[col]){
31071                     col = i;
31072                     continue;
31073                 }
31074                 
31075                 col = Math.min(col, i);
31076                 
31077             }
31078             
31079             x = pos.x + col * (this.colWidth + this.padWidth);
31080             
31081             y = maxY[col];
31082             
31083             var positions = [];
31084             
31085             switch (box.length){
31086                 case 1 :
31087                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31088                     break;
31089                 case 2 :
31090                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31091                     break;
31092                 case 3 :
31093                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31094                     break;
31095                 case 4 :
31096                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31097                     break;
31098                 default :
31099                     break;
31100             }
31101             
31102             Roo.each(box, function(b,kk){
31103                 
31104                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31105                 
31106                 var sz = b.el.getSize();
31107                 
31108                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31109                 
31110             }, this);
31111             
31112         }, this);
31113         
31114         var mY = 0;
31115         
31116         for (var i = 0; i < this.cols; i++){
31117             mY = Math.max(mY, maxY[i]);
31118         }
31119         
31120         this.el.setHeight(mY - pos.y);
31121         
31122     },
31123     
31124 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31125 //    {
31126 //        var pos = this.el.getBox(true);
31127 //        var x = pos.x;
31128 //        var y = pos.y;
31129 //        var maxX = pos.right;
31130 //        
31131 //        var maxHeight = 0;
31132 //        
31133 //        Roo.each(items, function(item, k){
31134 //            
31135 //            var c = k % 2;
31136 //            
31137 //            item.el.position('absolute');
31138 //                
31139 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31140 //
31141 //            item.el.setWidth(width);
31142 //
31143 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31144 //
31145 //            item.el.setHeight(height);
31146 //            
31147 //            if(c == 0){
31148 //                item.el.setXY([x, y], isInstant ? false : true);
31149 //            } else {
31150 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31151 //            }
31152 //            
31153 //            y = y + height + this.alternativePadWidth;
31154 //            
31155 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31156 //            
31157 //        }, this);
31158 //        
31159 //        this.el.setHeight(maxHeight);
31160 //        
31161 //    },
31162     
31163     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31164     {
31165         var pos = this.el.getBox(true);
31166         
31167         var minX = pos.x;
31168         var minY = pos.y;
31169         
31170         var maxX = pos.right;
31171         
31172         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31173         
31174         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31175         
31176         Roo.each(queue, function(box, k){
31177             
31178             Roo.each(box, function(b, kk){
31179                 
31180                 b.el.position('absolute');
31181                 
31182                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31183                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31184                 
31185                 if(b.size == 'md-left' || b.size == 'md-right'){
31186                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31187                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31188                 }
31189                 
31190                 b.el.setWidth(width);
31191                 b.el.setHeight(height);
31192                 
31193             }, this);
31194             
31195             if(!box.length){
31196                 return;
31197             }
31198             
31199             var positions = [];
31200             
31201             switch (box.length){
31202                 case 1 :
31203                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31204                     break;
31205                 case 2 :
31206                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31207                     break;
31208                 case 3 :
31209                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31210                     break;
31211                 case 4 :
31212                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31213                     break;
31214                 default :
31215                     break;
31216             }
31217             
31218             Roo.each(box, function(b,kk){
31219                 
31220                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31221                 
31222                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31223                 
31224             }, this);
31225             
31226         }, this);
31227         
31228     },
31229     
31230     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31231     {
31232         Roo.each(eItems, function(b,k){
31233             
31234             b.size = (k == 0) ? 'sm' : 'xs';
31235             b.x = (k == 0) ? 2 : 1;
31236             b.y = (k == 0) ? 2 : 1;
31237             
31238             b.el.position('absolute');
31239             
31240             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31241                 
31242             b.el.setWidth(width);
31243             
31244             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31245             
31246             b.el.setHeight(height);
31247             
31248         }, this);
31249
31250         var positions = [];
31251         
31252         positions.push({
31253             x : maxX - this.unitWidth * 2 - this.gutter,
31254             y : minY
31255         });
31256         
31257         positions.push({
31258             x : maxX - this.unitWidth,
31259             y : minY + (this.unitWidth + this.gutter) * 2
31260         });
31261         
31262         positions.push({
31263             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31264             y : minY
31265         });
31266         
31267         Roo.each(eItems, function(b,k){
31268             
31269             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31270
31271         }, this);
31272         
31273     },
31274     
31275     getVerticalOneBoxColPositions : function(x, y, box)
31276     {
31277         var pos = [];
31278         
31279         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31280         
31281         if(box[0].size == 'md-left'){
31282             rand = 0;
31283         }
31284         
31285         if(box[0].size == 'md-right'){
31286             rand = 1;
31287         }
31288         
31289         pos.push({
31290             x : x + (this.unitWidth + this.gutter) * rand,
31291             y : y
31292         });
31293         
31294         return pos;
31295     },
31296     
31297     getVerticalTwoBoxColPositions : function(x, y, box)
31298     {
31299         var pos = [];
31300         
31301         if(box[0].size == 'xs'){
31302             
31303             pos.push({
31304                 x : x,
31305                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31306             });
31307
31308             pos.push({
31309                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31310                 y : y
31311             });
31312             
31313             return pos;
31314             
31315         }
31316         
31317         pos.push({
31318             x : x,
31319             y : y
31320         });
31321
31322         pos.push({
31323             x : x + (this.unitWidth + this.gutter) * 2,
31324             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31325         });
31326         
31327         return pos;
31328         
31329     },
31330     
31331     getVerticalThreeBoxColPositions : function(x, y, box)
31332     {
31333         var pos = [];
31334         
31335         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31336             
31337             pos.push({
31338                 x : x,
31339                 y : y
31340             });
31341
31342             pos.push({
31343                 x : x + (this.unitWidth + this.gutter) * 1,
31344                 y : y
31345             });
31346             
31347             pos.push({
31348                 x : x + (this.unitWidth + this.gutter) * 2,
31349                 y : y
31350             });
31351             
31352             return pos;
31353             
31354         }
31355         
31356         if(box[0].size == 'xs' && box[1].size == 'xs'){
31357             
31358             pos.push({
31359                 x : x,
31360                 y : y
31361             });
31362
31363             pos.push({
31364                 x : x,
31365                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31366             });
31367             
31368             pos.push({
31369                 x : x + (this.unitWidth + this.gutter) * 1,
31370                 y : y
31371             });
31372             
31373             return pos;
31374             
31375         }
31376         
31377         pos.push({
31378             x : x,
31379             y : y
31380         });
31381
31382         pos.push({
31383             x : x + (this.unitWidth + this.gutter) * 2,
31384             y : y
31385         });
31386
31387         pos.push({
31388             x : x + (this.unitWidth + this.gutter) * 2,
31389             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31390         });
31391             
31392         return pos;
31393         
31394     },
31395     
31396     getVerticalFourBoxColPositions : function(x, y, box)
31397     {
31398         var pos = [];
31399         
31400         if(box[0].size == 'xs'){
31401             
31402             pos.push({
31403                 x : x,
31404                 y : y
31405             });
31406
31407             pos.push({
31408                 x : x,
31409                 y : y + (this.unitHeight + this.gutter) * 1
31410             });
31411             
31412             pos.push({
31413                 x : x,
31414                 y : y + (this.unitHeight + this.gutter) * 2
31415             });
31416             
31417             pos.push({
31418                 x : x + (this.unitWidth + this.gutter) * 1,
31419                 y : y
31420             });
31421             
31422             return pos;
31423             
31424         }
31425         
31426         pos.push({
31427             x : x,
31428             y : y
31429         });
31430
31431         pos.push({
31432             x : x + (this.unitWidth + this.gutter) * 2,
31433             y : y
31434         });
31435
31436         pos.push({
31437             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31438             y : y + (this.unitHeight + this.gutter) * 1
31439         });
31440
31441         pos.push({
31442             x : x + (this.unitWidth + this.gutter) * 2,
31443             y : y + (this.unitWidth + this.gutter) * 2
31444         });
31445
31446         return pos;
31447         
31448     },
31449     
31450     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31451     {
31452         var pos = [];
31453         
31454         if(box[0].size == 'md-left'){
31455             pos.push({
31456                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31457                 y : minY
31458             });
31459             
31460             return pos;
31461         }
31462         
31463         if(box[0].size == 'md-right'){
31464             pos.push({
31465                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31466                 y : minY + (this.unitWidth + this.gutter) * 1
31467             });
31468             
31469             return pos;
31470         }
31471         
31472         var rand = Math.floor(Math.random() * (4 - box[0].y));
31473         
31474         pos.push({
31475             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31476             y : minY + (this.unitWidth + this.gutter) * rand
31477         });
31478         
31479         return pos;
31480         
31481     },
31482     
31483     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31484     {
31485         var pos = [];
31486         
31487         if(box[0].size == 'xs'){
31488             
31489             pos.push({
31490                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31491                 y : minY
31492             });
31493
31494             pos.push({
31495                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31496                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31497             });
31498             
31499             return pos;
31500             
31501         }
31502         
31503         pos.push({
31504             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31505             y : minY
31506         });
31507
31508         pos.push({
31509             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31510             y : minY + (this.unitWidth + this.gutter) * 2
31511         });
31512         
31513         return pos;
31514         
31515     },
31516     
31517     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31518     {
31519         var pos = [];
31520         
31521         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31522             
31523             pos.push({
31524                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31525                 y : minY
31526             });
31527
31528             pos.push({
31529                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31530                 y : minY + (this.unitWidth + this.gutter) * 1
31531             });
31532             
31533             pos.push({
31534                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31535                 y : minY + (this.unitWidth + this.gutter) * 2
31536             });
31537             
31538             return pos;
31539             
31540         }
31541         
31542         if(box[0].size == 'xs' && box[1].size == 'xs'){
31543             
31544             pos.push({
31545                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31546                 y : minY
31547             });
31548
31549             pos.push({
31550                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31551                 y : minY
31552             });
31553             
31554             pos.push({
31555                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31556                 y : minY + (this.unitWidth + this.gutter) * 1
31557             });
31558             
31559             return pos;
31560             
31561         }
31562         
31563         pos.push({
31564             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31565             y : minY
31566         });
31567
31568         pos.push({
31569             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31570             y : minY + (this.unitWidth + this.gutter) * 2
31571         });
31572
31573         pos.push({
31574             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31575             y : minY + (this.unitWidth + this.gutter) * 2
31576         });
31577             
31578         return pos;
31579         
31580     },
31581     
31582     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31583     {
31584         var pos = [];
31585         
31586         if(box[0].size == 'xs'){
31587             
31588             pos.push({
31589                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31590                 y : minY
31591             });
31592
31593             pos.push({
31594                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31595                 y : minY
31596             });
31597             
31598             pos.push({
31599                 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),
31600                 y : minY
31601             });
31602             
31603             pos.push({
31604                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31605                 y : minY + (this.unitWidth + this.gutter) * 1
31606             });
31607             
31608             return pos;
31609             
31610         }
31611         
31612         pos.push({
31613             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31614             y : minY
31615         });
31616         
31617         pos.push({
31618             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31619             y : minY + (this.unitWidth + this.gutter) * 2
31620         });
31621         
31622         pos.push({
31623             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31624             y : minY + (this.unitWidth + this.gutter) * 2
31625         });
31626         
31627         pos.push({
31628             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),
31629             y : minY + (this.unitWidth + this.gutter) * 2
31630         });
31631
31632         return pos;
31633         
31634     },
31635     
31636     /**
31637     * remove a Masonry Brick
31638     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31639     */
31640     removeBrick : function(brick_id)
31641     {
31642         if (!brick_id) {
31643             return;
31644         }
31645         
31646         for (var i = 0; i<this.bricks.length; i++) {
31647             if (this.bricks[i].id == brick_id) {
31648                 this.bricks.splice(i,1);
31649                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31650                 this.initial();
31651             }
31652         }
31653     },
31654     
31655     /**
31656     * adds a Masonry Brick
31657     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31658     */
31659     addBrick : function(cfg)
31660     {
31661         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31662         //this.register(cn);
31663         cn.parentId = this.id;
31664         cn.onRender(this.el, null);
31665         return cn;
31666     },
31667     
31668     /**
31669     * register a Masonry Brick
31670     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31671     */
31672     
31673     register : function(brick)
31674     {
31675         this.bricks.push(brick);
31676         brick.masonryId = this.id;
31677     },
31678     
31679     /**
31680     * clear all the Masonry Brick
31681     */
31682     clearAll : function()
31683     {
31684         this.bricks = [];
31685         //this.getChildContainer().dom.innerHTML = "";
31686         this.el.dom.innerHTML = '';
31687     },
31688     
31689     getSelected : function()
31690     {
31691         if (!this.selectedBrick) {
31692             return false;
31693         }
31694         
31695         return this.selectedBrick;
31696     }
31697 });
31698
31699 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31700     
31701     groups: {},
31702      /**
31703     * register a Masonry Layout
31704     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31705     */
31706     
31707     register : function(layout)
31708     {
31709         this.groups[layout.id] = layout;
31710     },
31711     /**
31712     * fetch a  Masonry Layout based on the masonry layout ID
31713     * @param {string} the masonry layout to add
31714     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31715     */
31716     
31717     get: function(layout_id) {
31718         if (typeof(this.groups[layout_id]) == 'undefined') {
31719             return false;
31720         }
31721         return this.groups[layout_id] ;
31722     }
31723     
31724     
31725     
31726 });
31727
31728  
31729
31730  /**
31731  *
31732  * This is based on 
31733  * http://masonry.desandro.com
31734  *
31735  * The idea is to render all the bricks based on vertical width...
31736  *
31737  * The original code extends 'outlayer' - we might need to use that....
31738  * 
31739  */
31740
31741
31742 /**
31743  * @class Roo.bootstrap.LayoutMasonryAuto
31744  * @extends Roo.bootstrap.Component
31745  * Bootstrap Layout Masonry class
31746  * 
31747  * @constructor
31748  * Create a new Element
31749  * @param {Object} config The config object
31750  */
31751
31752 Roo.bootstrap.LayoutMasonryAuto = function(config){
31753     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31754 };
31755
31756 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
31757     
31758       /**
31759      * @cfg {Boolean} isFitWidth  - resize the width..
31760      */   
31761     isFitWidth : false,  // options..
31762     /**
31763      * @cfg {Boolean} isOriginLeft = left align?
31764      */   
31765     isOriginLeft : true,
31766     /**
31767      * @cfg {Boolean} isOriginTop = top align?
31768      */   
31769     isOriginTop : false,
31770     /**
31771      * @cfg {Boolean} isLayoutInstant = no animation?
31772      */   
31773     isLayoutInstant : false, // needed?
31774     /**
31775      * @cfg {Boolean} isResizingContainer = not sure if this is used..
31776      */   
31777     isResizingContainer : true,
31778     /**
31779      * @cfg {Number} columnWidth  width of the columns 
31780      */   
31781     
31782     columnWidth : 0,
31783     
31784     /**
31785      * @cfg {Number} maxCols maximum number of columns
31786      */   
31787     
31788     maxCols: 0,
31789     /**
31790      * @cfg {Number} padHeight padding below box..
31791      */   
31792     
31793     padHeight : 10, 
31794     
31795     /**
31796      * @cfg {Boolean} isAutoInitial defalut true
31797      */   
31798     
31799     isAutoInitial : true, 
31800     
31801     // private?
31802     gutter : 0,
31803     
31804     containerWidth: 0,
31805     initialColumnWidth : 0,
31806     currentSize : null,
31807     
31808     colYs : null, // array.
31809     maxY : 0,
31810     padWidth: 10,
31811     
31812     
31813     tag: 'div',
31814     cls: '',
31815     bricks: null, //CompositeElement
31816     cols : 0, // array?
31817     // element : null, // wrapped now this.el
31818     _isLayoutInited : null, 
31819     
31820     
31821     getAutoCreate : function(){
31822         
31823         var cfg = {
31824             tag: this.tag,
31825             cls: 'blog-masonary-wrapper ' + this.cls,
31826             cn : {
31827                 cls : 'mas-boxes masonary'
31828             }
31829         };
31830         
31831         return cfg;
31832     },
31833     
31834     getChildContainer: function( )
31835     {
31836         if (this.boxesEl) {
31837             return this.boxesEl;
31838         }
31839         
31840         this.boxesEl = this.el.select('.mas-boxes').first();
31841         
31842         return this.boxesEl;
31843     },
31844     
31845     
31846     initEvents : function()
31847     {
31848         var _this = this;
31849         
31850         if(this.isAutoInitial){
31851             Roo.log('hook children rendered');
31852             this.on('childrenrendered', function() {
31853                 Roo.log('children rendered');
31854                 _this.initial();
31855             } ,this);
31856         }
31857         
31858     },
31859     
31860     initial : function()
31861     {
31862         this.reloadItems();
31863
31864         this.currentSize = this.el.getBox(true);
31865
31866         /// was window resize... - let's see if this works..
31867         Roo.EventManager.onWindowResize(this.resize, this); 
31868
31869         if(!this.isAutoInitial){
31870             this.layout();
31871             return;
31872         }
31873         
31874         this.layout.defer(500,this);
31875     },
31876     
31877     reloadItems: function()
31878     {
31879         this.bricks = this.el.select('.masonry-brick', true);
31880         
31881         this.bricks.each(function(b) {
31882             //Roo.log(b.getSize());
31883             if (!b.attr('originalwidth')) {
31884                 b.attr('originalwidth',  b.getSize().width);
31885             }
31886             
31887         });
31888         
31889         Roo.log(this.bricks.elements.length);
31890     },
31891     
31892     resize : function()
31893     {
31894         Roo.log('resize');
31895         var cs = this.el.getBox(true);
31896         
31897         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
31898             Roo.log("no change in with or X");
31899             return;
31900         }
31901         this.currentSize = cs;
31902         this.layout();
31903     },
31904     
31905     layout : function()
31906     {
31907          Roo.log('layout');
31908         this._resetLayout();
31909         //this._manageStamps();
31910       
31911         // don't animate first layout
31912         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31913         this.layoutItems( isInstant );
31914       
31915         // flag for initalized
31916         this._isLayoutInited = true;
31917     },
31918     
31919     layoutItems : function( isInstant )
31920     {
31921         //var items = this._getItemsForLayout( this.items );
31922         // original code supports filtering layout items.. we just ignore it..
31923         
31924         this._layoutItems( this.bricks , isInstant );
31925       
31926         this._postLayout();
31927     },
31928     _layoutItems : function ( items , isInstant)
31929     {
31930        //this.fireEvent( 'layout', this, items );
31931     
31932
31933         if ( !items || !items.elements.length ) {
31934           // no items, emit event with empty array
31935             return;
31936         }
31937
31938         var queue = [];
31939         items.each(function(item) {
31940             Roo.log("layout item");
31941             Roo.log(item);
31942             // get x/y object from method
31943             var position = this._getItemLayoutPosition( item );
31944             // enqueue
31945             position.item = item;
31946             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
31947             queue.push( position );
31948         }, this);
31949       
31950         this._processLayoutQueue( queue );
31951     },
31952     /** Sets position of item in DOM
31953     * @param {Element} item
31954     * @param {Number} x - horizontal position
31955     * @param {Number} y - vertical position
31956     * @param {Boolean} isInstant - disables transitions
31957     */
31958     _processLayoutQueue : function( queue )
31959     {
31960         for ( var i=0, len = queue.length; i < len; i++ ) {
31961             var obj = queue[i];
31962             obj.item.position('absolute');
31963             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
31964         }
31965     },
31966       
31967     
31968     /**
31969     * Any logic you want to do after each layout,
31970     * i.e. size the container
31971     */
31972     _postLayout : function()
31973     {
31974         this.resizeContainer();
31975     },
31976     
31977     resizeContainer : function()
31978     {
31979         if ( !this.isResizingContainer ) {
31980             return;
31981         }
31982         var size = this._getContainerSize();
31983         if ( size ) {
31984             this.el.setSize(size.width,size.height);
31985             this.boxesEl.setSize(size.width,size.height);
31986         }
31987     },
31988     
31989     
31990     
31991     _resetLayout : function()
31992     {
31993         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
31994         this.colWidth = this.el.getWidth();
31995         //this.gutter = this.el.getWidth(); 
31996         
31997         this.measureColumns();
31998
31999         // reset column Y
32000         var i = this.cols;
32001         this.colYs = [];
32002         while (i--) {
32003             this.colYs.push( 0 );
32004         }
32005     
32006         this.maxY = 0;
32007     },
32008
32009     measureColumns : function()
32010     {
32011         this.getContainerWidth();
32012       // if columnWidth is 0, default to outerWidth of first item
32013         if ( !this.columnWidth ) {
32014             var firstItem = this.bricks.first();
32015             Roo.log(firstItem);
32016             this.columnWidth  = this.containerWidth;
32017             if (firstItem && firstItem.attr('originalwidth') ) {
32018                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32019             }
32020             // columnWidth fall back to item of first element
32021             Roo.log("set column width?");
32022                         this.initialColumnWidth = this.columnWidth  ;
32023
32024             // if first elem has no width, default to size of container
32025             
32026         }
32027         
32028         
32029         if (this.initialColumnWidth) {
32030             this.columnWidth = this.initialColumnWidth;
32031         }
32032         
32033         
32034             
32035         // column width is fixed at the top - however if container width get's smaller we should
32036         // reduce it...
32037         
32038         // this bit calcs how man columns..
32039             
32040         var columnWidth = this.columnWidth += this.gutter;
32041       
32042         // calculate columns
32043         var containerWidth = this.containerWidth + this.gutter;
32044         
32045         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32046         // fix rounding errors, typically with gutters
32047         var excess = columnWidth - containerWidth % columnWidth;
32048         
32049         
32050         // if overshoot is less than a pixel, round up, otherwise floor it
32051         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32052         cols = Math[ mathMethod ]( cols );
32053         this.cols = Math.max( cols, 1 );
32054         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32055         
32056          // padding positioning..
32057         var totalColWidth = this.cols * this.columnWidth;
32058         var padavail = this.containerWidth - totalColWidth;
32059         // so for 2 columns - we need 3 'pads'
32060         
32061         var padNeeded = (1+this.cols) * this.padWidth;
32062         
32063         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32064         
32065         this.columnWidth += padExtra
32066         //this.padWidth = Math.floor(padavail /  ( this.cols));
32067         
32068         // adjust colum width so that padding is fixed??
32069         
32070         // we have 3 columns ... total = width * 3
32071         // we have X left over... that should be used by 
32072         
32073         //if (this.expandC) {
32074             
32075         //}
32076         
32077         
32078         
32079     },
32080     
32081     getContainerWidth : function()
32082     {
32083        /* // container is parent if fit width
32084         var container = this.isFitWidth ? this.element.parentNode : this.element;
32085         // check that this.size and size are there
32086         // IE8 triggers resize on body size change, so they might not be
32087         
32088         var size = getSize( container );  //FIXME
32089         this.containerWidth = size && size.innerWidth; //FIXME
32090         */
32091          
32092         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32093         
32094     },
32095     
32096     _getItemLayoutPosition : function( item )  // what is item?
32097     {
32098         // we resize the item to our columnWidth..
32099       
32100         item.setWidth(this.columnWidth);
32101         item.autoBoxAdjust  = false;
32102         
32103         var sz = item.getSize();
32104  
32105         // how many columns does this brick span
32106         var remainder = this.containerWidth % this.columnWidth;
32107         
32108         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32109         // round if off by 1 pixel, otherwise use ceil
32110         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32111         colSpan = Math.min( colSpan, this.cols );
32112         
32113         // normally this should be '1' as we dont' currently allow multi width columns..
32114         
32115         var colGroup = this._getColGroup( colSpan );
32116         // get the minimum Y value from the columns
32117         var minimumY = Math.min.apply( Math, colGroup );
32118         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32119         
32120         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32121          
32122         // position the brick
32123         var position = {
32124             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32125             y: this.currentSize.y + minimumY + this.padHeight
32126         };
32127         
32128         Roo.log(position);
32129         // apply setHeight to necessary columns
32130         var setHeight = minimumY + sz.height + this.padHeight;
32131         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32132         
32133         var setSpan = this.cols + 1 - colGroup.length;
32134         for ( var i = 0; i < setSpan; i++ ) {
32135           this.colYs[ shortColIndex + i ] = setHeight ;
32136         }
32137       
32138         return position;
32139     },
32140     
32141     /**
32142      * @param {Number} colSpan - number of columns the element spans
32143      * @returns {Array} colGroup
32144      */
32145     _getColGroup : function( colSpan )
32146     {
32147         if ( colSpan < 2 ) {
32148           // if brick spans only one column, use all the column Ys
32149           return this.colYs;
32150         }
32151       
32152         var colGroup = [];
32153         // how many different places could this brick fit horizontally
32154         var groupCount = this.cols + 1 - colSpan;
32155         // for each group potential horizontal position
32156         for ( var i = 0; i < groupCount; i++ ) {
32157           // make an array of colY values for that one group
32158           var groupColYs = this.colYs.slice( i, i + colSpan );
32159           // and get the max value of the array
32160           colGroup[i] = Math.max.apply( Math, groupColYs );
32161         }
32162         return colGroup;
32163     },
32164     /*
32165     _manageStamp : function( stamp )
32166     {
32167         var stampSize =  stamp.getSize();
32168         var offset = stamp.getBox();
32169         // get the columns that this stamp affects
32170         var firstX = this.isOriginLeft ? offset.x : offset.right;
32171         var lastX = firstX + stampSize.width;
32172         var firstCol = Math.floor( firstX / this.columnWidth );
32173         firstCol = Math.max( 0, firstCol );
32174         
32175         var lastCol = Math.floor( lastX / this.columnWidth );
32176         // lastCol should not go over if multiple of columnWidth #425
32177         lastCol -= lastX % this.columnWidth ? 0 : 1;
32178         lastCol = Math.min( this.cols - 1, lastCol );
32179         
32180         // set colYs to bottom of the stamp
32181         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32182             stampSize.height;
32183             
32184         for ( var i = firstCol; i <= lastCol; i++ ) {
32185           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32186         }
32187     },
32188     */
32189     
32190     _getContainerSize : function()
32191     {
32192         this.maxY = Math.max.apply( Math, this.colYs );
32193         var size = {
32194             height: this.maxY
32195         };
32196       
32197         if ( this.isFitWidth ) {
32198             size.width = this._getContainerFitWidth();
32199         }
32200       
32201         return size;
32202     },
32203     
32204     _getContainerFitWidth : function()
32205     {
32206         var unusedCols = 0;
32207         // count unused columns
32208         var i = this.cols;
32209         while ( --i ) {
32210           if ( this.colYs[i] !== 0 ) {
32211             break;
32212           }
32213           unusedCols++;
32214         }
32215         // fit container to columns that have been used
32216         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32217     },
32218     
32219     needsResizeLayout : function()
32220     {
32221         var previousWidth = this.containerWidth;
32222         this.getContainerWidth();
32223         return previousWidth !== this.containerWidth;
32224     }
32225  
32226 });
32227
32228  
32229
32230  /*
32231  * - LGPL
32232  *
32233  * element
32234  * 
32235  */
32236
32237 /**
32238  * @class Roo.bootstrap.MasonryBrick
32239  * @extends Roo.bootstrap.Component
32240  * Bootstrap MasonryBrick class
32241  * 
32242  * @constructor
32243  * Create a new MasonryBrick
32244  * @param {Object} config The config object
32245  */
32246
32247 Roo.bootstrap.MasonryBrick = function(config){
32248     
32249     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32250     
32251     Roo.bootstrap.MasonryBrick.register(this);
32252     
32253     this.addEvents({
32254         // raw events
32255         /**
32256          * @event click
32257          * When a MasonryBrick is clcik
32258          * @param {Roo.bootstrap.MasonryBrick} this
32259          * @param {Roo.EventObject} e
32260          */
32261         "click" : true
32262     });
32263 };
32264
32265 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32266     
32267     /**
32268      * @cfg {String} title
32269      */   
32270     title : '',
32271     /**
32272      * @cfg {String} html
32273      */   
32274     html : '',
32275     /**
32276      * @cfg {String} bgimage
32277      */   
32278     bgimage : '',
32279     /**
32280      * @cfg {String} videourl
32281      */   
32282     videourl : '',
32283     /**
32284      * @cfg {String} cls
32285      */   
32286     cls : '',
32287     /**
32288      * @cfg {String} href
32289      */   
32290     href : '',
32291     /**
32292      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32293      */   
32294     size : 'xs',
32295     
32296     /**
32297      * @cfg {String} placetitle (center|bottom)
32298      */   
32299     placetitle : '',
32300     
32301     /**
32302      * @cfg {Boolean} isFitContainer defalut true
32303      */   
32304     isFitContainer : true, 
32305     
32306     /**
32307      * @cfg {Boolean} preventDefault defalut false
32308      */   
32309     preventDefault : false, 
32310     
32311     /**
32312      * @cfg {Boolean} inverse defalut false
32313      */   
32314     maskInverse : false, 
32315     
32316     getAutoCreate : function()
32317     {
32318         if(!this.isFitContainer){
32319             return this.getSplitAutoCreate();
32320         }
32321         
32322         var cls = 'masonry-brick masonry-brick-full';
32323         
32324         if(this.href.length){
32325             cls += ' masonry-brick-link';
32326         }
32327         
32328         if(this.bgimage.length){
32329             cls += ' masonry-brick-image';
32330         }
32331         
32332         if(this.maskInverse){
32333             cls += ' mask-inverse';
32334         }
32335         
32336         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32337             cls += ' enable-mask';
32338         }
32339         
32340         if(this.size){
32341             cls += ' masonry-' + this.size + '-brick';
32342         }
32343         
32344         if(this.placetitle.length){
32345             
32346             switch (this.placetitle) {
32347                 case 'center' :
32348                     cls += ' masonry-center-title';
32349                     break;
32350                 case 'bottom' :
32351                     cls += ' masonry-bottom-title';
32352                     break;
32353                 default:
32354                     break;
32355             }
32356             
32357         } else {
32358             if(!this.html.length && !this.bgimage.length){
32359                 cls += ' masonry-center-title';
32360             }
32361
32362             if(!this.html.length && this.bgimage.length){
32363                 cls += ' masonry-bottom-title';
32364             }
32365         }
32366         
32367         if(this.cls){
32368             cls += ' ' + this.cls;
32369         }
32370         
32371         var cfg = {
32372             tag: (this.href.length) ? 'a' : 'div',
32373             cls: cls,
32374             cn: [
32375                 {
32376                     tag: 'div',
32377                     cls: 'masonry-brick-mask'
32378                 },
32379                 {
32380                     tag: 'div',
32381                     cls: 'masonry-brick-paragraph',
32382                     cn: []
32383                 }
32384             ]
32385         };
32386         
32387         if(this.href.length){
32388             cfg.href = this.href;
32389         }
32390         
32391         var cn = cfg.cn[1].cn;
32392         
32393         if(this.title.length){
32394             cn.push({
32395                 tag: 'h4',
32396                 cls: 'masonry-brick-title',
32397                 html: this.title
32398             });
32399         }
32400         
32401         if(this.html.length){
32402             cn.push({
32403                 tag: 'p',
32404                 cls: 'masonry-brick-text',
32405                 html: this.html
32406             });
32407         }
32408         
32409         if (!this.title.length && !this.html.length) {
32410             cfg.cn[1].cls += ' hide';
32411         }
32412         
32413         if(this.bgimage.length){
32414             cfg.cn.push({
32415                 tag: 'img',
32416                 cls: 'masonry-brick-image-view',
32417                 src: this.bgimage
32418             });
32419         }
32420         
32421         if(this.videourl.length){
32422             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32423             // youtube support only?
32424             cfg.cn.push({
32425                 tag: 'iframe',
32426                 cls: 'masonry-brick-image-view',
32427                 src: vurl,
32428                 frameborder : 0,
32429                 allowfullscreen : true
32430             });
32431         }
32432         
32433         return cfg;
32434         
32435     },
32436     
32437     getSplitAutoCreate : function()
32438     {
32439         var cls = 'masonry-brick masonry-brick-split';
32440         
32441         if(this.href.length){
32442             cls += ' masonry-brick-link';
32443         }
32444         
32445         if(this.bgimage.length){
32446             cls += ' masonry-brick-image';
32447         }
32448         
32449         if(this.size){
32450             cls += ' masonry-' + this.size + '-brick';
32451         }
32452         
32453         switch (this.placetitle) {
32454             case 'center' :
32455                 cls += ' masonry-center-title';
32456                 break;
32457             case 'bottom' :
32458                 cls += ' masonry-bottom-title';
32459                 break;
32460             default:
32461                 if(!this.bgimage.length){
32462                     cls += ' masonry-center-title';
32463                 }
32464
32465                 if(this.bgimage.length){
32466                     cls += ' masonry-bottom-title';
32467                 }
32468                 break;
32469         }
32470         
32471         if(this.cls){
32472             cls += ' ' + this.cls;
32473         }
32474         
32475         var cfg = {
32476             tag: (this.href.length) ? 'a' : 'div',
32477             cls: cls,
32478             cn: [
32479                 {
32480                     tag: 'div',
32481                     cls: 'masonry-brick-split-head',
32482                     cn: [
32483                         {
32484                             tag: 'div',
32485                             cls: 'masonry-brick-paragraph',
32486                             cn: []
32487                         }
32488                     ]
32489                 },
32490                 {
32491                     tag: 'div',
32492                     cls: 'masonry-brick-split-body',
32493                     cn: []
32494                 }
32495             ]
32496         };
32497         
32498         if(this.href.length){
32499             cfg.href = this.href;
32500         }
32501         
32502         if(this.title.length){
32503             cfg.cn[0].cn[0].cn.push({
32504                 tag: 'h4',
32505                 cls: 'masonry-brick-title',
32506                 html: this.title
32507             });
32508         }
32509         
32510         if(this.html.length){
32511             cfg.cn[1].cn.push({
32512                 tag: 'p',
32513                 cls: 'masonry-brick-text',
32514                 html: this.html
32515             });
32516         }
32517
32518         if(this.bgimage.length){
32519             cfg.cn[0].cn.push({
32520                 tag: 'img',
32521                 cls: 'masonry-brick-image-view',
32522                 src: this.bgimage
32523             });
32524         }
32525         
32526         if(this.videourl.length){
32527             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32528             // youtube support only?
32529             cfg.cn[0].cn.cn.push({
32530                 tag: 'iframe',
32531                 cls: 'masonry-brick-image-view',
32532                 src: vurl,
32533                 frameborder : 0,
32534                 allowfullscreen : true
32535             });
32536         }
32537         
32538         return cfg;
32539     },
32540     
32541     initEvents: function() 
32542     {
32543         switch (this.size) {
32544             case 'xs' :
32545                 this.x = 1;
32546                 this.y = 1;
32547                 break;
32548             case 'sm' :
32549                 this.x = 2;
32550                 this.y = 2;
32551                 break;
32552             case 'md' :
32553             case 'md-left' :
32554             case 'md-right' :
32555                 this.x = 3;
32556                 this.y = 3;
32557                 break;
32558             case 'tall' :
32559                 this.x = 2;
32560                 this.y = 3;
32561                 break;
32562             case 'wide' :
32563                 this.x = 3;
32564                 this.y = 2;
32565                 break;
32566             case 'wide-thin' :
32567                 this.x = 3;
32568                 this.y = 1;
32569                 break;
32570                         
32571             default :
32572                 break;
32573         }
32574         
32575         if(Roo.isTouch){
32576             this.el.on('touchstart', this.onTouchStart, this);
32577             this.el.on('touchmove', this.onTouchMove, this);
32578             this.el.on('touchend', this.onTouchEnd, this);
32579             this.el.on('contextmenu', this.onContextMenu, this);
32580         } else {
32581             this.el.on('mouseenter'  ,this.enter, this);
32582             this.el.on('mouseleave', this.leave, this);
32583             this.el.on('click', this.onClick, this);
32584         }
32585         
32586         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32587             this.parent().bricks.push(this);   
32588         }
32589         
32590     },
32591     
32592     onClick: function(e, el)
32593     {
32594         var time = this.endTimer - this.startTimer;
32595         // Roo.log(e.preventDefault());
32596         if(Roo.isTouch){
32597             if(time > 1000){
32598                 e.preventDefault();
32599                 return;
32600             }
32601         }
32602         
32603         if(!this.preventDefault){
32604             return;
32605         }
32606         
32607         e.preventDefault();
32608         
32609         if (this.activcClass != '') {
32610             this.selectBrick();
32611         }
32612         
32613         this.fireEvent('click', this);
32614     },
32615     
32616     enter: function(e, el)
32617     {
32618         e.preventDefault();
32619         
32620         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32621             return;
32622         }
32623         
32624         if(this.bgimage.length && this.html.length){
32625             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32626         }
32627     },
32628     
32629     leave: function(e, el)
32630     {
32631         e.preventDefault();
32632         
32633         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32634             return;
32635         }
32636         
32637         if(this.bgimage.length && this.html.length){
32638             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32639         }
32640     },
32641     
32642     onTouchStart: function(e, el)
32643     {
32644 //        e.preventDefault();
32645         
32646         this.touchmoved = false;
32647         
32648         if(!this.isFitContainer){
32649             return;
32650         }
32651         
32652         if(!this.bgimage.length || !this.html.length){
32653             return;
32654         }
32655         
32656         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32657         
32658         this.timer = new Date().getTime();
32659         
32660     },
32661     
32662     onTouchMove: function(e, el)
32663     {
32664         this.touchmoved = true;
32665     },
32666     
32667     onContextMenu : function(e,el)
32668     {
32669         e.preventDefault();
32670         e.stopPropagation();
32671         return false;
32672     },
32673     
32674     onTouchEnd: function(e, el)
32675     {
32676 //        e.preventDefault();
32677         
32678         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32679         
32680             this.leave(e,el);
32681             
32682             return;
32683         }
32684         
32685         if(!this.bgimage.length || !this.html.length){
32686             
32687             if(this.href.length){
32688                 window.location.href = this.href;
32689             }
32690             
32691             return;
32692         }
32693         
32694         if(!this.isFitContainer){
32695             return;
32696         }
32697         
32698         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32699         
32700         window.location.href = this.href;
32701     },
32702     
32703     //selection on single brick only
32704     selectBrick : function() {
32705         
32706         if (!this.parentId) {
32707             return;
32708         }
32709         
32710         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32711         var index = m.selectedBrick.indexOf(this.id);
32712         
32713         if ( index > -1) {
32714             m.selectedBrick.splice(index,1);
32715             this.el.removeClass(this.activeClass);
32716             return;
32717         }
32718         
32719         for(var i = 0; i < m.selectedBrick.length; i++) {
32720             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32721             b.el.removeClass(b.activeClass);
32722         }
32723         
32724         m.selectedBrick = [];
32725         
32726         m.selectedBrick.push(this.id);
32727         this.el.addClass(this.activeClass);
32728         return;
32729     }
32730     
32731 });
32732
32733 Roo.apply(Roo.bootstrap.MasonryBrick, {
32734     
32735     //groups: {},
32736     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32737      /**
32738     * register a Masonry Brick
32739     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32740     */
32741     
32742     register : function(brick)
32743     {
32744         //this.groups[brick.id] = brick;
32745         this.groups.add(brick.id, brick);
32746     },
32747     /**
32748     * fetch a  masonry brick based on the masonry brick ID
32749     * @param {string} the masonry brick to add
32750     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32751     */
32752     
32753     get: function(brick_id) 
32754     {
32755         // if (typeof(this.groups[brick_id]) == 'undefined') {
32756         //     return false;
32757         // }
32758         // return this.groups[brick_id] ;
32759         
32760         if(this.groups.key(brick_id)) {
32761             return this.groups.key(brick_id);
32762         }
32763         
32764         return false;
32765     }
32766     
32767     
32768     
32769 });
32770
32771  /*
32772  * - LGPL
32773  *
32774  * element
32775  * 
32776  */
32777
32778 /**
32779  * @class Roo.bootstrap.Brick
32780  * @extends Roo.bootstrap.Component
32781  * Bootstrap Brick class
32782  * 
32783  * @constructor
32784  * Create a new Brick
32785  * @param {Object} config The config object
32786  */
32787
32788 Roo.bootstrap.Brick = function(config){
32789     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
32790     
32791     this.addEvents({
32792         // raw events
32793         /**
32794          * @event click
32795          * When a Brick is click
32796          * @param {Roo.bootstrap.Brick} this
32797          * @param {Roo.EventObject} e
32798          */
32799         "click" : true
32800     });
32801 };
32802
32803 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
32804     
32805     /**
32806      * @cfg {String} title
32807      */   
32808     title : '',
32809     /**
32810      * @cfg {String} html
32811      */   
32812     html : '',
32813     /**
32814      * @cfg {String} bgimage
32815      */   
32816     bgimage : '',
32817     /**
32818      * @cfg {String} cls
32819      */   
32820     cls : '',
32821     /**
32822      * @cfg {String} href
32823      */   
32824     href : '',
32825     /**
32826      * @cfg {String} video
32827      */   
32828     video : '',
32829     /**
32830      * @cfg {Boolean} square
32831      */   
32832     square : true,
32833     
32834     getAutoCreate : function()
32835     {
32836         var cls = 'roo-brick';
32837         
32838         if(this.href.length){
32839             cls += ' roo-brick-link';
32840         }
32841         
32842         if(this.bgimage.length){
32843             cls += ' roo-brick-image';
32844         }
32845         
32846         if(!this.html.length && !this.bgimage.length){
32847             cls += ' roo-brick-center-title';
32848         }
32849         
32850         if(!this.html.length && this.bgimage.length){
32851             cls += ' roo-brick-bottom-title';
32852         }
32853         
32854         if(this.cls){
32855             cls += ' ' + this.cls;
32856         }
32857         
32858         var cfg = {
32859             tag: (this.href.length) ? 'a' : 'div',
32860             cls: cls,
32861             cn: [
32862                 {
32863                     tag: 'div',
32864                     cls: 'roo-brick-paragraph',
32865                     cn: []
32866                 }
32867             ]
32868         };
32869         
32870         if(this.href.length){
32871             cfg.href = this.href;
32872         }
32873         
32874         var cn = cfg.cn[0].cn;
32875         
32876         if(this.title.length){
32877             cn.push({
32878                 tag: 'h4',
32879                 cls: 'roo-brick-title',
32880                 html: this.title
32881             });
32882         }
32883         
32884         if(this.html.length){
32885             cn.push({
32886                 tag: 'p',
32887                 cls: 'roo-brick-text',
32888                 html: this.html
32889             });
32890         } else {
32891             cn.cls += ' hide';
32892         }
32893         
32894         if(this.bgimage.length){
32895             cfg.cn.push({
32896                 tag: 'img',
32897                 cls: 'roo-brick-image-view',
32898                 src: this.bgimage
32899             });
32900         }
32901         
32902         return cfg;
32903     },
32904     
32905     initEvents: function() 
32906     {
32907         if(this.title.length || this.html.length){
32908             this.el.on('mouseenter'  ,this.enter, this);
32909             this.el.on('mouseleave', this.leave, this);
32910         }
32911         
32912         Roo.EventManager.onWindowResize(this.resize, this); 
32913         
32914         if(this.bgimage.length){
32915             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
32916             this.imageEl.on('load', this.onImageLoad, this);
32917             return;
32918         }
32919         
32920         this.resize();
32921     },
32922     
32923     onImageLoad : function()
32924     {
32925         this.resize();
32926     },
32927     
32928     resize : function()
32929     {
32930         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
32931         
32932         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
32933         
32934         if(this.bgimage.length){
32935             var image = this.el.select('.roo-brick-image-view', true).first();
32936             
32937             image.setWidth(paragraph.getWidth());
32938             
32939             if(this.square){
32940                 image.setHeight(paragraph.getWidth());
32941             }
32942             
32943             this.el.setHeight(image.getHeight());
32944             paragraph.setHeight(image.getHeight());
32945             
32946         }
32947         
32948     },
32949     
32950     enter: function(e, el)
32951     {
32952         e.preventDefault();
32953         
32954         if(this.bgimage.length){
32955             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
32956             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
32957         }
32958     },
32959     
32960     leave: function(e, el)
32961     {
32962         e.preventDefault();
32963         
32964         if(this.bgimage.length){
32965             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
32966             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
32967         }
32968     }
32969     
32970 });
32971
32972  
32973
32974  /*
32975  * - LGPL
32976  *
32977  * Input
32978  * 
32979  */
32980
32981 /**
32982  * @class Roo.bootstrap.NumberField
32983  * @extends Roo.bootstrap.Input
32984  * Bootstrap NumberField class
32985  * 
32986  * 
32987  * 
32988  * 
32989  * @constructor
32990  * Create a new NumberField
32991  * @param {Object} config The config object
32992  */
32993
32994 Roo.bootstrap.NumberField = function(config){
32995     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
32996 };
32997
32998 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
32999     
33000     /**
33001      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
33002      */
33003     allowDecimals : true,
33004     /**
33005      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
33006      */
33007     decimalSeparator : ".",
33008     /**
33009      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
33010      */
33011     decimalPrecision : 2,
33012     /**
33013      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
33014      */
33015     allowNegative : true,
33016     /**
33017      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33018      */
33019     minValue : Number.NEGATIVE_INFINITY,
33020     /**
33021      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33022      */
33023     maxValue : Number.MAX_VALUE,
33024     /**
33025      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33026      */
33027     minText : "The minimum value for this field is {0}",
33028     /**
33029      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33030      */
33031     maxText : "The maximum value for this field is {0}",
33032     /**
33033      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33034      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33035      */
33036     nanText : "{0} is not a valid number",
33037     /**
33038      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
33039      */
33040     castInt : true,
33041     /**
33042      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
33043      */
33044     thousandsDelimiter : false,
33045     /**
33046      * @cfg {String} valueAlign alignment of value
33047      */
33048     valueAlign : "left",
33049
33050     getAutoCreate : function()
33051     {
33052         var hiddenInput = {
33053             tag: 'input',
33054             type: 'hidden',
33055             id: Roo.id(),
33056             cls: 'hidden-number-input'
33057         };
33058         
33059         if (this.name) {
33060             hiddenInput.name = this.name;
33061         }
33062         
33063         this.name = '';
33064         
33065         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
33066         
33067         this.name = hiddenInput.name;
33068         
33069         if(cfg.cn.length > 0) {
33070             cfg.cn.push(hiddenInput);
33071         }
33072         
33073         return cfg;
33074     },
33075
33076     // private
33077     initEvents : function()
33078     {   
33079         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33080         
33081         var allowed = "0123456789";
33082         
33083         if(this.allowDecimals){
33084             allowed += this.decimalSeparator;
33085         }
33086         
33087         if(this.allowNegative){
33088             allowed += "-";
33089         }
33090         
33091         if(this.thousandsDelimiter) {
33092             allowed += ",";
33093         }
33094         
33095         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33096         
33097         var keyPress = function(e){
33098             
33099             var k = e.getKey();
33100             
33101             var c = e.getCharCode();
33102             
33103             if(
33104                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33105                     allowed.indexOf(String.fromCharCode(c)) === -1
33106             ){
33107                 e.stopEvent();
33108                 return;
33109             }
33110             
33111             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33112                 return;
33113             }
33114             
33115             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33116                 e.stopEvent();
33117             }
33118         };
33119         
33120         this.el.on("keypress", keyPress, this);
33121     },
33122     
33123     validateValue : function(value)
33124     {
33125         
33126         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33127             return false;
33128         }
33129         
33130         var num = this.parseValue(value);
33131         
33132         if(isNaN(num)){
33133             this.markInvalid(String.format(this.nanText, value));
33134             return false;
33135         }
33136         
33137         if(num < this.minValue){
33138             this.markInvalid(String.format(this.minText, this.minValue));
33139             return false;
33140         }
33141         
33142         if(num > this.maxValue){
33143             this.markInvalid(String.format(this.maxText, this.maxValue));
33144             return false;
33145         }
33146         
33147         return true;
33148     },
33149
33150     getValue : function()
33151     {
33152         var v = this.hiddenEl().getValue();
33153         
33154         return this.fixPrecision(this.parseValue(v));
33155     },
33156
33157     parseValue : function(value)
33158     {
33159         if(this.thousandsDelimiter) {
33160             value += "";
33161             r = new RegExp(",", "g");
33162             value = value.replace(r, "");
33163         }
33164         
33165         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33166         return isNaN(value) ? '' : value;
33167     },
33168
33169     fixPrecision : function(value)
33170     {
33171         if(this.thousandsDelimiter) {
33172             value += "";
33173             r = new RegExp(",", "g");
33174             value = value.replace(r, "");
33175         }
33176         
33177         var nan = isNaN(value);
33178         
33179         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33180             return nan ? '' : value;
33181         }
33182         return parseFloat(value).toFixed(this.decimalPrecision);
33183     },
33184
33185     setValue : function(v)
33186     {
33187         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
33188         
33189         this.value = v;
33190         
33191         if(this.rendered){
33192             
33193             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
33194             
33195             this.inputEl().dom.value = (this.thousandsDelimiter ? Roo.util.Format.number(v, this.decimalPrecision) : v);
33196             
33197             this.validate();
33198         }
33199     },
33200
33201     decimalPrecisionFcn : function(v)
33202     {
33203         return Math.floor(v);
33204     },
33205
33206     beforeBlur : function()
33207     {
33208         if(!this.castInt){
33209             return;
33210         }
33211         
33212         var v = this.parseValue(this.getRawValue());
33213         if(v){
33214             this.setValue(v);
33215         }
33216     },
33217     
33218     hiddenEl : function()
33219     {
33220         return this.el.select('input.hidden-number-input',true).first();
33221     }
33222     
33223 });
33224
33225  
33226
33227 /*
33228 * Licence: LGPL
33229 */
33230
33231 /**
33232  * @class Roo.bootstrap.DocumentSlider
33233  * @extends Roo.bootstrap.Component
33234  * Bootstrap DocumentSlider class
33235  * 
33236  * @constructor
33237  * Create a new DocumentViewer
33238  * @param {Object} config The config object
33239  */
33240
33241 Roo.bootstrap.DocumentSlider = function(config){
33242     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33243     
33244     this.files = [];
33245     
33246     this.addEvents({
33247         /**
33248          * @event initial
33249          * Fire after initEvent
33250          * @param {Roo.bootstrap.DocumentSlider} this
33251          */
33252         "initial" : true,
33253         /**
33254          * @event update
33255          * Fire after update
33256          * @param {Roo.bootstrap.DocumentSlider} this
33257          */
33258         "update" : true,
33259         /**
33260          * @event click
33261          * Fire after click
33262          * @param {Roo.bootstrap.DocumentSlider} this
33263          */
33264         "click" : true
33265     });
33266 };
33267
33268 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33269     
33270     files : false,
33271     
33272     indicator : 0,
33273     
33274     getAutoCreate : function()
33275     {
33276         var cfg = {
33277             tag : 'div',
33278             cls : 'roo-document-slider',
33279             cn : [
33280                 {
33281                     tag : 'div',
33282                     cls : 'roo-document-slider-header',
33283                     cn : [
33284                         {
33285                             tag : 'div',
33286                             cls : 'roo-document-slider-header-title'
33287                         }
33288                     ]
33289                 },
33290                 {
33291                     tag : 'div',
33292                     cls : 'roo-document-slider-body',
33293                     cn : [
33294                         {
33295                             tag : 'div',
33296                             cls : 'roo-document-slider-prev',
33297                             cn : [
33298                                 {
33299                                     tag : 'i',
33300                                     cls : 'fa fa-chevron-left'
33301                                 }
33302                             ]
33303                         },
33304                         {
33305                             tag : 'div',
33306                             cls : 'roo-document-slider-thumb',
33307                             cn : [
33308                                 {
33309                                     tag : 'img',
33310                                     cls : 'roo-document-slider-image'
33311                                 }
33312                             ]
33313                         },
33314                         {
33315                             tag : 'div',
33316                             cls : 'roo-document-slider-next',
33317                             cn : [
33318                                 {
33319                                     tag : 'i',
33320                                     cls : 'fa fa-chevron-right'
33321                                 }
33322                             ]
33323                         }
33324                     ]
33325                 }
33326             ]
33327         };
33328         
33329         return cfg;
33330     },
33331     
33332     initEvents : function()
33333     {
33334         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33335         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33336         
33337         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33338         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33339         
33340         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33341         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33342         
33343         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33344         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33345         
33346         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33347         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33348         
33349         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33350         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33351         
33352         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33353         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33354         
33355         this.thumbEl.on('click', this.onClick, this);
33356         
33357         this.prevIndicator.on('click', this.prev, this);
33358         
33359         this.nextIndicator.on('click', this.next, this);
33360         
33361     },
33362     
33363     initial : function()
33364     {
33365         if(this.files.length){
33366             this.indicator = 1;
33367             this.update()
33368         }
33369         
33370         this.fireEvent('initial', this);
33371     },
33372     
33373     update : function()
33374     {
33375         this.imageEl.attr('src', this.files[this.indicator - 1]);
33376         
33377         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33378         
33379         this.prevIndicator.show();
33380         
33381         if(this.indicator == 1){
33382             this.prevIndicator.hide();
33383         }
33384         
33385         this.nextIndicator.show();
33386         
33387         if(this.indicator == this.files.length){
33388             this.nextIndicator.hide();
33389         }
33390         
33391         this.thumbEl.scrollTo('top');
33392         
33393         this.fireEvent('update', this);
33394     },
33395     
33396     onClick : function(e)
33397     {
33398         e.preventDefault();
33399         
33400         this.fireEvent('click', this);
33401     },
33402     
33403     prev : function(e)
33404     {
33405         e.preventDefault();
33406         
33407         this.indicator = Math.max(1, this.indicator - 1);
33408         
33409         this.update();
33410     },
33411     
33412     next : function(e)
33413     {
33414         e.preventDefault();
33415         
33416         this.indicator = Math.min(this.files.length, this.indicator + 1);
33417         
33418         this.update();
33419     }
33420 });
33421 /*
33422  * - LGPL
33423  *
33424  * RadioSet
33425  *
33426  *
33427  */
33428
33429 /**
33430  * @class Roo.bootstrap.RadioSet
33431  * @extends Roo.bootstrap.Input
33432  * Bootstrap RadioSet class
33433  * @cfg {String} indicatorpos (left|right) default left
33434  * @cfg {Boolean} inline (true|false) inline the element (default true)
33435  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33436  * @constructor
33437  * Create a new RadioSet
33438  * @param {Object} config The config object
33439  */
33440
33441 Roo.bootstrap.RadioSet = function(config){
33442     
33443     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33444     
33445     this.radioes = [];
33446     
33447     Roo.bootstrap.RadioSet.register(this);
33448     
33449     this.addEvents({
33450         /**
33451         * @event check
33452         * Fires when the element is checked or unchecked.
33453         * @param {Roo.bootstrap.RadioSet} this This radio
33454         * @param {Roo.bootstrap.Radio} item The checked item
33455         */
33456        check : true
33457     });
33458     
33459 };
33460
33461 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33462
33463     radioes : false,
33464     
33465     inline : true,
33466     
33467     weight : '',
33468     
33469     indicatorpos : 'left',
33470     
33471     getAutoCreate : function()
33472     {
33473         var label = {
33474             tag : 'label',
33475             cls : 'roo-radio-set-label',
33476             cn : [
33477                 {
33478                     tag : 'span',
33479                     html : this.fieldLabel
33480                 }
33481             ]
33482         };
33483         
33484         if(this.indicatorpos == 'left'){
33485             label.cn.unshift({
33486                 tag : 'i',
33487                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33488                 tooltip : 'This field is required'
33489             });
33490         } else {
33491             label.cn.push({
33492                 tag : 'i',
33493                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33494                 tooltip : 'This field is required'
33495             });
33496         }
33497         
33498         var items = {
33499             tag : 'div',
33500             cls : 'roo-radio-set-items'
33501         };
33502         
33503         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33504         
33505         if (align === 'left' && this.fieldLabel.length) {
33506             
33507             items = {
33508                 cls : "roo-radio-set-right", 
33509                 cn: [
33510                     items
33511                 ]
33512             };
33513             
33514             if(this.labelWidth > 12){
33515                 label.style = "width: " + this.labelWidth + 'px';
33516             }
33517             
33518             if(this.labelWidth < 13 && this.labelmd == 0){
33519                 this.labelmd = this.labelWidth;
33520             }
33521             
33522             if(this.labellg > 0){
33523                 label.cls += ' col-lg-' + this.labellg;
33524                 items.cls += ' col-lg-' + (12 - this.labellg);
33525             }
33526             
33527             if(this.labelmd > 0){
33528                 label.cls += ' col-md-' + this.labelmd;
33529                 items.cls += ' col-md-' + (12 - this.labelmd);
33530             }
33531             
33532             if(this.labelsm > 0){
33533                 label.cls += ' col-sm-' + this.labelsm;
33534                 items.cls += ' col-sm-' + (12 - this.labelsm);
33535             }
33536             
33537             if(this.labelxs > 0){
33538                 label.cls += ' col-xs-' + this.labelxs;
33539                 items.cls += ' col-xs-' + (12 - this.labelxs);
33540             }
33541         }
33542         
33543         var cfg = {
33544             tag : 'div',
33545             cls : 'roo-radio-set',
33546             cn : [
33547                 {
33548                     tag : 'input',
33549                     cls : 'roo-radio-set-input',
33550                     type : 'hidden',
33551                     name : this.name,
33552                     value : this.value ? this.value :  ''
33553                 },
33554                 label,
33555                 items
33556             ]
33557         };
33558         
33559         if(this.weight.length){
33560             cfg.cls += ' roo-radio-' + this.weight;
33561         }
33562         
33563         if(this.inline) {
33564             cfg.cls += ' roo-radio-set-inline';
33565         }
33566         
33567         var settings=this;
33568         ['xs','sm','md','lg'].map(function(size){
33569             if (settings[size]) {
33570                 cfg.cls += ' col-' + size + '-' + settings[size];
33571             }
33572         });
33573         
33574         return cfg;
33575         
33576     },
33577
33578     initEvents : function()
33579     {
33580         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33581         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33582         
33583         if(!this.fieldLabel.length){
33584             this.labelEl.hide();
33585         }
33586         
33587         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33588         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33589         
33590         this.indicatorEl().addClass('invisible');
33591         
33592         this.originalValue = this.getValue();
33593         
33594     },
33595     
33596     inputEl: function ()
33597     {
33598         return this.el.select('.roo-radio-set-input', true).first();
33599     },
33600     
33601     getChildContainer : function()
33602     {
33603         return this.itemsEl;
33604     },
33605     
33606     register : function(item)
33607     {
33608         this.radioes.push(item);
33609         
33610     },
33611     
33612     validate : function()
33613     {   
33614         var valid = false;
33615         
33616         Roo.each(this.radioes, function(i){
33617             if(!i.checked){
33618                 return;
33619             }
33620             
33621             valid = true;
33622             return false;
33623         });
33624         
33625         if(this.allowBlank) {
33626             return true;
33627         }
33628         
33629         if(this.disabled || valid){
33630             this.markValid();
33631             return true;
33632         }
33633         
33634         this.markInvalid();
33635         return false;
33636         
33637     },
33638     
33639     markValid : function()
33640     {
33641         if(this.labelEl.isVisible(true)){
33642             this.indicatorEl().removeClass('visible');
33643             this.indicatorEl().addClass('invisible');
33644         }
33645         
33646         this.el.removeClass([this.invalidClass, this.validClass]);
33647         this.el.addClass(this.validClass);
33648         
33649         this.fireEvent('valid', this);
33650     },
33651     
33652     markInvalid : function(msg)
33653     {
33654         if(this.allowBlank || this.disabled){
33655             return;
33656         }
33657         
33658         if(this.labelEl.isVisible(true)){
33659             this.indicatorEl().removeClass('invisible');
33660             this.indicatorEl().addClass('visible');
33661         }
33662         
33663         this.el.removeClass([this.invalidClass, this.validClass]);
33664         this.el.addClass(this.invalidClass);
33665         
33666         this.fireEvent('invalid', this, msg);
33667         
33668     },
33669     
33670     setValue : function(v, suppressEvent)
33671     {   
33672         if(this.value === v){
33673             return;
33674         }
33675         
33676         this.value = v;
33677         
33678         if(this.rendered){
33679             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33680         }
33681         
33682         Roo.each(this.radioes, function(i){
33683             i.checked = false;
33684             i.el.removeClass('checked');
33685         });
33686         
33687         Roo.each(this.radioes, function(i){
33688             
33689             if(i.value === v || i.value.toString() === v.toString()){
33690                 i.checked = true;
33691                 i.el.addClass('checked');
33692                 
33693                 if(suppressEvent !== true){
33694                     this.fireEvent('check', this, i);
33695                 }
33696                 
33697                 return false;
33698             }
33699             
33700         }, this);
33701         
33702         this.validate();
33703     },
33704     
33705     clearInvalid : function(){
33706         
33707         if(!this.el || this.preventMark){
33708             return;
33709         }
33710         
33711         this.el.removeClass([this.invalidClass]);
33712         
33713         this.fireEvent('valid', this);
33714     }
33715     
33716 });
33717
33718 Roo.apply(Roo.bootstrap.RadioSet, {
33719     
33720     groups: {},
33721     
33722     register : function(set)
33723     {
33724         this.groups[set.name] = set;
33725     },
33726     
33727     get: function(name) 
33728     {
33729         if (typeof(this.groups[name]) == 'undefined') {
33730             return false;
33731         }
33732         
33733         return this.groups[name] ;
33734     }
33735     
33736 });
33737 /*
33738  * Based on:
33739  * Ext JS Library 1.1.1
33740  * Copyright(c) 2006-2007, Ext JS, LLC.
33741  *
33742  * Originally Released Under LGPL - original licence link has changed is not relivant.
33743  *
33744  * Fork - LGPL
33745  * <script type="text/javascript">
33746  */
33747
33748
33749 /**
33750  * @class Roo.bootstrap.SplitBar
33751  * @extends Roo.util.Observable
33752  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33753  * <br><br>
33754  * Usage:
33755  * <pre><code>
33756 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33757                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33758 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
33759 split.minSize = 100;
33760 split.maxSize = 600;
33761 split.animate = true;
33762 split.on('moved', splitterMoved);
33763 </code></pre>
33764  * @constructor
33765  * Create a new SplitBar
33766  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
33767  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
33768  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33769  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
33770                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
33771                         position of the SplitBar).
33772  */
33773 Roo.bootstrap.SplitBar = function(cfg){
33774     
33775     /** @private */
33776     
33777     //{
33778     //  dragElement : elm
33779     //  resizingElement: el,
33780         // optional..
33781     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
33782     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
33783         // existingProxy ???
33784     //}
33785     
33786     this.el = Roo.get(cfg.dragElement, true);
33787     this.el.dom.unselectable = "on";
33788     /** @private */
33789     this.resizingEl = Roo.get(cfg.resizingElement, true);
33790
33791     /**
33792      * @private
33793      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33794      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
33795      * @type Number
33796      */
33797     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
33798     
33799     /**
33800      * The minimum size of the resizing element. (Defaults to 0)
33801      * @type Number
33802      */
33803     this.minSize = 0;
33804     
33805     /**
33806      * The maximum size of the resizing element. (Defaults to 2000)
33807      * @type Number
33808      */
33809     this.maxSize = 2000;
33810     
33811     /**
33812      * Whether to animate the transition to the new size
33813      * @type Boolean
33814      */
33815     this.animate = false;
33816     
33817     /**
33818      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
33819      * @type Boolean
33820      */
33821     this.useShim = false;
33822     
33823     /** @private */
33824     this.shim = null;
33825     
33826     if(!cfg.existingProxy){
33827         /** @private */
33828         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
33829     }else{
33830         this.proxy = Roo.get(cfg.existingProxy).dom;
33831     }
33832     /** @private */
33833     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
33834     
33835     /** @private */
33836     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
33837     
33838     /** @private */
33839     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
33840     
33841     /** @private */
33842     this.dragSpecs = {};
33843     
33844     /**
33845      * @private The adapter to use to positon and resize elements
33846      */
33847     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33848     this.adapter.init(this);
33849     
33850     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33851         /** @private */
33852         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
33853         this.el.addClass("roo-splitbar-h");
33854     }else{
33855         /** @private */
33856         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
33857         this.el.addClass("roo-splitbar-v");
33858     }
33859     
33860     this.addEvents({
33861         /**
33862          * @event resize
33863          * Fires when the splitter is moved (alias for {@link #event-moved})
33864          * @param {Roo.bootstrap.SplitBar} this
33865          * @param {Number} newSize the new width or height
33866          */
33867         "resize" : true,
33868         /**
33869          * @event moved
33870          * Fires when the splitter is moved
33871          * @param {Roo.bootstrap.SplitBar} this
33872          * @param {Number} newSize the new width or height
33873          */
33874         "moved" : true,
33875         /**
33876          * @event beforeresize
33877          * Fires before the splitter is dragged
33878          * @param {Roo.bootstrap.SplitBar} this
33879          */
33880         "beforeresize" : true,
33881
33882         "beforeapply" : true
33883     });
33884
33885     Roo.util.Observable.call(this);
33886 };
33887
33888 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
33889     onStartProxyDrag : function(x, y){
33890         this.fireEvent("beforeresize", this);
33891         if(!this.overlay){
33892             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
33893             o.unselectable();
33894             o.enableDisplayMode("block");
33895             // all splitbars share the same overlay
33896             Roo.bootstrap.SplitBar.prototype.overlay = o;
33897         }
33898         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33899         this.overlay.show();
33900         Roo.get(this.proxy).setDisplayed("block");
33901         var size = this.adapter.getElementSize(this);
33902         this.activeMinSize = this.getMinimumSize();;
33903         this.activeMaxSize = this.getMaximumSize();;
33904         var c1 = size - this.activeMinSize;
33905         var c2 = Math.max(this.activeMaxSize - size, 0);
33906         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33907             this.dd.resetConstraints();
33908             this.dd.setXConstraint(
33909                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
33910                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
33911             );
33912             this.dd.setYConstraint(0, 0);
33913         }else{
33914             this.dd.resetConstraints();
33915             this.dd.setXConstraint(0, 0);
33916             this.dd.setYConstraint(
33917                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
33918                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
33919             );
33920          }
33921         this.dragSpecs.startSize = size;
33922         this.dragSpecs.startPoint = [x, y];
33923         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
33924     },
33925     
33926     /** 
33927      * @private Called after the drag operation by the DDProxy
33928      */
33929     onEndProxyDrag : function(e){
33930         Roo.get(this.proxy).setDisplayed(false);
33931         var endPoint = Roo.lib.Event.getXY(e);
33932         if(this.overlay){
33933             this.overlay.hide();
33934         }
33935         var newSize;
33936         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33937             newSize = this.dragSpecs.startSize + 
33938                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
33939                     endPoint[0] - this.dragSpecs.startPoint[0] :
33940                     this.dragSpecs.startPoint[0] - endPoint[0]
33941                 );
33942         }else{
33943             newSize = this.dragSpecs.startSize + 
33944                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
33945                     endPoint[1] - this.dragSpecs.startPoint[1] :
33946                     this.dragSpecs.startPoint[1] - endPoint[1]
33947                 );
33948         }
33949         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
33950         if(newSize != this.dragSpecs.startSize){
33951             if(this.fireEvent('beforeapply', this, newSize) !== false){
33952                 this.adapter.setElementSize(this, newSize);
33953                 this.fireEvent("moved", this, newSize);
33954                 this.fireEvent("resize", this, newSize);
33955             }
33956         }
33957     },
33958     
33959     /**
33960      * Get the adapter this SplitBar uses
33961      * @return The adapter object
33962      */
33963     getAdapter : function(){
33964         return this.adapter;
33965     },
33966     
33967     /**
33968      * Set the adapter this SplitBar uses
33969      * @param {Object} adapter A SplitBar adapter object
33970      */
33971     setAdapter : function(adapter){
33972         this.adapter = adapter;
33973         this.adapter.init(this);
33974     },
33975     
33976     /**
33977      * Gets the minimum size for the resizing element
33978      * @return {Number} The minimum size
33979      */
33980     getMinimumSize : function(){
33981         return this.minSize;
33982     },
33983     
33984     /**
33985      * Sets the minimum size for the resizing element
33986      * @param {Number} minSize The minimum size
33987      */
33988     setMinimumSize : function(minSize){
33989         this.minSize = minSize;
33990     },
33991     
33992     /**
33993      * Gets the maximum size for the resizing element
33994      * @return {Number} The maximum size
33995      */
33996     getMaximumSize : function(){
33997         return this.maxSize;
33998     },
33999     
34000     /**
34001      * Sets the maximum size for the resizing element
34002      * @param {Number} maxSize The maximum size
34003      */
34004     setMaximumSize : function(maxSize){
34005         this.maxSize = maxSize;
34006     },
34007     
34008     /**
34009      * Sets the initialize size for the resizing element
34010      * @param {Number} size The initial size
34011      */
34012     setCurrentSize : function(size){
34013         var oldAnimate = this.animate;
34014         this.animate = false;
34015         this.adapter.setElementSize(this, size);
34016         this.animate = oldAnimate;
34017     },
34018     
34019     /**
34020      * Destroy this splitbar. 
34021      * @param {Boolean} removeEl True to remove the element
34022      */
34023     destroy : function(removeEl){
34024         if(this.shim){
34025             this.shim.remove();
34026         }
34027         this.dd.unreg();
34028         this.proxy.parentNode.removeChild(this.proxy);
34029         if(removeEl){
34030             this.el.remove();
34031         }
34032     }
34033 });
34034
34035 /**
34036  * @private static Create our own proxy element element. So it will be the same same size on all browsers, we won't use borders. Instead we use a background color.
34037  */
34038 Roo.bootstrap.SplitBar.createProxy = function(dir){
34039     var proxy = new Roo.Element(document.createElement("div"));
34040     proxy.unselectable();
34041     var cls = 'roo-splitbar-proxy';
34042     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
34043     document.body.appendChild(proxy.dom);
34044     return proxy.dom;
34045 };
34046
34047 /** 
34048  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
34049  * Default Adapter. It assumes the splitter and resizing element are not positioned
34050  * elements and only gets/sets the width of the element. Generally used for table based layouts.
34051  */
34052 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
34053 };
34054
34055 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
34056     // do nothing for now
34057     init : function(s){
34058     
34059     },
34060     /**
34061      * Called before drag operations to get the current size of the resizing element. 
34062      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34063      */
34064      getElementSize : function(s){
34065         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34066             return s.resizingEl.getWidth();
34067         }else{
34068             return s.resizingEl.getHeight();
34069         }
34070     },
34071     
34072     /**
34073      * Called after drag operations to set the size of the resizing element.
34074      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
34075      * @param {Number} newSize The new size to set
34076      * @param {Function} onComplete A function to be invoked when resizing is complete
34077      */
34078     setElementSize : function(s, newSize, onComplete){
34079         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
34080             if(!s.animate){
34081                 s.resizingEl.setWidth(newSize);
34082                 if(onComplete){
34083                     onComplete(s, newSize);
34084                 }
34085             }else{
34086                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34087             }
34088         }else{
34089             
34090             if(!s.animate){
34091                 s.resizingEl.setHeight(newSize);
34092                 if(onComplete){
34093                     onComplete(s, newSize);
34094                 }
34095             }else{
34096                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34097             }
34098         }
34099     }
34100 };
34101
34102 /** 
34103  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34104  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34105  * Adapter that  moves the splitter element to align with the resized sizing element. 
34106  * Used with an absolute positioned SplitBar.
34107  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34108  * document.body, make sure you assign an id to the body element.
34109  */
34110 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34111     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34112     this.container = Roo.get(container);
34113 };
34114
34115 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34116     init : function(s){
34117         this.basic.init(s);
34118     },
34119     
34120     getElementSize : function(s){
34121         return this.basic.getElementSize(s);
34122     },
34123     
34124     setElementSize : function(s, newSize, onComplete){
34125         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34126     },
34127     
34128     moveSplitter : function(s){
34129         var yes = Roo.bootstrap.SplitBar;
34130         switch(s.placement){
34131             case yes.LEFT:
34132                 s.el.setX(s.resizingEl.getRight());
34133                 break;
34134             case yes.RIGHT:
34135                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34136                 break;
34137             case yes.TOP:
34138                 s.el.setY(s.resizingEl.getBottom());
34139                 break;
34140             case yes.BOTTOM:
34141                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34142                 break;
34143         }
34144     }
34145 };
34146
34147 /**
34148  * Orientation constant - Create a vertical SplitBar
34149  * @static
34150  * @type Number
34151  */
34152 Roo.bootstrap.SplitBar.VERTICAL = 1;
34153
34154 /**
34155  * Orientation constant - Create a horizontal SplitBar
34156  * @static
34157  * @type Number
34158  */
34159 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34160
34161 /**
34162  * Placement constant - The resizing element is to the left of the splitter element
34163  * @static
34164  * @type Number
34165  */
34166 Roo.bootstrap.SplitBar.LEFT = 1;
34167
34168 /**
34169  * Placement constant - The resizing element is to the right of the splitter element
34170  * @static
34171  * @type Number
34172  */
34173 Roo.bootstrap.SplitBar.RIGHT = 2;
34174
34175 /**
34176  * Placement constant - The resizing element is positioned above the splitter element
34177  * @static
34178  * @type Number
34179  */
34180 Roo.bootstrap.SplitBar.TOP = 3;
34181
34182 /**
34183  * Placement constant - The resizing element is positioned under splitter element
34184  * @static
34185  * @type Number
34186  */
34187 Roo.bootstrap.SplitBar.BOTTOM = 4;
34188 Roo.namespace("Roo.bootstrap.layout");/*
34189  * Based on:
34190  * Ext JS Library 1.1.1
34191  * Copyright(c) 2006-2007, Ext JS, LLC.
34192  *
34193  * Originally Released Under LGPL - original licence link has changed is not relivant.
34194  *
34195  * Fork - LGPL
34196  * <script type="text/javascript">
34197  */
34198
34199 /**
34200  * @class Roo.bootstrap.layout.Manager
34201  * @extends Roo.bootstrap.Component
34202  * Base class for layout managers.
34203  */
34204 Roo.bootstrap.layout.Manager = function(config)
34205 {
34206     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34207
34208
34209
34210
34211
34212     /** false to disable window resize monitoring @type Boolean */
34213     this.monitorWindowResize = true;
34214     this.regions = {};
34215     this.addEvents({
34216         /**
34217          * @event layout
34218          * Fires when a layout is performed.
34219          * @param {Roo.LayoutManager} this
34220          */
34221         "layout" : true,
34222         /**
34223          * @event regionresized
34224          * Fires when the user resizes a region.
34225          * @param {Roo.LayoutRegion} region The resized region
34226          * @param {Number} newSize The new size (width for east/west, height for north/south)
34227          */
34228         "regionresized" : true,
34229         /**
34230          * @event regioncollapsed
34231          * Fires when a region is collapsed.
34232          * @param {Roo.LayoutRegion} region The collapsed region
34233          */
34234         "regioncollapsed" : true,
34235         /**
34236          * @event regionexpanded
34237          * Fires when a region is expanded.
34238          * @param {Roo.LayoutRegion} region The expanded region
34239          */
34240         "regionexpanded" : true
34241     });
34242     this.updating = false;
34243
34244     if (config.el) {
34245         this.el = Roo.get(config.el);
34246         this.initEvents();
34247     }
34248
34249 };
34250
34251 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34252
34253
34254     regions : null,
34255
34256     monitorWindowResize : true,
34257
34258
34259     updating : false,
34260
34261
34262     onRender : function(ct, position)
34263     {
34264         if(!this.el){
34265             this.el = Roo.get(ct);
34266             this.initEvents();
34267         }
34268         //this.fireEvent('render',this);
34269     },
34270
34271
34272     initEvents: function()
34273     {
34274
34275
34276         // ie scrollbar fix
34277         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34278             document.body.scroll = "no";
34279         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34280             this.el.position('relative');
34281         }
34282         this.id = this.el.id;
34283         this.el.addClass("roo-layout-container");
34284         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34285         if(this.el.dom != document.body ) {
34286             this.el.on('resize', this.layout,this);
34287             this.el.on('show', this.layout,this);
34288         }
34289
34290     },
34291
34292     /**
34293      * Returns true if this layout is currently being updated
34294      * @return {Boolean}
34295      */
34296     isUpdating : function(){
34297         return this.updating;
34298     },
34299
34300     /**
34301      * Suspend the LayoutManager from doing auto-layouts while
34302      * making multiple add or remove calls
34303      */
34304     beginUpdate : function(){
34305         this.updating = true;
34306     },
34307
34308     /**
34309      * Restore auto-layouts and optionally disable the manager from performing a layout
34310      * @param {Boolean} noLayout true to disable a layout update
34311      */
34312     endUpdate : function(noLayout){
34313         this.updating = false;
34314         if(!noLayout){
34315             this.layout();
34316         }
34317     },
34318
34319     layout: function(){
34320         // abstract...
34321     },
34322
34323     onRegionResized : function(region, newSize){
34324         this.fireEvent("regionresized", region, newSize);
34325         this.layout();
34326     },
34327
34328     onRegionCollapsed : function(region){
34329         this.fireEvent("regioncollapsed", region);
34330     },
34331
34332     onRegionExpanded : function(region){
34333         this.fireEvent("regionexpanded", region);
34334     },
34335
34336     /**
34337      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34338      * performs box-model adjustments.
34339      * @return {Object} The size as an object {width: (the width), height: (the height)}
34340      */
34341     getViewSize : function()
34342     {
34343         var size;
34344         if(this.el.dom != document.body){
34345             size = this.el.getSize();
34346         }else{
34347             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34348         }
34349         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34350         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34351         return size;
34352     },
34353
34354     /**
34355      * Returns the Element this layout is bound to.
34356      * @return {Roo.Element}
34357      */
34358     getEl : function(){
34359         return this.el;
34360     },
34361
34362     /**
34363      * Returns the specified region.
34364      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34365      * @return {Roo.LayoutRegion}
34366      */
34367     getRegion : function(target){
34368         return this.regions[target.toLowerCase()];
34369     },
34370
34371     onWindowResize : function(){
34372         if(this.monitorWindowResize){
34373             this.layout();
34374         }
34375     }
34376 });
34377 /*
34378  * Based on:
34379  * Ext JS Library 1.1.1
34380  * Copyright(c) 2006-2007, Ext JS, LLC.
34381  *
34382  * Originally Released Under LGPL - original licence link has changed is not relivant.
34383  *
34384  * Fork - LGPL
34385  * <script type="text/javascript">
34386  */
34387 /**
34388  * @class Roo.bootstrap.layout.Border
34389  * @extends Roo.bootstrap.layout.Manager
34390  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34391  * please see: examples/bootstrap/nested.html<br><br>
34392  
34393 <b>The container the layout is rendered into can be either the body element or any other element.
34394 If it is not the body element, the container needs to either be an absolute positioned element,
34395 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34396 the container size if it is not the body element.</b>
34397
34398 * @constructor
34399 * Create a new Border
34400 * @param {Object} config Configuration options
34401  */
34402 Roo.bootstrap.layout.Border = function(config){
34403     config = config || {};
34404     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34405     
34406     
34407     
34408     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34409         if(config[region]){
34410             config[region].region = region;
34411             this.addRegion(config[region]);
34412         }
34413     },this);
34414     
34415 };
34416
34417 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34418
34419 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34420     /**
34421      * Creates and adds a new region if it doesn't already exist.
34422      * @param {String} target The target region key (north, south, east, west or center).
34423      * @param {Object} config The regions config object
34424      * @return {BorderLayoutRegion} The new region
34425      */
34426     addRegion : function(config)
34427     {
34428         if(!this.regions[config.region]){
34429             var r = this.factory(config);
34430             this.bindRegion(r);
34431         }
34432         return this.regions[config.region];
34433     },
34434
34435     // private (kinda)
34436     bindRegion : function(r){
34437         this.regions[r.config.region] = r;
34438         
34439         r.on("visibilitychange",    this.layout, this);
34440         r.on("paneladded",          this.layout, this);
34441         r.on("panelremoved",        this.layout, this);
34442         r.on("invalidated",         this.layout, this);
34443         r.on("resized",             this.onRegionResized, this);
34444         r.on("collapsed",           this.onRegionCollapsed, this);
34445         r.on("expanded",            this.onRegionExpanded, this);
34446     },
34447
34448     /**
34449      * Performs a layout update.
34450      */
34451     layout : function()
34452     {
34453         if(this.updating) {
34454             return;
34455         }
34456         
34457         // render all the rebions if they have not been done alreayd?
34458         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34459             if(this.regions[region] && !this.regions[region].bodyEl){
34460                 this.regions[region].onRender(this.el)
34461             }
34462         },this);
34463         
34464         var size = this.getViewSize();
34465         var w = size.width;
34466         var h = size.height;
34467         var centerW = w;
34468         var centerH = h;
34469         var centerY = 0;
34470         var centerX = 0;
34471         //var x = 0, y = 0;
34472
34473         var rs = this.regions;
34474         var north = rs["north"];
34475         var south = rs["south"]; 
34476         var west = rs["west"];
34477         var east = rs["east"];
34478         var center = rs["center"];
34479         //if(this.hideOnLayout){ // not supported anymore
34480             //c.el.setStyle("display", "none");
34481         //}
34482         if(north && north.isVisible()){
34483             var b = north.getBox();
34484             var m = north.getMargins();
34485             b.width = w - (m.left+m.right);
34486             b.x = m.left;
34487             b.y = m.top;
34488             centerY = b.height + b.y + m.bottom;
34489             centerH -= centerY;
34490             north.updateBox(this.safeBox(b));
34491         }
34492         if(south && south.isVisible()){
34493             var b = south.getBox();
34494             var m = south.getMargins();
34495             b.width = w - (m.left+m.right);
34496             b.x = m.left;
34497             var totalHeight = (b.height + m.top + m.bottom);
34498             b.y = h - totalHeight + m.top;
34499             centerH -= totalHeight;
34500             south.updateBox(this.safeBox(b));
34501         }
34502         if(west && west.isVisible()){
34503             var b = west.getBox();
34504             var m = west.getMargins();
34505             b.height = centerH - (m.top+m.bottom);
34506             b.x = m.left;
34507             b.y = centerY + m.top;
34508             var totalWidth = (b.width + m.left + m.right);
34509             centerX += totalWidth;
34510             centerW -= totalWidth;
34511             west.updateBox(this.safeBox(b));
34512         }
34513         if(east && east.isVisible()){
34514             var b = east.getBox();
34515             var m = east.getMargins();
34516             b.height = centerH - (m.top+m.bottom);
34517             var totalWidth = (b.width + m.left + m.right);
34518             b.x = w - totalWidth + m.left;
34519             b.y = centerY + m.top;
34520             centerW -= totalWidth;
34521             east.updateBox(this.safeBox(b));
34522         }
34523         if(center){
34524             var m = center.getMargins();
34525             var centerBox = {
34526                 x: centerX + m.left,
34527                 y: centerY + m.top,
34528                 width: centerW - (m.left+m.right),
34529                 height: centerH - (m.top+m.bottom)
34530             };
34531             //if(this.hideOnLayout){
34532                 //center.el.setStyle("display", "block");
34533             //}
34534             center.updateBox(this.safeBox(centerBox));
34535         }
34536         this.el.repaint();
34537         this.fireEvent("layout", this);
34538     },
34539
34540     // private
34541     safeBox : function(box){
34542         box.width = Math.max(0, box.width);
34543         box.height = Math.max(0, box.height);
34544         return box;
34545     },
34546
34547     /**
34548      * Adds a ContentPanel (or subclass) to this layout.
34549      * @param {String} target The target region key (north, south, east, west or center).
34550      * @param {Roo.ContentPanel} panel The panel to add
34551      * @return {Roo.ContentPanel} The added panel
34552      */
34553     add : function(target, panel){
34554          
34555         target = target.toLowerCase();
34556         return this.regions[target].add(panel);
34557     },
34558
34559     /**
34560      * Remove a ContentPanel (or subclass) to this layout.
34561      * @param {String} target The target region key (north, south, east, west or center).
34562      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34563      * @return {Roo.ContentPanel} The removed panel
34564      */
34565     remove : function(target, panel){
34566         target = target.toLowerCase();
34567         return this.regions[target].remove(panel);
34568     },
34569
34570     /**
34571      * Searches all regions for a panel with the specified id
34572      * @param {String} panelId
34573      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34574      */
34575     findPanel : function(panelId){
34576         var rs = this.regions;
34577         for(var target in rs){
34578             if(typeof rs[target] != "function"){
34579                 var p = rs[target].getPanel(panelId);
34580                 if(p){
34581                     return p;
34582                 }
34583             }
34584         }
34585         return null;
34586     },
34587
34588     /**
34589      * Searches all regions for a panel with the specified id and activates (shows) it.
34590      * @param {String/ContentPanel} panelId The panels id or the panel itself
34591      * @return {Roo.ContentPanel} The shown panel or null
34592      */
34593     showPanel : function(panelId) {
34594       var rs = this.regions;
34595       for(var target in rs){
34596          var r = rs[target];
34597          if(typeof r != "function"){
34598             if(r.hasPanel(panelId)){
34599                return r.showPanel(panelId);
34600             }
34601          }
34602       }
34603       return null;
34604    },
34605
34606    /**
34607      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34608      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34609      */
34610    /*
34611     restoreState : function(provider){
34612         if(!provider){
34613             provider = Roo.state.Manager;
34614         }
34615         var sm = new Roo.LayoutStateManager();
34616         sm.init(this, provider);
34617     },
34618 */
34619  
34620  
34621     /**
34622      * Adds a xtype elements to the layout.
34623      * <pre><code>
34624
34625 layout.addxtype({
34626        xtype : 'ContentPanel',
34627        region: 'west',
34628        items: [ .... ]
34629    }
34630 );
34631
34632 layout.addxtype({
34633         xtype : 'NestedLayoutPanel',
34634         region: 'west',
34635         layout: {
34636            center: { },
34637            west: { }   
34638         },
34639         items : [ ... list of content panels or nested layout panels.. ]
34640    }
34641 );
34642 </code></pre>
34643      * @param {Object} cfg Xtype definition of item to add.
34644      */
34645     addxtype : function(cfg)
34646     {
34647         // basically accepts a pannel...
34648         // can accept a layout region..!?!?
34649         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34650         
34651         
34652         // theory?  children can only be panels??
34653         
34654         //if (!cfg.xtype.match(/Panel$/)) {
34655         //    return false;
34656         //}
34657         var ret = false;
34658         
34659         if (typeof(cfg.region) == 'undefined') {
34660             Roo.log("Failed to add Panel, region was not set");
34661             Roo.log(cfg);
34662             return false;
34663         }
34664         var region = cfg.region;
34665         delete cfg.region;
34666         
34667           
34668         var xitems = [];
34669         if (cfg.items) {
34670             xitems = cfg.items;
34671             delete cfg.items;
34672         }
34673         var nb = false;
34674         
34675         switch(cfg.xtype) 
34676         {
34677             case 'Content':  // ContentPanel (el, cfg)
34678             case 'Scroll':  // ContentPanel (el, cfg)
34679             case 'View': 
34680                 cfg.autoCreate = true;
34681                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34682                 //} else {
34683                 //    var el = this.el.createChild();
34684                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34685                 //}
34686                 
34687                 this.add(region, ret);
34688                 break;
34689             
34690             /*
34691             case 'TreePanel': // our new panel!
34692                 cfg.el = this.el.createChild();
34693                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34694                 this.add(region, ret);
34695                 break;
34696             */
34697             
34698             case 'Nest': 
34699                 // create a new Layout (which is  a Border Layout...
34700                 
34701                 var clayout = cfg.layout;
34702                 clayout.el  = this.el.createChild();
34703                 clayout.items   = clayout.items  || [];
34704                 
34705                 delete cfg.layout;
34706                 
34707                 // replace this exitems with the clayout ones..
34708                 xitems = clayout.items;
34709                  
34710                 // force background off if it's in center...
34711                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34712                     cfg.background = false;
34713                 }
34714                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
34715                 
34716                 
34717                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34718                 //console.log('adding nested layout panel '  + cfg.toSource());
34719                 this.add(region, ret);
34720                 nb = {}; /// find first...
34721                 break;
34722             
34723             case 'Grid':
34724                 
34725                 // needs grid and region
34726                 
34727                 //var el = this.getRegion(region).el.createChild();
34728                 /*
34729                  *var el = this.el.createChild();
34730                 // create the grid first...
34731                 cfg.grid.container = el;
34732                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34733                 */
34734                 
34735                 if (region == 'center' && this.active ) {
34736                     cfg.background = false;
34737                 }
34738                 
34739                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34740                 
34741                 this.add(region, ret);
34742                 /*
34743                 if (cfg.background) {
34744                     // render grid on panel activation (if panel background)
34745                     ret.on('activate', function(gp) {
34746                         if (!gp.grid.rendered) {
34747                     //        gp.grid.render(el);
34748                         }
34749                     });
34750                 } else {
34751                   //  cfg.grid.render(el);
34752                 }
34753                 */
34754                 break;
34755            
34756            
34757             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34758                 // it was the old xcomponent building that caused this before.
34759                 // espeically if border is the top element in the tree.
34760                 ret = this;
34761                 break; 
34762                 
34763                     
34764                 
34765                 
34766                 
34767             default:
34768                 /*
34769                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34770                     
34771                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34772                     this.add(region, ret);
34773                 } else {
34774                 */
34775                     Roo.log(cfg);
34776                     throw "Can not add '" + cfg.xtype + "' to Border";
34777                     return null;
34778              
34779                                 
34780              
34781         }
34782         this.beginUpdate();
34783         // add children..
34784         var region = '';
34785         var abn = {};
34786         Roo.each(xitems, function(i)  {
34787             region = nb && i.region ? i.region : false;
34788             
34789             var add = ret.addxtype(i);
34790            
34791             if (region) {
34792                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
34793                 if (!i.background) {
34794                     abn[region] = nb[region] ;
34795                 }
34796             }
34797             
34798         });
34799         this.endUpdate();
34800
34801         // make the last non-background panel active..
34802         //if (nb) { Roo.log(abn); }
34803         if (nb) {
34804             
34805             for(var r in abn) {
34806                 region = this.getRegion(r);
34807                 if (region) {
34808                     // tried using nb[r], but it does not work..
34809                      
34810                     region.showPanel(abn[r]);
34811                    
34812                 }
34813             }
34814         }
34815         return ret;
34816         
34817     },
34818     
34819     
34820 // private
34821     factory : function(cfg)
34822     {
34823         
34824         var validRegions = Roo.bootstrap.layout.Border.regions;
34825
34826         var target = cfg.region;
34827         cfg.mgr = this;
34828         
34829         var r = Roo.bootstrap.layout;
34830         Roo.log(target);
34831         switch(target){
34832             case "north":
34833                 return new r.North(cfg);
34834             case "south":
34835                 return new r.South(cfg);
34836             case "east":
34837                 return new r.East(cfg);
34838             case "west":
34839                 return new r.West(cfg);
34840             case "center":
34841                 return new r.Center(cfg);
34842         }
34843         throw 'Layout region "'+target+'" not supported.';
34844     }
34845     
34846     
34847 });
34848  /*
34849  * Based on:
34850  * Ext JS Library 1.1.1
34851  * Copyright(c) 2006-2007, Ext JS, LLC.
34852  *
34853  * Originally Released Under LGPL - original licence link has changed is not relivant.
34854  *
34855  * Fork - LGPL
34856  * <script type="text/javascript">
34857  */
34858  
34859 /**
34860  * @class Roo.bootstrap.layout.Basic
34861  * @extends Roo.util.Observable
34862  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
34863  * and does not have a titlebar, tabs or any other features. All it does is size and position 
34864  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
34865  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34866  * @cfg {string}   region  the region that it inhabits..
34867  * @cfg {bool}   skipConfig skip config?
34868  * 
34869
34870  */
34871 Roo.bootstrap.layout.Basic = function(config){
34872     
34873     this.mgr = config.mgr;
34874     
34875     this.position = config.region;
34876     
34877     var skipConfig = config.skipConfig;
34878     
34879     this.events = {
34880         /**
34881          * @scope Roo.BasicLayoutRegion
34882          */
34883         
34884         /**
34885          * @event beforeremove
34886          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
34887          * @param {Roo.LayoutRegion} this
34888          * @param {Roo.ContentPanel} panel The panel
34889          * @param {Object} e The cancel event object
34890          */
34891         "beforeremove" : true,
34892         /**
34893          * @event invalidated
34894          * Fires when the layout for this region is changed.
34895          * @param {Roo.LayoutRegion} this
34896          */
34897         "invalidated" : true,
34898         /**
34899          * @event visibilitychange
34900          * Fires when this region is shown or hidden 
34901          * @param {Roo.LayoutRegion} this
34902          * @param {Boolean} visibility true or false
34903          */
34904         "visibilitychange" : true,
34905         /**
34906          * @event paneladded
34907          * Fires when a panel is added. 
34908          * @param {Roo.LayoutRegion} this
34909          * @param {Roo.ContentPanel} panel The panel
34910          */
34911         "paneladded" : true,
34912         /**
34913          * @event panelremoved
34914          * Fires when a panel is removed. 
34915          * @param {Roo.LayoutRegion} this
34916          * @param {Roo.ContentPanel} panel The panel
34917          */
34918         "panelremoved" : true,
34919         /**
34920          * @event beforecollapse
34921          * Fires when this region before collapse.
34922          * @param {Roo.LayoutRegion} this
34923          */
34924         "beforecollapse" : true,
34925         /**
34926          * @event collapsed
34927          * Fires when this region is collapsed.
34928          * @param {Roo.LayoutRegion} this
34929          */
34930         "collapsed" : true,
34931         /**
34932          * @event expanded
34933          * Fires when this region is expanded.
34934          * @param {Roo.LayoutRegion} this
34935          */
34936         "expanded" : true,
34937         /**
34938          * @event slideshow
34939          * Fires when this region is slid into view.
34940          * @param {Roo.LayoutRegion} this
34941          */
34942         "slideshow" : true,
34943         /**
34944          * @event slidehide
34945          * Fires when this region slides out of view. 
34946          * @param {Roo.LayoutRegion} this
34947          */
34948         "slidehide" : true,
34949         /**
34950          * @event panelactivated
34951          * Fires when a panel is activated. 
34952          * @param {Roo.LayoutRegion} this
34953          * @param {Roo.ContentPanel} panel The activated panel
34954          */
34955         "panelactivated" : true,
34956         /**
34957          * @event resized
34958          * Fires when the user resizes this region. 
34959          * @param {Roo.LayoutRegion} this
34960          * @param {Number} newSize The new size (width for east/west, height for north/south)
34961          */
34962         "resized" : true
34963     };
34964     /** A collection of panels in this region. @type Roo.util.MixedCollection */
34965     this.panels = new Roo.util.MixedCollection();
34966     this.panels.getKey = this.getPanelId.createDelegate(this);
34967     this.box = null;
34968     this.activePanel = null;
34969     // ensure listeners are added...
34970     
34971     if (config.listeners || config.events) {
34972         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
34973             listeners : config.listeners || {},
34974             events : config.events || {}
34975         });
34976     }
34977     
34978     if(skipConfig !== true){
34979         this.applyConfig(config);
34980     }
34981 };
34982
34983 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
34984 {
34985     getPanelId : function(p){
34986         return p.getId();
34987     },
34988     
34989     applyConfig : function(config){
34990         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34991         this.config = config;
34992         
34993     },
34994     
34995     /**
34996      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
34997      * the width, for horizontal (north, south) the height.
34998      * @param {Number} newSize The new width or height
34999      */
35000     resizeTo : function(newSize){
35001         var el = this.el ? this.el :
35002                  (this.activePanel ? this.activePanel.getEl() : null);
35003         if(el){
35004             switch(this.position){
35005                 case "east":
35006                 case "west":
35007                     el.setWidth(newSize);
35008                     this.fireEvent("resized", this, newSize);
35009                 break;
35010                 case "north":
35011                 case "south":
35012                     el.setHeight(newSize);
35013                     this.fireEvent("resized", this, newSize);
35014                 break;                
35015             }
35016         }
35017     },
35018     
35019     getBox : function(){
35020         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
35021     },
35022     
35023     getMargins : function(){
35024         return this.margins;
35025     },
35026     
35027     updateBox : function(box){
35028         this.box = box;
35029         var el = this.activePanel.getEl();
35030         el.dom.style.left = box.x + "px";
35031         el.dom.style.top = box.y + "px";
35032         this.activePanel.setSize(box.width, box.height);
35033     },
35034     
35035     /**
35036      * Returns the container element for this region.
35037      * @return {Roo.Element}
35038      */
35039     getEl : function(){
35040         return this.activePanel;
35041     },
35042     
35043     /**
35044      * Returns true if this region is currently visible.
35045      * @return {Boolean}
35046      */
35047     isVisible : function(){
35048         return this.activePanel ? true : false;
35049     },
35050     
35051     setActivePanel : function(panel){
35052         panel = this.getPanel(panel);
35053         if(this.activePanel && this.activePanel != panel){
35054             this.activePanel.setActiveState(false);
35055             this.activePanel.getEl().setLeftTop(-10000,-10000);
35056         }
35057         this.activePanel = panel;
35058         panel.setActiveState(true);
35059         if(this.box){
35060             panel.setSize(this.box.width, this.box.height);
35061         }
35062         this.fireEvent("panelactivated", this, panel);
35063         this.fireEvent("invalidated");
35064     },
35065     
35066     /**
35067      * Show the specified panel.
35068      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
35069      * @return {Roo.ContentPanel} The shown panel or null
35070      */
35071     showPanel : function(panel){
35072         panel = this.getPanel(panel);
35073         if(panel){
35074             this.setActivePanel(panel);
35075         }
35076         return panel;
35077     },
35078     
35079     /**
35080      * Get the active panel for this region.
35081      * @return {Roo.ContentPanel} The active panel or null
35082      */
35083     getActivePanel : function(){
35084         return this.activePanel;
35085     },
35086     
35087     /**
35088      * Add the passed ContentPanel(s)
35089      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35090      * @return {Roo.ContentPanel} The panel added (if only one was added)
35091      */
35092     add : function(panel){
35093         if(arguments.length > 1){
35094             for(var i = 0, len = arguments.length; i < len; i++) {
35095                 this.add(arguments[i]);
35096             }
35097             return null;
35098         }
35099         if(this.hasPanel(panel)){
35100             this.showPanel(panel);
35101             return panel;
35102         }
35103         var el = panel.getEl();
35104         if(el.dom.parentNode != this.mgr.el.dom){
35105             this.mgr.el.dom.appendChild(el.dom);
35106         }
35107         if(panel.setRegion){
35108             panel.setRegion(this);
35109         }
35110         this.panels.add(panel);
35111         el.setStyle("position", "absolute");
35112         if(!panel.background){
35113             this.setActivePanel(panel);
35114             if(this.config.initialSize && this.panels.getCount()==1){
35115                 this.resizeTo(this.config.initialSize);
35116             }
35117         }
35118         this.fireEvent("paneladded", this, panel);
35119         return panel;
35120     },
35121     
35122     /**
35123      * Returns true if the panel is in this region.
35124      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35125      * @return {Boolean}
35126      */
35127     hasPanel : function(panel){
35128         if(typeof panel == "object"){ // must be panel obj
35129             panel = panel.getId();
35130         }
35131         return this.getPanel(panel) ? true : false;
35132     },
35133     
35134     /**
35135      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35136      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35137      * @param {Boolean} preservePanel Overrides the config preservePanel option
35138      * @return {Roo.ContentPanel} The panel that was removed
35139      */
35140     remove : function(panel, preservePanel){
35141         panel = this.getPanel(panel);
35142         if(!panel){
35143             return null;
35144         }
35145         var e = {};
35146         this.fireEvent("beforeremove", this, panel, e);
35147         if(e.cancel === true){
35148             return null;
35149         }
35150         var panelId = panel.getId();
35151         this.panels.removeKey(panelId);
35152         return panel;
35153     },
35154     
35155     /**
35156      * Returns the panel specified or null if it's not in this region.
35157      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35158      * @return {Roo.ContentPanel}
35159      */
35160     getPanel : function(id){
35161         if(typeof id == "object"){ // must be panel obj
35162             return id;
35163         }
35164         return this.panels.get(id);
35165     },
35166     
35167     /**
35168      * Returns this regions position (north/south/east/west/center).
35169      * @return {String} 
35170      */
35171     getPosition: function(){
35172         return this.position;    
35173     }
35174 });/*
35175  * Based on:
35176  * Ext JS Library 1.1.1
35177  * Copyright(c) 2006-2007, Ext JS, LLC.
35178  *
35179  * Originally Released Under LGPL - original licence link has changed is not relivant.
35180  *
35181  * Fork - LGPL
35182  * <script type="text/javascript">
35183  */
35184  
35185 /**
35186  * @class Roo.bootstrap.layout.Region
35187  * @extends Roo.bootstrap.layout.Basic
35188  * This class represents a region in a layout manager.
35189  
35190  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35191  * @cfg {Object}    cmargins        Margins for the element when collapsed (defaults to: north/south {top: 2, left: 0, right:0, bottom: 2} or east/west {top: 0, left: 2, right:2, bottom: 0})
35192  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35193  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35194  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35195  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35196  * @cfg {String}    title           The title for the region (overrides panel titles)
35197  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35198  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35199  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35200  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35201  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35202  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35203  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35204  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35205  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35206  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35207
35208  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35209  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35210  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35211  * @cfg {Number}    width           For East/West panels
35212  * @cfg {Number}    height          For North/South panels
35213  * @cfg {Boolean}   split           To show the splitter
35214  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35215  * 
35216  * @cfg {string}   cls             Extra CSS classes to add to region
35217  * 
35218  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35219  * @cfg {string}   region  the region that it inhabits..
35220  *
35221
35222  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35223  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35224
35225  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35226  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35227  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35228  */
35229 Roo.bootstrap.layout.Region = function(config)
35230 {
35231     this.applyConfig(config);
35232
35233     var mgr = config.mgr;
35234     var pos = config.region;
35235     config.skipConfig = true;
35236     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35237     
35238     if (mgr.el) {
35239         this.onRender(mgr.el);   
35240     }
35241      
35242     this.visible = true;
35243     this.collapsed = false;
35244     this.unrendered_panels = [];
35245 };
35246
35247 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35248
35249     position: '', // set by wrapper (eg. north/south etc..)
35250     unrendered_panels : null,  // unrendered panels.
35251     createBody : function(){
35252         /** This region's body element 
35253         * @type Roo.Element */
35254         this.bodyEl = this.el.createChild({
35255                 tag: "div",
35256                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35257         });
35258     },
35259
35260     onRender: function(ctr, pos)
35261     {
35262         var dh = Roo.DomHelper;
35263         /** This region's container element 
35264         * @type Roo.Element */
35265         this.el = dh.append(ctr.dom, {
35266                 tag: "div",
35267                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35268             }, true);
35269         /** This region's title element 
35270         * @type Roo.Element */
35271     
35272         this.titleEl = dh.append(this.el.dom,
35273             {
35274                     tag: "div",
35275                     unselectable: "on",
35276                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35277                     children:[
35278                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35279                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35280                     ]}, true);
35281         
35282         this.titleEl.enableDisplayMode();
35283         /** This region's title text element 
35284         * @type HTMLElement */
35285         this.titleTextEl = this.titleEl.dom.firstChild;
35286         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35287         /*
35288         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35289         this.closeBtn.enableDisplayMode();
35290         this.closeBtn.on("click", this.closeClicked, this);
35291         this.closeBtn.hide();
35292     */
35293         this.createBody(this.config);
35294         if(this.config.hideWhenEmpty){
35295             this.hide();
35296             this.on("paneladded", this.validateVisibility, this);
35297             this.on("panelremoved", this.validateVisibility, this);
35298         }
35299         if(this.autoScroll){
35300             this.bodyEl.setStyle("overflow", "auto");
35301         }else{
35302             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35303         }
35304         //if(c.titlebar !== false){
35305             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35306                 this.titleEl.hide();
35307             }else{
35308                 this.titleEl.show();
35309                 if(this.config.title){
35310                     this.titleTextEl.innerHTML = this.config.title;
35311                 }
35312             }
35313         //}
35314         if(this.config.collapsed){
35315             this.collapse(true);
35316         }
35317         if(this.config.hidden){
35318             this.hide();
35319         }
35320         
35321         if (this.unrendered_panels && this.unrendered_panels.length) {
35322             for (var i =0;i< this.unrendered_panels.length; i++) {
35323                 this.add(this.unrendered_panels[i]);
35324             }
35325             this.unrendered_panels = null;
35326             
35327         }
35328         
35329     },
35330     
35331     applyConfig : function(c)
35332     {
35333         /*
35334          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35335             var dh = Roo.DomHelper;
35336             if(c.titlebar !== false){
35337                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35338                 this.collapseBtn.on("click", this.collapse, this);
35339                 this.collapseBtn.enableDisplayMode();
35340                 /*
35341                 if(c.showPin === true || this.showPin){
35342                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35343                     this.stickBtn.enableDisplayMode();
35344                     this.stickBtn.on("click", this.expand, this);
35345                     this.stickBtn.hide();
35346                 }
35347                 
35348             }
35349             */
35350             /** This region's collapsed element
35351             * @type Roo.Element */
35352             /*
35353              *
35354             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35355                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35356             ]}, true);
35357             
35358             if(c.floatable !== false){
35359                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35360                this.collapsedEl.on("click", this.collapseClick, this);
35361             }
35362
35363             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35364                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35365                    id: "message", unselectable: "on", style:{"float":"left"}});
35366                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35367              }
35368             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35369             this.expandBtn.on("click", this.expand, this);
35370             
35371         }
35372         
35373         if(this.collapseBtn){
35374             this.collapseBtn.setVisible(c.collapsible == true);
35375         }
35376         
35377         this.cmargins = c.cmargins || this.cmargins ||
35378                          (this.position == "west" || this.position == "east" ?
35379                              {top: 0, left: 2, right:2, bottom: 0} :
35380                              {top: 2, left: 0, right:0, bottom: 2});
35381         */
35382         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35383         
35384         
35385         this.bottomTabs = c.tabPosition != "top";
35386         
35387         this.autoScroll = c.autoScroll || false;
35388         
35389         
35390        
35391         
35392         this.duration = c.duration || .30;
35393         this.slideDuration = c.slideDuration || .45;
35394         this.config = c;
35395        
35396     },
35397     /**
35398      * Returns true if this region is currently visible.
35399      * @return {Boolean}
35400      */
35401     isVisible : function(){
35402         return this.visible;
35403     },
35404
35405     /**
35406      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35407      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35408      */
35409     //setCollapsedTitle : function(title){
35410     //    title = title || "&#160;";
35411      //   if(this.collapsedTitleTextEl){
35412       //      this.collapsedTitleTextEl.innerHTML = title;
35413        // }
35414     //},
35415
35416     getBox : function(){
35417         var b;
35418       //  if(!this.collapsed){
35419             b = this.el.getBox(false, true);
35420        // }else{
35421           //  b = this.collapsedEl.getBox(false, true);
35422         //}
35423         return b;
35424     },
35425
35426     getMargins : function(){
35427         return this.margins;
35428         //return this.collapsed ? this.cmargins : this.margins;
35429     },
35430 /*
35431     highlight : function(){
35432         this.el.addClass("x-layout-panel-dragover");
35433     },
35434
35435     unhighlight : function(){
35436         this.el.removeClass("x-layout-panel-dragover");
35437     },
35438 */
35439     updateBox : function(box)
35440     {
35441         if (!this.bodyEl) {
35442             return; // not rendered yet..
35443         }
35444         
35445         this.box = box;
35446         if(!this.collapsed){
35447             this.el.dom.style.left = box.x + "px";
35448             this.el.dom.style.top = box.y + "px";
35449             this.updateBody(box.width, box.height);
35450         }else{
35451             this.collapsedEl.dom.style.left = box.x + "px";
35452             this.collapsedEl.dom.style.top = box.y + "px";
35453             this.collapsedEl.setSize(box.width, box.height);
35454         }
35455         if(this.tabs){
35456             this.tabs.autoSizeTabs();
35457         }
35458     },
35459
35460     updateBody : function(w, h)
35461     {
35462         if(w !== null){
35463             this.el.setWidth(w);
35464             w -= this.el.getBorderWidth("rl");
35465             if(this.config.adjustments){
35466                 w += this.config.adjustments[0];
35467             }
35468         }
35469         if(h !== null && h > 0){
35470             this.el.setHeight(h);
35471             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35472             h -= this.el.getBorderWidth("tb");
35473             if(this.config.adjustments){
35474                 h += this.config.adjustments[1];
35475             }
35476             this.bodyEl.setHeight(h);
35477             if(this.tabs){
35478                 h = this.tabs.syncHeight(h);
35479             }
35480         }
35481         if(this.panelSize){
35482             w = w !== null ? w : this.panelSize.width;
35483             h = h !== null ? h : this.panelSize.height;
35484         }
35485         if(this.activePanel){
35486             var el = this.activePanel.getEl();
35487             w = w !== null ? w : el.getWidth();
35488             h = h !== null ? h : el.getHeight();
35489             this.panelSize = {width: w, height: h};
35490             this.activePanel.setSize(w, h);
35491         }
35492         if(Roo.isIE && this.tabs){
35493             this.tabs.el.repaint();
35494         }
35495     },
35496
35497     /**
35498      * Returns the container element for this region.
35499      * @return {Roo.Element}
35500      */
35501     getEl : function(){
35502         return this.el;
35503     },
35504
35505     /**
35506      * Hides this region.
35507      */
35508     hide : function(){
35509         //if(!this.collapsed){
35510             this.el.dom.style.left = "-2000px";
35511             this.el.hide();
35512         //}else{
35513          //   this.collapsedEl.dom.style.left = "-2000px";
35514          //   this.collapsedEl.hide();
35515        // }
35516         this.visible = false;
35517         this.fireEvent("visibilitychange", this, false);
35518     },
35519
35520     /**
35521      * Shows this region if it was previously hidden.
35522      */
35523     show : function(){
35524         //if(!this.collapsed){
35525             this.el.show();
35526         //}else{
35527         //    this.collapsedEl.show();
35528        // }
35529         this.visible = true;
35530         this.fireEvent("visibilitychange", this, true);
35531     },
35532 /*
35533     closeClicked : function(){
35534         if(this.activePanel){
35535             this.remove(this.activePanel);
35536         }
35537     },
35538
35539     collapseClick : function(e){
35540         if(this.isSlid){
35541            e.stopPropagation();
35542            this.slideIn();
35543         }else{
35544            e.stopPropagation();
35545            this.slideOut();
35546         }
35547     },
35548 */
35549     /**
35550      * Collapses this region.
35551      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35552      */
35553     /*
35554     collapse : function(skipAnim, skipCheck = false){
35555         if(this.collapsed) {
35556             return;
35557         }
35558         
35559         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35560             
35561             this.collapsed = true;
35562             if(this.split){
35563                 this.split.el.hide();
35564             }
35565             if(this.config.animate && skipAnim !== true){
35566                 this.fireEvent("invalidated", this);
35567                 this.animateCollapse();
35568             }else{
35569                 this.el.setLocation(-20000,-20000);
35570                 this.el.hide();
35571                 this.collapsedEl.show();
35572                 this.fireEvent("collapsed", this);
35573                 this.fireEvent("invalidated", this);
35574             }
35575         }
35576         
35577     },
35578 */
35579     animateCollapse : function(){
35580         // overridden
35581     },
35582
35583     /**
35584      * Expands this region if it was previously collapsed.
35585      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35586      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35587      */
35588     /*
35589     expand : function(e, skipAnim){
35590         if(e) {
35591             e.stopPropagation();
35592         }
35593         if(!this.collapsed || this.el.hasActiveFx()) {
35594             return;
35595         }
35596         if(this.isSlid){
35597             this.afterSlideIn();
35598             skipAnim = true;
35599         }
35600         this.collapsed = false;
35601         if(this.config.animate && skipAnim !== true){
35602             this.animateExpand();
35603         }else{
35604             this.el.show();
35605             if(this.split){
35606                 this.split.el.show();
35607             }
35608             this.collapsedEl.setLocation(-2000,-2000);
35609             this.collapsedEl.hide();
35610             this.fireEvent("invalidated", this);
35611             this.fireEvent("expanded", this);
35612         }
35613     },
35614 */
35615     animateExpand : function(){
35616         // overridden
35617     },
35618
35619     initTabs : function()
35620     {
35621         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35622         
35623         var ts = new Roo.bootstrap.panel.Tabs({
35624                 el: this.bodyEl.dom,
35625                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35626                 disableTooltips: this.config.disableTabTips,
35627                 toolbar : this.config.toolbar
35628             });
35629         
35630         if(this.config.hideTabs){
35631             ts.stripWrap.setDisplayed(false);
35632         }
35633         this.tabs = ts;
35634         ts.resizeTabs = this.config.resizeTabs === true;
35635         ts.minTabWidth = this.config.minTabWidth || 40;
35636         ts.maxTabWidth = this.config.maxTabWidth || 250;
35637         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35638         ts.monitorResize = false;
35639         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35640         ts.bodyEl.addClass('roo-layout-tabs-body');
35641         this.panels.each(this.initPanelAsTab, this);
35642     },
35643
35644     initPanelAsTab : function(panel){
35645         var ti = this.tabs.addTab(
35646             panel.getEl().id,
35647             panel.getTitle(),
35648             null,
35649             this.config.closeOnTab && panel.isClosable(),
35650             panel.tpl
35651         );
35652         if(panel.tabTip !== undefined){
35653             ti.setTooltip(panel.tabTip);
35654         }
35655         ti.on("activate", function(){
35656               this.setActivePanel(panel);
35657         }, this);
35658         
35659         if(this.config.closeOnTab){
35660             ti.on("beforeclose", function(t, e){
35661                 e.cancel = true;
35662                 this.remove(panel);
35663             }, this);
35664         }
35665         
35666         panel.tabItem = ti;
35667         
35668         return ti;
35669     },
35670
35671     updatePanelTitle : function(panel, title)
35672     {
35673         if(this.activePanel == panel){
35674             this.updateTitle(title);
35675         }
35676         if(this.tabs){
35677             var ti = this.tabs.getTab(panel.getEl().id);
35678             ti.setText(title);
35679             if(panel.tabTip !== undefined){
35680                 ti.setTooltip(panel.tabTip);
35681             }
35682         }
35683     },
35684
35685     updateTitle : function(title){
35686         if(this.titleTextEl && !this.config.title){
35687             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
35688         }
35689     },
35690
35691     setActivePanel : function(panel)
35692     {
35693         panel = this.getPanel(panel);
35694         if(this.activePanel && this.activePanel != panel){
35695             if(this.activePanel.setActiveState(false) === false){
35696                 return;
35697             }
35698         }
35699         this.activePanel = panel;
35700         panel.setActiveState(true);
35701         if(this.panelSize){
35702             panel.setSize(this.panelSize.width, this.panelSize.height);
35703         }
35704         if(this.closeBtn){
35705             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35706         }
35707         this.updateTitle(panel.getTitle());
35708         if(this.tabs){
35709             this.fireEvent("invalidated", this);
35710         }
35711         this.fireEvent("panelactivated", this, panel);
35712     },
35713
35714     /**
35715      * Shows the specified panel.
35716      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35717      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35718      */
35719     showPanel : function(panel)
35720     {
35721         panel = this.getPanel(panel);
35722         if(panel){
35723             if(this.tabs){
35724                 var tab = this.tabs.getTab(panel.getEl().id);
35725                 if(tab.isHidden()){
35726                     this.tabs.unhideTab(tab.id);
35727                 }
35728                 tab.activate();
35729             }else{
35730                 this.setActivePanel(panel);
35731             }
35732         }
35733         return panel;
35734     },
35735
35736     /**
35737      * Get the active panel for this region.
35738      * @return {Roo.ContentPanel} The active panel or null
35739      */
35740     getActivePanel : function(){
35741         return this.activePanel;
35742     },
35743
35744     validateVisibility : function(){
35745         if(this.panels.getCount() < 1){
35746             this.updateTitle("&#160;");
35747             this.closeBtn.hide();
35748             this.hide();
35749         }else{
35750             if(!this.isVisible()){
35751                 this.show();
35752             }
35753         }
35754     },
35755
35756     /**
35757      * Adds the passed ContentPanel(s) to this region.
35758      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35759      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35760      */
35761     add : function(panel)
35762     {
35763         if(arguments.length > 1){
35764             for(var i = 0, len = arguments.length; i < len; i++) {
35765                 this.add(arguments[i]);
35766             }
35767             return null;
35768         }
35769         
35770         // if we have not been rendered yet, then we can not really do much of this..
35771         if (!this.bodyEl) {
35772             this.unrendered_panels.push(panel);
35773             return panel;
35774         }
35775         
35776         
35777         
35778         
35779         if(this.hasPanel(panel)){
35780             this.showPanel(panel);
35781             return panel;
35782         }
35783         panel.setRegion(this);
35784         this.panels.add(panel);
35785        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
35786             // sinle panel - no tab...?? would it not be better to render it with the tabs,
35787             // and hide them... ???
35788             this.bodyEl.dom.appendChild(panel.getEl().dom);
35789             if(panel.background !== true){
35790                 this.setActivePanel(panel);
35791             }
35792             this.fireEvent("paneladded", this, panel);
35793             return panel;
35794         }
35795         */
35796         if(!this.tabs){
35797             this.initTabs();
35798         }else{
35799             this.initPanelAsTab(panel);
35800         }
35801         
35802         
35803         if(panel.background !== true){
35804             this.tabs.activate(panel.getEl().id);
35805         }
35806         this.fireEvent("paneladded", this, panel);
35807         return panel;
35808     },
35809
35810     /**
35811      * Hides the tab for the specified panel.
35812      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35813      */
35814     hidePanel : function(panel){
35815         if(this.tabs && (panel = this.getPanel(panel))){
35816             this.tabs.hideTab(panel.getEl().id);
35817         }
35818     },
35819
35820     /**
35821      * Unhides the tab for a previously hidden panel.
35822      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35823      */
35824     unhidePanel : function(panel){
35825         if(this.tabs && (panel = this.getPanel(panel))){
35826             this.tabs.unhideTab(panel.getEl().id);
35827         }
35828     },
35829
35830     clearPanels : function(){
35831         while(this.panels.getCount() > 0){
35832              this.remove(this.panels.first());
35833         }
35834     },
35835
35836     /**
35837      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35838      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35839      * @param {Boolean} preservePanel Overrides the config preservePanel option
35840      * @return {Roo.ContentPanel} The panel that was removed
35841      */
35842     remove : function(panel, preservePanel)
35843     {
35844         panel = this.getPanel(panel);
35845         if(!panel){
35846             return null;
35847         }
35848         var e = {};
35849         this.fireEvent("beforeremove", this, panel, e);
35850         if(e.cancel === true){
35851             return null;
35852         }
35853         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
35854         var panelId = panel.getId();
35855         this.panels.removeKey(panelId);
35856         if(preservePanel){
35857             document.body.appendChild(panel.getEl().dom);
35858         }
35859         if(this.tabs){
35860             this.tabs.removeTab(panel.getEl().id);
35861         }else if (!preservePanel){
35862             this.bodyEl.dom.removeChild(panel.getEl().dom);
35863         }
35864         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
35865             var p = this.panels.first();
35866             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
35867             tempEl.appendChild(p.getEl().dom);
35868             this.bodyEl.update("");
35869             this.bodyEl.dom.appendChild(p.getEl().dom);
35870             tempEl = null;
35871             this.updateTitle(p.getTitle());
35872             this.tabs = null;
35873             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
35874             this.setActivePanel(p);
35875         }
35876         panel.setRegion(null);
35877         if(this.activePanel == panel){
35878             this.activePanel = null;
35879         }
35880         if(this.config.autoDestroy !== false && preservePanel !== true){
35881             try{panel.destroy();}catch(e){}
35882         }
35883         this.fireEvent("panelremoved", this, panel);
35884         return panel;
35885     },
35886
35887     /**
35888      * Returns the TabPanel component used by this region
35889      * @return {Roo.TabPanel}
35890      */
35891     getTabs : function(){
35892         return this.tabs;
35893     },
35894
35895     createTool : function(parentEl, className){
35896         var btn = Roo.DomHelper.append(parentEl, {
35897             tag: "div",
35898             cls: "x-layout-tools-button",
35899             children: [ {
35900                 tag: "div",
35901                 cls: "roo-layout-tools-button-inner " + className,
35902                 html: "&#160;"
35903             }]
35904         }, true);
35905         btn.addClassOnOver("roo-layout-tools-button-over");
35906         return btn;
35907     }
35908 });/*
35909  * Based on:
35910  * Ext JS Library 1.1.1
35911  * Copyright(c) 2006-2007, Ext JS, LLC.
35912  *
35913  * Originally Released Under LGPL - original licence link has changed is not relivant.
35914  *
35915  * Fork - LGPL
35916  * <script type="text/javascript">
35917  */
35918  
35919
35920
35921 /**
35922  * @class Roo.SplitLayoutRegion
35923  * @extends Roo.LayoutRegion
35924  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
35925  */
35926 Roo.bootstrap.layout.Split = function(config){
35927     this.cursor = config.cursor;
35928     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
35929 };
35930
35931 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
35932 {
35933     splitTip : "Drag to resize.",
35934     collapsibleSplitTip : "Drag to resize. Double click to hide.",
35935     useSplitTips : false,
35936
35937     applyConfig : function(config){
35938         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
35939     },
35940     
35941     onRender : function(ctr,pos) {
35942         
35943         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
35944         if(!this.config.split){
35945             return;
35946         }
35947         if(!this.split){
35948             
35949             var splitEl = Roo.DomHelper.append(ctr.dom,  {
35950                             tag: "div",
35951                             id: this.el.id + "-split",
35952                             cls: "roo-layout-split roo-layout-split-"+this.position,
35953                             html: "&#160;"
35954             });
35955             /** The SplitBar for this region 
35956             * @type Roo.SplitBar */
35957             // does not exist yet...
35958             Roo.log([this.position, this.orientation]);
35959             
35960             this.split = new Roo.bootstrap.SplitBar({
35961                 dragElement : splitEl,
35962                 resizingElement: this.el,
35963                 orientation : this.orientation
35964             });
35965             
35966             this.split.on("moved", this.onSplitMove, this);
35967             this.split.useShim = this.config.useShim === true;
35968             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
35969             if(this.useSplitTips){
35970                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
35971             }
35972             //if(config.collapsible){
35973             //    this.split.el.on("dblclick", this.collapse,  this);
35974             //}
35975         }
35976         if(typeof this.config.minSize != "undefined"){
35977             this.split.minSize = this.config.minSize;
35978         }
35979         if(typeof this.config.maxSize != "undefined"){
35980             this.split.maxSize = this.config.maxSize;
35981         }
35982         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
35983             this.hideSplitter();
35984         }
35985         
35986     },
35987
35988     getHMaxSize : function(){
35989          var cmax = this.config.maxSize || 10000;
35990          var center = this.mgr.getRegion("center");
35991          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
35992     },
35993
35994     getVMaxSize : function(){
35995          var cmax = this.config.maxSize || 10000;
35996          var center = this.mgr.getRegion("center");
35997          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
35998     },
35999
36000     onSplitMove : function(split, newSize){
36001         this.fireEvent("resized", this, newSize);
36002     },
36003     
36004     /** 
36005      * Returns the {@link Roo.SplitBar} for this region.
36006      * @return {Roo.SplitBar}
36007      */
36008     getSplitBar : function(){
36009         return this.split;
36010     },
36011     
36012     hide : function(){
36013         this.hideSplitter();
36014         Roo.bootstrap.layout.Split.superclass.hide.call(this);
36015     },
36016
36017     hideSplitter : function(){
36018         if(this.split){
36019             this.split.el.setLocation(-2000,-2000);
36020             this.split.el.hide();
36021         }
36022     },
36023
36024     show : function(){
36025         if(this.split){
36026             this.split.el.show();
36027         }
36028         Roo.bootstrap.layout.Split.superclass.show.call(this);
36029     },
36030     
36031     beforeSlide: function(){
36032         if(Roo.isGecko){// firefox overflow auto bug workaround
36033             this.bodyEl.clip();
36034             if(this.tabs) {
36035                 this.tabs.bodyEl.clip();
36036             }
36037             if(this.activePanel){
36038                 this.activePanel.getEl().clip();
36039                 
36040                 if(this.activePanel.beforeSlide){
36041                     this.activePanel.beforeSlide();
36042                 }
36043             }
36044         }
36045     },
36046     
36047     afterSlide : function(){
36048         if(Roo.isGecko){// firefox overflow auto bug workaround
36049             this.bodyEl.unclip();
36050             if(this.tabs) {
36051                 this.tabs.bodyEl.unclip();
36052             }
36053             if(this.activePanel){
36054                 this.activePanel.getEl().unclip();
36055                 if(this.activePanel.afterSlide){
36056                     this.activePanel.afterSlide();
36057                 }
36058             }
36059         }
36060     },
36061
36062     initAutoHide : function(){
36063         if(this.autoHide !== false){
36064             if(!this.autoHideHd){
36065                 var st = new Roo.util.DelayedTask(this.slideIn, this);
36066                 this.autoHideHd = {
36067                     "mouseout": function(e){
36068                         if(!e.within(this.el, true)){
36069                             st.delay(500);
36070                         }
36071                     },
36072                     "mouseover" : function(e){
36073                         st.cancel();
36074                     },
36075                     scope : this
36076                 };
36077             }
36078             this.el.on(this.autoHideHd);
36079         }
36080     },
36081
36082     clearAutoHide : function(){
36083         if(this.autoHide !== false){
36084             this.el.un("mouseout", this.autoHideHd.mouseout);
36085             this.el.un("mouseover", this.autoHideHd.mouseover);
36086         }
36087     },
36088
36089     clearMonitor : function(){
36090         Roo.get(document).un("click", this.slideInIf, this);
36091     },
36092
36093     // these names are backwards but not changed for compat
36094     slideOut : function(){
36095         if(this.isSlid || this.el.hasActiveFx()){
36096             return;
36097         }
36098         this.isSlid = true;
36099         if(this.collapseBtn){
36100             this.collapseBtn.hide();
36101         }
36102         this.closeBtnState = this.closeBtn.getStyle('display');
36103         this.closeBtn.hide();
36104         if(this.stickBtn){
36105             this.stickBtn.show();
36106         }
36107         this.el.show();
36108         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36109         this.beforeSlide();
36110         this.el.setStyle("z-index", 10001);
36111         this.el.slideIn(this.getSlideAnchor(), {
36112             callback: function(){
36113                 this.afterSlide();
36114                 this.initAutoHide();
36115                 Roo.get(document).on("click", this.slideInIf, this);
36116                 this.fireEvent("slideshow", this);
36117             },
36118             scope: this,
36119             block: true
36120         });
36121     },
36122
36123     afterSlideIn : function(){
36124         this.clearAutoHide();
36125         this.isSlid = false;
36126         this.clearMonitor();
36127         this.el.setStyle("z-index", "");
36128         if(this.collapseBtn){
36129             this.collapseBtn.show();
36130         }
36131         this.closeBtn.setStyle('display', this.closeBtnState);
36132         if(this.stickBtn){
36133             this.stickBtn.hide();
36134         }
36135         this.fireEvent("slidehide", this);
36136     },
36137
36138     slideIn : function(cb){
36139         if(!this.isSlid || this.el.hasActiveFx()){
36140             Roo.callback(cb);
36141             return;
36142         }
36143         this.isSlid = false;
36144         this.beforeSlide();
36145         this.el.slideOut(this.getSlideAnchor(), {
36146             callback: function(){
36147                 this.el.setLeftTop(-10000, -10000);
36148                 this.afterSlide();
36149                 this.afterSlideIn();
36150                 Roo.callback(cb);
36151             },
36152             scope: this,
36153             block: true
36154         });
36155     },
36156     
36157     slideInIf : function(e){
36158         if(!e.within(this.el)){
36159             this.slideIn();
36160         }
36161     },
36162
36163     animateCollapse : function(){
36164         this.beforeSlide();
36165         this.el.setStyle("z-index", 20000);
36166         var anchor = this.getSlideAnchor();
36167         this.el.slideOut(anchor, {
36168             callback : function(){
36169                 this.el.setStyle("z-index", "");
36170                 this.collapsedEl.slideIn(anchor, {duration:.3});
36171                 this.afterSlide();
36172                 this.el.setLocation(-10000,-10000);
36173                 this.el.hide();
36174                 this.fireEvent("collapsed", this);
36175             },
36176             scope: this,
36177             block: true
36178         });
36179     },
36180
36181     animateExpand : function(){
36182         this.beforeSlide();
36183         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36184         this.el.setStyle("z-index", 20000);
36185         this.collapsedEl.hide({
36186             duration:.1
36187         });
36188         this.el.slideIn(this.getSlideAnchor(), {
36189             callback : function(){
36190                 this.el.setStyle("z-index", "");
36191                 this.afterSlide();
36192                 if(this.split){
36193                     this.split.el.show();
36194                 }
36195                 this.fireEvent("invalidated", this);
36196                 this.fireEvent("expanded", this);
36197             },
36198             scope: this,
36199             block: true
36200         });
36201     },
36202
36203     anchors : {
36204         "west" : "left",
36205         "east" : "right",
36206         "north" : "top",
36207         "south" : "bottom"
36208     },
36209
36210     sanchors : {
36211         "west" : "l",
36212         "east" : "r",
36213         "north" : "t",
36214         "south" : "b"
36215     },
36216
36217     canchors : {
36218         "west" : "tl-tr",
36219         "east" : "tr-tl",
36220         "north" : "tl-bl",
36221         "south" : "bl-tl"
36222     },
36223
36224     getAnchor : function(){
36225         return this.anchors[this.position];
36226     },
36227
36228     getCollapseAnchor : function(){
36229         return this.canchors[this.position];
36230     },
36231
36232     getSlideAnchor : function(){
36233         return this.sanchors[this.position];
36234     },
36235
36236     getAlignAdj : function(){
36237         var cm = this.cmargins;
36238         switch(this.position){
36239             case "west":
36240                 return [0, 0];
36241             break;
36242             case "east":
36243                 return [0, 0];
36244             break;
36245             case "north":
36246                 return [0, 0];
36247             break;
36248             case "south":
36249                 return [0, 0];
36250             break;
36251         }
36252     },
36253
36254     getExpandAdj : function(){
36255         var c = this.collapsedEl, cm = this.cmargins;
36256         switch(this.position){
36257             case "west":
36258                 return [-(cm.right+c.getWidth()+cm.left), 0];
36259             break;
36260             case "east":
36261                 return [cm.right+c.getWidth()+cm.left, 0];
36262             break;
36263             case "north":
36264                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36265             break;
36266             case "south":
36267                 return [0, cm.top+cm.bottom+c.getHeight()];
36268             break;
36269         }
36270     }
36271 });/*
36272  * Based on:
36273  * Ext JS Library 1.1.1
36274  * Copyright(c) 2006-2007, Ext JS, LLC.
36275  *
36276  * Originally Released Under LGPL - original licence link has changed is not relivant.
36277  *
36278  * Fork - LGPL
36279  * <script type="text/javascript">
36280  */
36281 /*
36282  * These classes are private internal classes
36283  */
36284 Roo.bootstrap.layout.Center = function(config){
36285     config.region = "center";
36286     Roo.bootstrap.layout.Region.call(this, config);
36287     this.visible = true;
36288     this.minWidth = config.minWidth || 20;
36289     this.minHeight = config.minHeight || 20;
36290 };
36291
36292 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36293     hide : function(){
36294         // center panel can't be hidden
36295     },
36296     
36297     show : function(){
36298         // center panel can't be hidden
36299     },
36300     
36301     getMinWidth: function(){
36302         return this.minWidth;
36303     },
36304     
36305     getMinHeight: function(){
36306         return this.minHeight;
36307     }
36308 });
36309
36310
36311
36312
36313  
36314
36315
36316
36317
36318
36319 Roo.bootstrap.layout.North = function(config)
36320 {
36321     config.region = 'north';
36322     config.cursor = 'n-resize';
36323     
36324     Roo.bootstrap.layout.Split.call(this, config);
36325     
36326     
36327     if(this.split){
36328         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36329         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36330         this.split.el.addClass("roo-layout-split-v");
36331     }
36332     var size = config.initialSize || config.height;
36333     if(typeof size != "undefined"){
36334         this.el.setHeight(size);
36335     }
36336 };
36337 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36338 {
36339     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36340     
36341     
36342     
36343     getBox : function(){
36344         if(this.collapsed){
36345             return this.collapsedEl.getBox();
36346         }
36347         var box = this.el.getBox();
36348         if(this.split){
36349             box.height += this.split.el.getHeight();
36350         }
36351         return box;
36352     },
36353     
36354     updateBox : function(box){
36355         if(this.split && !this.collapsed){
36356             box.height -= this.split.el.getHeight();
36357             this.split.el.setLeft(box.x);
36358             this.split.el.setTop(box.y+box.height);
36359             this.split.el.setWidth(box.width);
36360         }
36361         if(this.collapsed){
36362             this.updateBody(box.width, null);
36363         }
36364         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36365     }
36366 });
36367
36368
36369
36370
36371
36372 Roo.bootstrap.layout.South = function(config){
36373     config.region = 'south';
36374     config.cursor = 's-resize';
36375     Roo.bootstrap.layout.Split.call(this, config);
36376     if(this.split){
36377         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36378         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36379         this.split.el.addClass("roo-layout-split-v");
36380     }
36381     var size = config.initialSize || config.height;
36382     if(typeof size != "undefined"){
36383         this.el.setHeight(size);
36384     }
36385 };
36386
36387 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36388     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36389     getBox : function(){
36390         if(this.collapsed){
36391             return this.collapsedEl.getBox();
36392         }
36393         var box = this.el.getBox();
36394         if(this.split){
36395             var sh = this.split.el.getHeight();
36396             box.height += sh;
36397             box.y -= sh;
36398         }
36399         return box;
36400     },
36401     
36402     updateBox : function(box){
36403         if(this.split && !this.collapsed){
36404             var sh = this.split.el.getHeight();
36405             box.height -= sh;
36406             box.y += sh;
36407             this.split.el.setLeft(box.x);
36408             this.split.el.setTop(box.y-sh);
36409             this.split.el.setWidth(box.width);
36410         }
36411         if(this.collapsed){
36412             this.updateBody(box.width, null);
36413         }
36414         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36415     }
36416 });
36417
36418 Roo.bootstrap.layout.East = function(config){
36419     config.region = "east";
36420     config.cursor = "e-resize";
36421     Roo.bootstrap.layout.Split.call(this, config);
36422     if(this.split){
36423         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36424         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36425         this.split.el.addClass("roo-layout-split-h");
36426     }
36427     var size = config.initialSize || config.width;
36428     if(typeof size != "undefined"){
36429         this.el.setWidth(size);
36430     }
36431 };
36432 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36433     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36434     getBox : function(){
36435         if(this.collapsed){
36436             return this.collapsedEl.getBox();
36437         }
36438         var box = this.el.getBox();
36439         if(this.split){
36440             var sw = this.split.el.getWidth();
36441             box.width += sw;
36442             box.x -= sw;
36443         }
36444         return box;
36445     },
36446
36447     updateBox : function(box){
36448         if(this.split && !this.collapsed){
36449             var sw = this.split.el.getWidth();
36450             box.width -= sw;
36451             this.split.el.setLeft(box.x);
36452             this.split.el.setTop(box.y);
36453             this.split.el.setHeight(box.height);
36454             box.x += sw;
36455         }
36456         if(this.collapsed){
36457             this.updateBody(null, box.height);
36458         }
36459         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36460     }
36461 });
36462
36463 Roo.bootstrap.layout.West = function(config){
36464     config.region = "west";
36465     config.cursor = "w-resize";
36466     
36467     Roo.bootstrap.layout.Split.call(this, config);
36468     if(this.split){
36469         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36470         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36471         this.split.el.addClass("roo-layout-split-h");
36472     }
36473     
36474 };
36475 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36476     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36477     
36478     onRender: function(ctr, pos)
36479     {
36480         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36481         var size = this.config.initialSize || this.config.width;
36482         if(typeof size != "undefined"){
36483             this.el.setWidth(size);
36484         }
36485     },
36486     
36487     getBox : function(){
36488         if(this.collapsed){
36489             return this.collapsedEl.getBox();
36490         }
36491         var box = this.el.getBox();
36492         if(this.split){
36493             box.width += this.split.el.getWidth();
36494         }
36495         return box;
36496     },
36497     
36498     updateBox : function(box){
36499         if(this.split && !this.collapsed){
36500             var sw = this.split.el.getWidth();
36501             box.width -= sw;
36502             this.split.el.setLeft(box.x+box.width);
36503             this.split.el.setTop(box.y);
36504             this.split.el.setHeight(box.height);
36505         }
36506         if(this.collapsed){
36507             this.updateBody(null, box.height);
36508         }
36509         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36510     }
36511 });
36512 Roo.namespace("Roo.bootstrap.panel");/*
36513  * Based on:
36514  * Ext JS Library 1.1.1
36515  * Copyright(c) 2006-2007, Ext JS, LLC.
36516  *
36517  * Originally Released Under LGPL - original licence link has changed is not relivant.
36518  *
36519  * Fork - LGPL
36520  * <script type="text/javascript">
36521  */
36522 /**
36523  * @class Roo.ContentPanel
36524  * @extends Roo.util.Observable
36525  * A basic ContentPanel element.
36526  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36527  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36528  * @cfg {Boolean/Object} autoCreate True to auto generate the DOM element for this panel, or a {@link Roo.DomHelper} config of the element to create
36529  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36530  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36531  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36532  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36533  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36534  * @cfg {String} title          The title for this panel
36535  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36536  * @cfg {String} url            Calls {@link #setUrl} with this value
36537  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36538  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36539  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36540  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36541  * @cfg {Boolean} badges render the badges
36542
36543  * @constructor
36544  * Create a new ContentPanel.
36545  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36546  * @param {String/Object} config A string to set only the title or a config object
36547  * @param {String} content (optional) Set the HTML content for this panel
36548  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36549  */
36550 Roo.bootstrap.panel.Content = function( config){
36551     
36552     this.tpl = config.tpl || false;
36553     
36554     var el = config.el;
36555     var content = config.content;
36556
36557     if(config.autoCreate){ // xtype is available if this is called from factory
36558         el = Roo.id();
36559     }
36560     this.el = Roo.get(el);
36561     if(!this.el && config && config.autoCreate){
36562         if(typeof config.autoCreate == "object"){
36563             if(!config.autoCreate.id){
36564                 config.autoCreate.id = config.id||el;
36565             }
36566             this.el = Roo.DomHelper.append(document.body,
36567                         config.autoCreate, true);
36568         }else{
36569             var elcfg =  {   tag: "div",
36570                             cls: "roo-layout-inactive-content",
36571                             id: config.id||el
36572                             };
36573             if (config.html) {
36574                 elcfg.html = config.html;
36575                 
36576             }
36577                         
36578             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36579         }
36580     } 
36581     this.closable = false;
36582     this.loaded = false;
36583     this.active = false;
36584    
36585       
36586     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36587         
36588         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36589         
36590         this.wrapEl = this.el; //this.el.wrap();
36591         var ti = [];
36592         if (config.toolbar.items) {
36593             ti = config.toolbar.items ;
36594             delete config.toolbar.items ;
36595         }
36596         
36597         var nitems = [];
36598         this.toolbar.render(this.wrapEl, 'before');
36599         for(var i =0;i < ti.length;i++) {
36600           //  Roo.log(['add child', items[i]]);
36601             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36602         }
36603         this.toolbar.items = nitems;
36604         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36605         delete config.toolbar;
36606         
36607     }
36608     /*
36609     // xtype created footer. - not sure if will work as we normally have to render first..
36610     if (this.footer && !this.footer.el && this.footer.xtype) {
36611         if (!this.wrapEl) {
36612             this.wrapEl = this.el.wrap();
36613         }
36614     
36615         this.footer.container = this.wrapEl.createChild();
36616          
36617         this.footer = Roo.factory(this.footer, Roo);
36618         
36619     }
36620     */
36621     
36622      if(typeof config == "string"){
36623         this.title = config;
36624     }else{
36625         Roo.apply(this, config);
36626     }
36627     
36628     if(this.resizeEl){
36629         this.resizeEl = Roo.get(this.resizeEl, true);
36630     }else{
36631         this.resizeEl = this.el;
36632     }
36633     // handle view.xtype
36634     
36635  
36636     
36637     
36638     this.addEvents({
36639         /**
36640          * @event activate
36641          * Fires when this panel is activated. 
36642          * @param {Roo.ContentPanel} this
36643          */
36644         "activate" : true,
36645         /**
36646          * @event deactivate
36647          * Fires when this panel is activated. 
36648          * @param {Roo.ContentPanel} this
36649          */
36650         "deactivate" : true,
36651
36652         /**
36653          * @event resize
36654          * Fires when this panel is resized if fitToFrame is true.
36655          * @param {Roo.ContentPanel} this
36656          * @param {Number} width The width after any component adjustments
36657          * @param {Number} height The height after any component adjustments
36658          */
36659         "resize" : true,
36660         
36661          /**
36662          * @event render
36663          * Fires when this tab is created
36664          * @param {Roo.ContentPanel} this
36665          */
36666         "render" : true
36667         
36668         
36669         
36670     });
36671     
36672
36673     
36674     
36675     if(this.autoScroll){
36676         this.resizeEl.setStyle("overflow", "auto");
36677     } else {
36678         // fix randome scrolling
36679         //this.el.on('scroll', function() {
36680         //    Roo.log('fix random scolling');
36681         //    this.scrollTo('top',0); 
36682         //});
36683     }
36684     content = content || this.content;
36685     if(content){
36686         this.setContent(content);
36687     }
36688     if(config && config.url){
36689         this.setUrl(this.url, this.params, this.loadOnce);
36690     }
36691     
36692     
36693     
36694     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36695     
36696     if (this.view && typeof(this.view.xtype) != 'undefined') {
36697         this.view.el = this.el.appendChild(document.createElement("div"));
36698         this.view = Roo.factory(this.view); 
36699         this.view.render  &&  this.view.render(false, '');  
36700     }
36701     
36702     
36703     this.fireEvent('render', this);
36704 };
36705
36706 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36707     
36708     tabTip : '',
36709     
36710     setRegion : function(region){
36711         this.region = region;
36712         this.setActiveClass(region && !this.background);
36713     },
36714     
36715     
36716     setActiveClass: function(state)
36717     {
36718         if(state){
36719            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36720            this.el.setStyle('position','relative');
36721         }else{
36722            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36723            this.el.setStyle('position', 'absolute');
36724         } 
36725     },
36726     
36727     /**
36728      * Returns the toolbar for this Panel if one was configured. 
36729      * @return {Roo.Toolbar} 
36730      */
36731     getToolbar : function(){
36732         return this.toolbar;
36733     },
36734     
36735     setActiveState : function(active)
36736     {
36737         this.active = active;
36738         this.setActiveClass(active);
36739         if(!active){
36740             if(this.fireEvent("deactivate", this) === false){
36741                 return false;
36742             }
36743             return true;
36744         }
36745         this.fireEvent("activate", this);
36746         return true;
36747     },
36748     /**
36749      * Updates this panel's element
36750      * @param {String} content The new content
36751      * @param {Boolean} loadScripts (optional) true to look for and process scripts
36752     */
36753     setContent : function(content, loadScripts){
36754         this.el.update(content, loadScripts);
36755     },
36756
36757     ignoreResize : function(w, h){
36758         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36759             return true;
36760         }else{
36761             this.lastSize = {width: w, height: h};
36762             return false;
36763         }
36764     },
36765     /**
36766      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36767      * @return {Roo.UpdateManager} The UpdateManager
36768      */
36769     getUpdateManager : function(){
36770         return this.el.getUpdateManager();
36771     },
36772      /**
36773      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36774      * @param {Object/String/Function} url The url for this request or a function to call to get the url or a config object containing any of the following options:
36775 <pre><code>
36776 panel.load({
36777     url: "your-url.php",
36778     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36779     callback: yourFunction,
36780     scope: yourObject, //(optional scope)
36781     discardUrl: false,
36782     nocache: false,
36783     text: "Loading...",
36784     timeout: 30,
36785     scripts: false
36786 });
36787 </code></pre>
36788      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
36789      * are shorthand for <i>disableCaching</i>, <i>indicatorText</i> and <i>loadScripts</i> and are used to set their associated property on this panel UpdateManager instance.
36790      * @param {String/Object} params (optional) The parameters to pass as either a URL encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
36791      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
36792      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used URL. If true, it will not store the URL.
36793      * @return {Roo.ContentPanel} this
36794      */
36795     load : function(){
36796         var um = this.el.getUpdateManager();
36797         um.update.apply(um, arguments);
36798         return this;
36799     },
36800
36801
36802     /**
36803      * Set a URL to be used to load the content for this panel. When this panel is activated, the content will be loaded from that URL.
36804      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
36805      * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
36806      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this panel is activated. (Defaults to false)
36807      * @return {Roo.UpdateManager} The UpdateManager
36808      */
36809     setUrl : function(url, params, loadOnce){
36810         if(this.refreshDelegate){
36811             this.removeListener("activate", this.refreshDelegate);
36812         }
36813         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36814         this.on("activate", this.refreshDelegate);
36815         return this.el.getUpdateManager();
36816     },
36817     
36818     _handleRefresh : function(url, params, loadOnce){
36819         if(!loadOnce || !this.loaded){
36820             var updater = this.el.getUpdateManager();
36821             updater.update(url, params, this._setLoaded.createDelegate(this));
36822         }
36823     },
36824     
36825     _setLoaded : function(){
36826         this.loaded = true;
36827     }, 
36828     
36829     /**
36830      * Returns this panel's id
36831      * @return {String} 
36832      */
36833     getId : function(){
36834         return this.el.id;
36835     },
36836     
36837     /** 
36838      * Returns this panel's element - used by regiosn to add.
36839      * @return {Roo.Element} 
36840      */
36841     getEl : function(){
36842         return this.wrapEl || this.el;
36843     },
36844     
36845    
36846     
36847     adjustForComponents : function(width, height)
36848     {
36849         //Roo.log('adjustForComponents ');
36850         if(this.resizeEl != this.el){
36851             width -= this.el.getFrameWidth('lr');
36852             height -= this.el.getFrameWidth('tb');
36853         }
36854         if(this.toolbar){
36855             var te = this.toolbar.getEl();
36856             te.setWidth(width);
36857             height -= te.getHeight();
36858         }
36859         if(this.footer){
36860             var te = this.footer.getEl();
36861             te.setWidth(width);
36862             height -= te.getHeight();
36863         }
36864         
36865         
36866         if(this.adjustments){
36867             width += this.adjustments[0];
36868             height += this.adjustments[1];
36869         }
36870         return {"width": width, "height": height};
36871     },
36872     
36873     setSize : function(width, height){
36874         if(this.fitToFrame && !this.ignoreResize(width, height)){
36875             if(this.fitContainer && this.resizeEl != this.el){
36876                 this.el.setSize(width, height);
36877             }
36878             var size = this.adjustForComponents(width, height);
36879             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
36880             this.fireEvent('resize', this, size.width, size.height);
36881         }
36882     },
36883     
36884     /**
36885      * Returns this panel's title
36886      * @return {String} 
36887      */
36888     getTitle : function(){
36889         
36890         if (typeof(this.title) != 'object') {
36891             return this.title;
36892         }
36893         
36894         var t = '';
36895         for (var k in this.title) {
36896             if (!this.title.hasOwnProperty(k)) {
36897                 continue;
36898             }
36899             
36900             if (k.indexOf('-') >= 0) {
36901                 var s = k.split('-');
36902                 for (var i = 0; i<s.length; i++) {
36903                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
36904                 }
36905             } else {
36906                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
36907             }
36908         }
36909         return t;
36910     },
36911     
36912     /**
36913      * Set this panel's title
36914      * @param {String} title
36915      */
36916     setTitle : function(title){
36917         this.title = title;
36918         if(this.region){
36919             this.region.updatePanelTitle(this, title);
36920         }
36921     },
36922     
36923     /**
36924      * Returns true is this panel was configured to be closable
36925      * @return {Boolean} 
36926      */
36927     isClosable : function(){
36928         return this.closable;
36929     },
36930     
36931     beforeSlide : function(){
36932         this.el.clip();
36933         this.resizeEl.clip();
36934     },
36935     
36936     afterSlide : function(){
36937         this.el.unclip();
36938         this.resizeEl.unclip();
36939     },
36940     
36941     /**
36942      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
36943      *   Will fail silently if the {@link #setUrl} method has not been called.
36944      *   This does not activate the panel, just updates its content.
36945      */
36946     refresh : function(){
36947         if(this.refreshDelegate){
36948            this.loaded = false;
36949            this.refreshDelegate();
36950         }
36951     },
36952     
36953     /**
36954      * Destroys this panel
36955      */
36956     destroy : function(){
36957         this.el.removeAllListeners();
36958         var tempEl = document.createElement("span");
36959         tempEl.appendChild(this.el.dom);
36960         tempEl.innerHTML = "";
36961         this.el.remove();
36962         this.el = null;
36963     },
36964     
36965     /**
36966      * form - if the content panel contains a form - this is a reference to it.
36967      * @type {Roo.form.Form}
36968      */
36969     form : false,
36970     /**
36971      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
36972      *    This contains a reference to it.
36973      * @type {Roo.View}
36974      */
36975     view : false,
36976     
36977       /**
36978      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
36979      * <pre><code>
36980
36981 layout.addxtype({
36982        xtype : 'Form',
36983        items: [ .... ]
36984    }
36985 );
36986
36987 </code></pre>
36988      * @param {Object} cfg Xtype definition of item to add.
36989      */
36990     
36991     
36992     getChildContainer: function () {
36993         return this.getEl();
36994     }
36995     
36996     
36997     /*
36998         var  ret = new Roo.factory(cfg);
36999         return ret;
37000         
37001         
37002         // add form..
37003         if (cfg.xtype.match(/^Form$/)) {
37004             
37005             var el;
37006             //if (this.footer) {
37007             //    el = this.footer.container.insertSibling(false, 'before');
37008             //} else {
37009                 el = this.el.createChild();
37010             //}
37011
37012             this.form = new  Roo.form.Form(cfg);
37013             
37014             
37015             if ( this.form.allItems.length) {
37016                 this.form.render(el.dom);
37017             }
37018             return this.form;
37019         }
37020         // should only have one of theses..
37021         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
37022             // views.. should not be just added - used named prop 'view''
37023             
37024             cfg.el = this.el.appendChild(document.createElement("div"));
37025             // factory?
37026             
37027             var ret = new Roo.factory(cfg);
37028              
37029              ret.render && ret.render(false, ''); // render blank..
37030             this.view = ret;
37031             return ret;
37032         }
37033         return false;
37034     }
37035     \*/
37036 });
37037  
37038 /**
37039  * @class Roo.bootstrap.panel.Grid
37040  * @extends Roo.bootstrap.panel.Content
37041  * @constructor
37042  * Create a new GridPanel.
37043  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
37044  * @param {Object} config A the config object
37045   
37046  */
37047
37048
37049
37050 Roo.bootstrap.panel.Grid = function(config)
37051 {
37052     
37053       
37054     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
37055         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
37056
37057     config.el = this.wrapper;
37058     //this.el = this.wrapper;
37059     
37060       if (config.container) {
37061         // ctor'ed from a Border/panel.grid
37062         
37063         
37064         this.wrapper.setStyle("overflow", "hidden");
37065         this.wrapper.addClass('roo-grid-container');
37066
37067     }
37068     
37069     
37070     if(config.toolbar){
37071         var tool_el = this.wrapper.createChild();    
37072         this.toolbar = Roo.factory(config.toolbar);
37073         var ti = [];
37074         if (config.toolbar.items) {
37075             ti = config.toolbar.items ;
37076             delete config.toolbar.items ;
37077         }
37078         
37079         var nitems = [];
37080         this.toolbar.render(tool_el);
37081         for(var i =0;i < ti.length;i++) {
37082           //  Roo.log(['add child', items[i]]);
37083             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
37084         }
37085         this.toolbar.items = nitems;
37086         
37087         delete config.toolbar;
37088     }
37089     
37090     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37091     config.grid.scrollBody = true;;
37092     config.grid.monitorWindowResize = false; // turn off autosizing
37093     config.grid.autoHeight = false;
37094     config.grid.autoWidth = false;
37095     
37096     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37097     
37098     if (config.background) {
37099         // render grid on panel activation (if panel background)
37100         this.on('activate', function(gp) {
37101             if (!gp.grid.rendered) {
37102                 gp.grid.render(this.wrapper);
37103                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37104             }
37105         });
37106             
37107     } else {
37108         this.grid.render(this.wrapper);
37109         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37110
37111     }
37112     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37113     // ??? needed ??? config.el = this.wrapper;
37114     
37115     
37116     
37117   
37118     // xtype created footer. - not sure if will work as we normally have to render first..
37119     if (this.footer && !this.footer.el && this.footer.xtype) {
37120         
37121         var ctr = this.grid.getView().getFooterPanel(true);
37122         this.footer.dataSource = this.grid.dataSource;
37123         this.footer = Roo.factory(this.footer, Roo);
37124         this.footer.render(ctr);
37125         
37126     }
37127     
37128     
37129     
37130     
37131      
37132 };
37133
37134 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37135     getId : function(){
37136         return this.grid.id;
37137     },
37138     
37139     /**
37140      * Returns the grid for this panel
37141      * @return {Roo.bootstrap.Table} 
37142      */
37143     getGrid : function(){
37144         return this.grid;    
37145     },
37146     
37147     setSize : function(width, height){
37148         if(!this.ignoreResize(width, height)){
37149             var grid = this.grid;
37150             var size = this.adjustForComponents(width, height);
37151             var gridel = grid.getGridEl();
37152             gridel.setSize(size.width, size.height);
37153             /*
37154             var thd = grid.getGridEl().select('thead',true).first();
37155             var tbd = grid.getGridEl().select('tbody', true).first();
37156             if (tbd) {
37157                 tbd.setSize(width, height - thd.getHeight());
37158             }
37159             */
37160             grid.autoSize();
37161         }
37162     },
37163      
37164     
37165     
37166     beforeSlide : function(){
37167         this.grid.getView().scroller.clip();
37168     },
37169     
37170     afterSlide : function(){
37171         this.grid.getView().scroller.unclip();
37172     },
37173     
37174     destroy : function(){
37175         this.grid.destroy();
37176         delete this.grid;
37177         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37178     }
37179 });
37180
37181 /**
37182  * @class Roo.bootstrap.panel.Nest
37183  * @extends Roo.bootstrap.panel.Content
37184  * @constructor
37185  * Create a new Panel, that can contain a layout.Border.
37186  * 
37187  * 
37188  * @param {Roo.BorderLayout} layout The layout for this panel
37189  * @param {String/Object} config A string to set only the title or a config object
37190  */
37191 Roo.bootstrap.panel.Nest = function(config)
37192 {
37193     // construct with only one argument..
37194     /* FIXME - implement nicer consturctors
37195     if (layout.layout) {
37196         config = layout;
37197         layout = config.layout;
37198         delete config.layout;
37199     }
37200     if (layout.xtype && !layout.getEl) {
37201         // then layout needs constructing..
37202         layout = Roo.factory(layout, Roo);
37203     }
37204     */
37205     
37206     config.el =  config.layout.getEl();
37207     
37208     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37209     
37210     config.layout.monitorWindowResize = false; // turn off autosizing
37211     this.layout = config.layout;
37212     this.layout.getEl().addClass("roo-layout-nested-layout");
37213     
37214     
37215     
37216     
37217 };
37218
37219 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37220
37221     setSize : function(width, height){
37222         if(!this.ignoreResize(width, height)){
37223             var size = this.adjustForComponents(width, height);
37224             var el = this.layout.getEl();
37225             if (size.height < 1) {
37226                 el.setWidth(size.width);   
37227             } else {
37228                 el.setSize(size.width, size.height);
37229             }
37230             var touch = el.dom.offsetWidth;
37231             this.layout.layout();
37232             // ie requires a double layout on the first pass
37233             if(Roo.isIE && !this.initialized){
37234                 this.initialized = true;
37235                 this.layout.layout();
37236             }
37237         }
37238     },
37239     
37240     // activate all subpanels if not currently active..
37241     
37242     setActiveState : function(active){
37243         this.active = active;
37244         this.setActiveClass(active);
37245         
37246         if(!active){
37247             this.fireEvent("deactivate", this);
37248             return;
37249         }
37250         
37251         this.fireEvent("activate", this);
37252         // not sure if this should happen before or after..
37253         if (!this.layout) {
37254             return; // should not happen..
37255         }
37256         var reg = false;
37257         for (var r in this.layout.regions) {
37258             reg = this.layout.getRegion(r);
37259             if (reg.getActivePanel()) {
37260                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37261                 reg.setActivePanel(reg.getActivePanel());
37262                 continue;
37263             }
37264             if (!reg.panels.length) {
37265                 continue;
37266             }
37267             reg.showPanel(reg.getPanel(0));
37268         }
37269         
37270         
37271         
37272         
37273     },
37274     
37275     /**
37276      * Returns the nested BorderLayout for this panel
37277      * @return {Roo.BorderLayout} 
37278      */
37279     getLayout : function(){
37280         return this.layout;
37281     },
37282     
37283      /**
37284      * Adds a xtype elements to the layout of the nested panel
37285      * <pre><code>
37286
37287 panel.addxtype({
37288        xtype : 'ContentPanel',
37289        region: 'west',
37290        items: [ .... ]
37291    }
37292 );
37293
37294 panel.addxtype({
37295         xtype : 'NestedLayoutPanel',
37296         region: 'west',
37297         layout: {
37298            center: { },
37299            west: { }   
37300         },
37301         items : [ ... list of content panels or nested layout panels.. ]
37302    }
37303 );
37304 </code></pre>
37305      * @param {Object} cfg Xtype definition of item to add.
37306      */
37307     addxtype : function(cfg) {
37308         return this.layout.addxtype(cfg);
37309     
37310     }
37311 });        /*
37312  * Based on:
37313  * Ext JS Library 1.1.1
37314  * Copyright(c) 2006-2007, Ext JS, LLC.
37315  *
37316  * Originally Released Under LGPL - original licence link has changed is not relivant.
37317  *
37318  * Fork - LGPL
37319  * <script type="text/javascript">
37320  */
37321 /**
37322  * @class Roo.TabPanel
37323  * @extends Roo.util.Observable
37324  * A lightweight tab container.
37325  * <br><br>
37326  * Usage:
37327  * <pre><code>
37328 // basic tabs 1, built from existing content
37329 var tabs = new Roo.TabPanel("tabs1");
37330 tabs.addTab("script", "View Script");
37331 tabs.addTab("markup", "View Markup");
37332 tabs.activate("script");
37333
37334 // more advanced tabs, built from javascript
37335 var jtabs = new Roo.TabPanel("jtabs");
37336 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37337
37338 // set up the UpdateManager
37339 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37340 var updater = tab2.getUpdateManager();
37341 updater.setDefaultUrl("ajax1.htm");
37342 tab2.on('activate', updater.refresh, updater, true);
37343
37344 // Use setUrl for Ajax loading
37345 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37346 tab3.setUrl("ajax2.htm", null, true);
37347
37348 // Disabled tab
37349 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37350 tab4.disable();
37351
37352 jtabs.activate("jtabs-1");
37353  * </code></pre>
37354  * @constructor
37355  * Create a new TabPanel.
37356  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37357  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37358  */
37359 Roo.bootstrap.panel.Tabs = function(config){
37360     /**
37361     * The container element for this TabPanel.
37362     * @type Roo.Element
37363     */
37364     this.el = Roo.get(config.el);
37365     delete config.el;
37366     if(config){
37367         if(typeof config == "boolean"){
37368             this.tabPosition = config ? "bottom" : "top";
37369         }else{
37370             Roo.apply(this, config);
37371         }
37372     }
37373     
37374     if(this.tabPosition == "bottom"){
37375         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37376         this.el.addClass("roo-tabs-bottom");
37377     }
37378     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37379     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37380     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37381     if(Roo.isIE){
37382         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37383     }
37384     if(this.tabPosition != "bottom"){
37385         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37386          * @type Roo.Element
37387          */
37388         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37389         this.el.addClass("roo-tabs-top");
37390     }
37391     this.items = [];
37392
37393     this.bodyEl.setStyle("position", "relative");
37394
37395     this.active = null;
37396     this.activateDelegate = this.activate.createDelegate(this);
37397
37398     this.addEvents({
37399         /**
37400          * @event tabchange
37401          * Fires when the active tab changes
37402          * @param {Roo.TabPanel} this
37403          * @param {Roo.TabPanelItem} activePanel The new active tab
37404          */
37405         "tabchange": true,
37406         /**
37407          * @event beforetabchange
37408          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37409          * @param {Roo.TabPanel} this
37410          * @param {Object} e Set cancel to true on this object to cancel the tab change
37411          * @param {Roo.TabPanelItem} tab The tab being changed to
37412          */
37413         "beforetabchange" : true
37414     });
37415
37416     Roo.EventManager.onWindowResize(this.onResize, this);
37417     this.cpad = this.el.getPadding("lr");
37418     this.hiddenCount = 0;
37419
37420
37421     // toolbar on the tabbar support...
37422     if (this.toolbar) {
37423         alert("no toolbar support yet");
37424         this.toolbar  = false;
37425         /*
37426         var tcfg = this.toolbar;
37427         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37428         this.toolbar = new Roo.Toolbar(tcfg);
37429         if (Roo.isSafari) {
37430             var tbl = tcfg.container.child('table', true);
37431             tbl.setAttribute('width', '100%');
37432         }
37433         */
37434         
37435     }
37436    
37437
37438
37439     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37440 };
37441
37442 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37443     /*
37444      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37445      */
37446     tabPosition : "top",
37447     /*
37448      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37449      */
37450     currentTabWidth : 0,
37451     /*
37452      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37453      */
37454     minTabWidth : 40,
37455     /*
37456      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37457      */
37458     maxTabWidth : 250,
37459     /*
37460      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37461      */
37462     preferredTabWidth : 175,
37463     /*
37464      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37465      */
37466     resizeTabs : false,
37467     /*
37468      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37469      */
37470     monitorResize : true,
37471     /*
37472      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37473      */
37474     toolbar : false,
37475
37476     /**
37477      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37478      * @param {String} id The id of the div to use <b>or create</b>
37479      * @param {String} text The text for the tab
37480      * @param {String} content (optional) Content to put in the TabPanelItem body
37481      * @param {Boolean} closable (optional) True to create a close icon on the tab
37482      * @return {Roo.TabPanelItem} The created TabPanelItem
37483      */
37484     addTab : function(id, text, content, closable, tpl)
37485     {
37486         var item = new Roo.bootstrap.panel.TabItem({
37487             panel: this,
37488             id : id,
37489             text : text,
37490             closable : closable,
37491             tpl : tpl
37492         });
37493         this.addTabItem(item);
37494         if(content){
37495             item.setContent(content);
37496         }
37497         return item;
37498     },
37499
37500     /**
37501      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37502      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37503      * @return {Roo.TabPanelItem}
37504      */
37505     getTab : function(id){
37506         return this.items[id];
37507     },
37508
37509     /**
37510      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37511      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37512      */
37513     hideTab : function(id){
37514         var t = this.items[id];
37515         if(!t.isHidden()){
37516            t.setHidden(true);
37517            this.hiddenCount++;
37518            this.autoSizeTabs();
37519         }
37520     },
37521
37522     /**
37523      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37524      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37525      */
37526     unhideTab : function(id){
37527         var t = this.items[id];
37528         if(t.isHidden()){
37529            t.setHidden(false);
37530            this.hiddenCount--;
37531            this.autoSizeTabs();
37532         }
37533     },
37534
37535     /**
37536      * Adds an existing {@link Roo.TabPanelItem}.
37537      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37538      */
37539     addTabItem : function(item){
37540         this.items[item.id] = item;
37541         this.items.push(item);
37542       //  if(this.resizeTabs){
37543     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37544   //         this.autoSizeTabs();
37545 //        }else{
37546 //            item.autoSize();
37547        // }
37548     },
37549
37550     /**
37551      * Removes a {@link Roo.TabPanelItem}.
37552      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37553      */
37554     removeTab : function(id){
37555         var items = this.items;
37556         var tab = items[id];
37557         if(!tab) { return; }
37558         var index = items.indexOf(tab);
37559         if(this.active == tab && items.length > 1){
37560             var newTab = this.getNextAvailable(index);
37561             if(newTab) {
37562                 newTab.activate();
37563             }
37564         }
37565         this.stripEl.dom.removeChild(tab.pnode.dom);
37566         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37567             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37568         }
37569         items.splice(index, 1);
37570         delete this.items[tab.id];
37571         tab.fireEvent("close", tab);
37572         tab.purgeListeners();
37573         this.autoSizeTabs();
37574     },
37575
37576     getNextAvailable : function(start){
37577         var items = this.items;
37578         var index = start;
37579         // look for a next tab that will slide over to
37580         // replace the one being removed
37581         while(index < items.length){
37582             var item = items[++index];
37583             if(item && !item.isHidden()){
37584                 return item;
37585             }
37586         }
37587         // if one isn't found select the previous tab (on the left)
37588         index = start;
37589         while(index >= 0){
37590             var item = items[--index];
37591             if(item && !item.isHidden()){
37592                 return item;
37593             }
37594         }
37595         return null;
37596     },
37597
37598     /**
37599      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37600      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37601      */
37602     disableTab : function(id){
37603         var tab = this.items[id];
37604         if(tab && this.active != tab){
37605             tab.disable();
37606         }
37607     },
37608
37609     /**
37610      * Enables a {@link Roo.TabPanelItem} that is disabled.
37611      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37612      */
37613     enableTab : function(id){
37614         var tab = this.items[id];
37615         tab.enable();
37616     },
37617
37618     /**
37619      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37620      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37621      * @return {Roo.TabPanelItem} The TabPanelItem.
37622      */
37623     activate : function(id){
37624         var tab = this.items[id];
37625         if(!tab){
37626             return null;
37627         }
37628         if(tab == this.active || tab.disabled){
37629             return tab;
37630         }
37631         var e = {};
37632         this.fireEvent("beforetabchange", this, e, tab);
37633         if(e.cancel !== true && !tab.disabled){
37634             if(this.active){
37635                 this.active.hide();
37636             }
37637             this.active = this.items[id];
37638             this.active.show();
37639             this.fireEvent("tabchange", this, this.active);
37640         }
37641         return tab;
37642     },
37643
37644     /**
37645      * Gets the active {@link Roo.TabPanelItem}.
37646      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37647      */
37648     getActiveTab : function(){
37649         return this.active;
37650     },
37651
37652     /**
37653      * Updates the tab body element to fit the height of the container element
37654      * for overflow scrolling
37655      * @param {Number} targetHeight (optional) Override the starting height from the elements height
37656      */
37657     syncHeight : function(targetHeight){
37658         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37659         var bm = this.bodyEl.getMargins();
37660         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37661         this.bodyEl.setHeight(newHeight);
37662         return newHeight;
37663     },
37664
37665     onResize : function(){
37666         if(this.monitorResize){
37667             this.autoSizeTabs();
37668         }
37669     },
37670
37671     /**
37672      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37673      */
37674     beginUpdate : function(){
37675         this.updating = true;
37676     },
37677
37678     /**
37679      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37680      */
37681     endUpdate : function(){
37682         this.updating = false;
37683         this.autoSizeTabs();
37684     },
37685
37686     /**
37687      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37688      */
37689     autoSizeTabs : function(){
37690         var count = this.items.length;
37691         var vcount = count - this.hiddenCount;
37692         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37693             return;
37694         }
37695         var w = Math.max(this.el.getWidth() - this.cpad, 10);
37696         var availWidth = Math.floor(w / vcount);
37697         var b = this.stripBody;
37698         if(b.getWidth() > w){
37699             var tabs = this.items;
37700             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37701             if(availWidth < this.minTabWidth){
37702                 /*if(!this.sleft){    // incomplete scrolling code
37703                     this.createScrollButtons();
37704                 }
37705                 this.showScroll();
37706                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37707             }
37708         }else{
37709             if(this.currentTabWidth < this.preferredTabWidth){
37710                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37711             }
37712         }
37713     },
37714
37715     /**
37716      * Returns the number of tabs in this TabPanel.
37717      * @return {Number}
37718      */
37719      getCount : function(){
37720          return this.items.length;
37721      },
37722
37723     /**
37724      * Resizes all the tabs to the passed width
37725      * @param {Number} The new width
37726      */
37727     setTabWidth : function(width){
37728         this.currentTabWidth = width;
37729         for(var i = 0, len = this.items.length; i < len; i++) {
37730                 if(!this.items[i].isHidden()) {
37731                 this.items[i].setWidth(width);
37732             }
37733         }
37734     },
37735
37736     /**
37737      * Destroys this TabPanel
37738      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37739      */
37740     destroy : function(removeEl){
37741         Roo.EventManager.removeResizeListener(this.onResize, this);
37742         for(var i = 0, len = this.items.length; i < len; i++){
37743             this.items[i].purgeListeners();
37744         }
37745         if(removeEl === true){
37746             this.el.update("");
37747             this.el.remove();
37748         }
37749     },
37750     
37751     createStrip : function(container)
37752     {
37753         var strip = document.createElement("nav");
37754         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37755         container.appendChild(strip);
37756         return strip;
37757     },
37758     
37759     createStripList : function(strip)
37760     {
37761         // div wrapper for retard IE
37762         // returns the "tr" element.
37763         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
37764         //'<div class="x-tabs-strip-wrap">'+
37765           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
37766           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
37767         return strip.firstChild; //.firstChild.firstChild.firstChild;
37768     },
37769     createBody : function(container)
37770     {
37771         var body = document.createElement("div");
37772         Roo.id(body, "tab-body");
37773         //Roo.fly(body).addClass("x-tabs-body");
37774         Roo.fly(body).addClass("tab-content");
37775         container.appendChild(body);
37776         return body;
37777     },
37778     createItemBody :function(bodyEl, id){
37779         var body = Roo.getDom(id);
37780         if(!body){
37781             body = document.createElement("div");
37782             body.id = id;
37783         }
37784         //Roo.fly(body).addClass("x-tabs-item-body");
37785         Roo.fly(body).addClass("tab-pane");
37786          bodyEl.insertBefore(body, bodyEl.firstChild);
37787         return body;
37788     },
37789     /** @private */
37790     createStripElements :  function(stripEl, text, closable, tpl)
37791     {
37792         var td = document.createElement("li"); // was td..
37793         
37794         
37795         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
37796         
37797         
37798         stripEl.appendChild(td);
37799         /*if(closable){
37800             td.className = "x-tabs-closable";
37801             if(!this.closeTpl){
37802                 this.closeTpl = new Roo.Template(
37803                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37804                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
37805                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
37806                 );
37807             }
37808             var el = this.closeTpl.overwrite(td, {"text": text});
37809             var close = el.getElementsByTagName("div")[0];
37810             var inner = el.getElementsByTagName("em")[0];
37811             return {"el": el, "close": close, "inner": inner};
37812         } else {
37813         */
37814         // not sure what this is..
37815 //            if(!this.tabTpl){
37816                 //this.tabTpl = new Roo.Template(
37817                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37818                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
37819                 //);
37820 //                this.tabTpl = new Roo.Template(
37821 //                   '<a href="#">' +
37822 //                   '<span unselectable="on"' +
37823 //                            (this.disableTooltips ? '' : ' title="{text}"') +
37824 //                            ' >{text}</span></a>'
37825 //                );
37826 //                
37827 //            }
37828
37829
37830             var template = tpl || this.tabTpl || false;
37831             
37832             if(!template){
37833                 
37834                 template = new Roo.Template(
37835                    '<a href="#">' +
37836                    '<span unselectable="on"' +
37837                             (this.disableTooltips ? '' : ' title="{text}"') +
37838                             ' >{text}</span></a>'
37839                 );
37840             }
37841             
37842             switch (typeof(template)) {
37843                 case 'object' :
37844                     break;
37845                 case 'string' :
37846                     template = new Roo.Template(template);
37847                     break;
37848                 default :
37849                     break;
37850             }
37851             
37852             var el = template.overwrite(td, {"text": text});
37853             
37854             var inner = el.getElementsByTagName("span")[0];
37855             
37856             return {"el": el, "inner": inner};
37857             
37858     }
37859         
37860     
37861 });
37862
37863 /**
37864  * @class Roo.TabPanelItem
37865  * @extends Roo.util.Observable
37866  * Represents an individual item (tab plus body) in a TabPanel.
37867  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
37868  * @param {String} id The id of this TabPanelItem
37869  * @param {String} text The text for the tab of this TabPanelItem
37870  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
37871  */
37872 Roo.bootstrap.panel.TabItem = function(config){
37873     /**
37874      * The {@link Roo.TabPanel} this TabPanelItem belongs to
37875      * @type Roo.TabPanel
37876      */
37877     this.tabPanel = config.panel;
37878     /**
37879      * The id for this TabPanelItem
37880      * @type String
37881      */
37882     this.id = config.id;
37883     /** @private */
37884     this.disabled = false;
37885     /** @private */
37886     this.text = config.text;
37887     /** @private */
37888     this.loaded = false;
37889     this.closable = config.closable;
37890
37891     /**
37892      * The body element for this TabPanelItem.
37893      * @type Roo.Element
37894      */
37895     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
37896     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
37897     this.bodyEl.setStyle("display", "block");
37898     this.bodyEl.setStyle("zoom", "1");
37899     //this.hideAction();
37900
37901     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
37902     /** @private */
37903     this.el = Roo.get(els.el);
37904     this.inner = Roo.get(els.inner, true);
37905     this.textEl = Roo.get(this.el.dom.firstChild, true);
37906     this.pnode = Roo.get(els.el.parentNode, true);
37907 //    this.el.on("mousedown", this.onTabMouseDown, this);
37908     this.el.on("click", this.onTabClick, this);
37909     /** @private */
37910     if(config.closable){
37911         var c = Roo.get(els.close, true);
37912         c.dom.title = this.closeText;
37913         c.addClassOnOver("close-over");
37914         c.on("click", this.closeClick, this);
37915      }
37916
37917     this.addEvents({
37918          /**
37919          * @event activate
37920          * Fires when this tab becomes the active tab.
37921          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37922          * @param {Roo.TabPanelItem} this
37923          */
37924         "activate": true,
37925         /**
37926          * @event beforeclose
37927          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
37928          * @param {Roo.TabPanelItem} this
37929          * @param {Object} e Set cancel to true on this object to cancel the close.
37930          */
37931         "beforeclose": true,
37932         /**
37933          * @event close
37934          * Fires when this tab is closed.
37935          * @param {Roo.TabPanelItem} this
37936          */
37937          "close": true,
37938         /**
37939          * @event deactivate
37940          * Fires when this tab is no longer the active tab.
37941          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37942          * @param {Roo.TabPanelItem} this
37943          */
37944          "deactivate" : true
37945     });
37946     this.hidden = false;
37947
37948     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
37949 };
37950
37951 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
37952            {
37953     purgeListeners : function(){
37954        Roo.util.Observable.prototype.purgeListeners.call(this);
37955        this.el.removeAllListeners();
37956     },
37957     /**
37958      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
37959      */
37960     show : function(){
37961         this.pnode.addClass("active");
37962         this.showAction();
37963         if(Roo.isOpera){
37964             this.tabPanel.stripWrap.repaint();
37965         }
37966         this.fireEvent("activate", this.tabPanel, this);
37967     },
37968
37969     /**
37970      * Returns true if this tab is the active tab.
37971      * @return {Boolean}
37972      */
37973     isActive : function(){
37974         return this.tabPanel.getActiveTab() == this;
37975     },
37976
37977     /**
37978      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
37979      */
37980     hide : function(){
37981         this.pnode.removeClass("active");
37982         this.hideAction();
37983         this.fireEvent("deactivate", this.tabPanel, this);
37984     },
37985
37986     hideAction : function(){
37987         this.bodyEl.hide();
37988         this.bodyEl.setStyle("position", "absolute");
37989         this.bodyEl.setLeft("-20000px");
37990         this.bodyEl.setTop("-20000px");
37991     },
37992
37993     showAction : function(){
37994         this.bodyEl.setStyle("position", "relative");
37995         this.bodyEl.setTop("");
37996         this.bodyEl.setLeft("");
37997         this.bodyEl.show();
37998     },
37999
38000     /**
38001      * Set the tooltip for the tab.
38002      * @param {String} tooltip The tab's tooltip
38003      */
38004     setTooltip : function(text){
38005         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
38006             this.textEl.dom.qtip = text;
38007             this.textEl.dom.removeAttribute('title');
38008         }else{
38009             this.textEl.dom.title = text;
38010         }
38011     },
38012
38013     onTabClick : function(e){
38014         e.preventDefault();
38015         this.tabPanel.activate(this.id);
38016     },
38017
38018     onTabMouseDown : function(e){
38019         e.preventDefault();
38020         this.tabPanel.activate(this.id);
38021     },
38022 /*
38023     getWidth : function(){
38024         return this.inner.getWidth();
38025     },
38026
38027     setWidth : function(width){
38028         var iwidth = width - this.pnode.getPadding("lr");
38029         this.inner.setWidth(iwidth);
38030         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
38031         this.pnode.setWidth(width);
38032     },
38033 */
38034     /**
38035      * Show or hide the tab
38036      * @param {Boolean} hidden True to hide or false to show.
38037      */
38038     setHidden : function(hidden){
38039         this.hidden = hidden;
38040         this.pnode.setStyle("display", hidden ? "none" : "");
38041     },
38042
38043     /**
38044      * Returns true if this tab is "hidden"
38045      * @return {Boolean}
38046      */
38047     isHidden : function(){
38048         return this.hidden;
38049     },
38050
38051     /**
38052      * Returns the text for this tab
38053      * @return {String}
38054      */
38055     getText : function(){
38056         return this.text;
38057     },
38058     /*
38059     autoSize : function(){
38060         //this.el.beginMeasure();
38061         this.textEl.setWidth(1);
38062         /*
38063          *  #2804 [new] Tabs in Roojs
38064          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
38065          */
38066         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
38067         //this.el.endMeasure();
38068     //},
38069
38070     /**
38071      * Sets the text for the tab (Note: this also sets the tooltip text)
38072      * @param {String} text The tab's text and tooltip
38073      */
38074     setText : function(text){
38075         this.text = text;
38076         this.textEl.update(text);
38077         this.setTooltip(text);
38078         //if(!this.tabPanel.resizeTabs){
38079         //    this.autoSize();
38080         //}
38081     },
38082     /**
38083      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
38084      */
38085     activate : function(){
38086         this.tabPanel.activate(this.id);
38087     },
38088
38089     /**
38090      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38091      */
38092     disable : function(){
38093         if(this.tabPanel.active != this){
38094             this.disabled = true;
38095             this.pnode.addClass("disabled");
38096         }
38097     },
38098
38099     /**
38100      * Enables this TabPanelItem if it was previously disabled.
38101      */
38102     enable : function(){
38103         this.disabled = false;
38104         this.pnode.removeClass("disabled");
38105     },
38106
38107     /**
38108      * Sets the content for this TabPanelItem.
38109      * @param {String} content The content
38110      * @param {Boolean} loadScripts true to look for and load scripts
38111      */
38112     setContent : function(content, loadScripts){
38113         this.bodyEl.update(content, loadScripts);
38114     },
38115
38116     /**
38117      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38118      * @return {Roo.UpdateManager} The UpdateManager
38119      */
38120     getUpdateManager : function(){
38121         return this.bodyEl.getUpdateManager();
38122     },
38123
38124     /**
38125      * Set a URL to be used to load the content for this TabPanelItem.
38126      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38127      * @param {String/Object} params (optional) The string params for the update call or an object of the params. See {@link Roo.UpdateManager#update} for more details. (Defaults to null)
38128      * @param {Boolean} loadOnce (optional) Whether to only load the content once. If this is false it makes the Ajax call every time this TabPanelItem is activated. (Defaults to false)
38129      * @return {Roo.UpdateManager} The UpdateManager
38130      */
38131     setUrl : function(url, params, loadOnce){
38132         if(this.refreshDelegate){
38133             this.un('activate', this.refreshDelegate);
38134         }
38135         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38136         this.on("activate", this.refreshDelegate);
38137         return this.bodyEl.getUpdateManager();
38138     },
38139
38140     /** @private */
38141     _handleRefresh : function(url, params, loadOnce){
38142         if(!loadOnce || !this.loaded){
38143             var updater = this.bodyEl.getUpdateManager();
38144             updater.update(url, params, this._setLoaded.createDelegate(this));
38145         }
38146     },
38147
38148     /**
38149      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38150      *   Will fail silently if the setUrl method has not been called.
38151      *   This does not activate the panel, just updates its content.
38152      */
38153     refresh : function(){
38154         if(this.refreshDelegate){
38155            this.loaded = false;
38156            this.refreshDelegate();
38157         }
38158     },
38159
38160     /** @private */
38161     _setLoaded : function(){
38162         this.loaded = true;
38163     },
38164
38165     /** @private */
38166     closeClick : function(e){
38167         var o = {};
38168         e.stopEvent();
38169         this.fireEvent("beforeclose", this, o);
38170         if(o.cancel !== true){
38171             this.tabPanel.removeTab(this.id);
38172         }
38173     },
38174     /**
38175      * The text displayed in the tooltip for the close icon.
38176      * @type String
38177      */
38178     closeText : "Close this tab"
38179 });
38180 /**
38181 *    This script refer to:
38182 *    Title: International Telephone Input
38183 *    Author: Jack O'Connor
38184 *    Code version:  v12.1.12
38185 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38186 **/
38187
38188 Roo.bootstrap.PhoneInputData = function() {
38189     var d = [
38190       [
38191         "Afghanistan (‫افغانستان‬‎)",
38192         "af",
38193         "93"
38194       ],
38195       [
38196         "Albania (Shqipëri)",
38197         "al",
38198         "355"
38199       ],
38200       [
38201         "Algeria (‫الجزائر‬‎)",
38202         "dz",
38203         "213"
38204       ],
38205       [
38206         "American Samoa",
38207         "as",
38208         "1684"
38209       ],
38210       [
38211         "Andorra",
38212         "ad",
38213         "376"
38214       ],
38215       [
38216         "Angola",
38217         "ao",
38218         "244"
38219       ],
38220       [
38221         "Anguilla",
38222         "ai",
38223         "1264"
38224       ],
38225       [
38226         "Antigua and Barbuda",
38227         "ag",
38228         "1268"
38229       ],
38230       [
38231         "Argentina",
38232         "ar",
38233         "54"
38234       ],
38235       [
38236         "Armenia (Հայաստան)",
38237         "am",
38238         "374"
38239       ],
38240       [
38241         "Aruba",
38242         "aw",
38243         "297"
38244       ],
38245       [
38246         "Australia",
38247         "au",
38248         "61",
38249         0
38250       ],
38251       [
38252         "Austria (Österreich)",
38253         "at",
38254         "43"
38255       ],
38256       [
38257         "Azerbaijan (Azərbaycan)",
38258         "az",
38259         "994"
38260       ],
38261       [
38262         "Bahamas",
38263         "bs",
38264         "1242"
38265       ],
38266       [
38267         "Bahrain (‫البحرين‬‎)",
38268         "bh",
38269         "973"
38270       ],
38271       [
38272         "Bangladesh (বাংলাদেশ)",
38273         "bd",
38274         "880"
38275       ],
38276       [
38277         "Barbados",
38278         "bb",
38279         "1246"
38280       ],
38281       [
38282         "Belarus (Беларусь)",
38283         "by",
38284         "375"
38285       ],
38286       [
38287         "Belgium (België)",
38288         "be",
38289         "32"
38290       ],
38291       [
38292         "Belize",
38293         "bz",
38294         "501"
38295       ],
38296       [
38297         "Benin (Bénin)",
38298         "bj",
38299         "229"
38300       ],
38301       [
38302         "Bermuda",
38303         "bm",
38304         "1441"
38305       ],
38306       [
38307         "Bhutan (འབྲུག)",
38308         "bt",
38309         "975"
38310       ],
38311       [
38312         "Bolivia",
38313         "bo",
38314         "591"
38315       ],
38316       [
38317         "Bosnia and Herzegovina (Босна и Херцеговина)",
38318         "ba",
38319         "387"
38320       ],
38321       [
38322         "Botswana",
38323         "bw",
38324         "267"
38325       ],
38326       [
38327         "Brazil (Brasil)",
38328         "br",
38329         "55"
38330       ],
38331       [
38332         "British Indian Ocean Territory",
38333         "io",
38334         "246"
38335       ],
38336       [
38337         "British Virgin Islands",
38338         "vg",
38339         "1284"
38340       ],
38341       [
38342         "Brunei",
38343         "bn",
38344         "673"
38345       ],
38346       [
38347         "Bulgaria (България)",
38348         "bg",
38349         "359"
38350       ],
38351       [
38352         "Burkina Faso",
38353         "bf",
38354         "226"
38355       ],
38356       [
38357         "Burundi (Uburundi)",
38358         "bi",
38359         "257"
38360       ],
38361       [
38362         "Cambodia (កម្ពុជា)",
38363         "kh",
38364         "855"
38365       ],
38366       [
38367         "Cameroon (Cameroun)",
38368         "cm",
38369         "237"
38370       ],
38371       [
38372         "Canada",
38373         "ca",
38374         "1",
38375         1,
38376         ["204", "226", "236", "249", "250", "289", "306", "343", "365", "387", "403", "416", "418", "431", "437", "438", "450", "506", "514", "519", "548", "579", "581", "587", "604", "613", "639", "647", "672", "705", "709", "742", "778", "780", "782", "807", "819", "825", "867", "873", "902", "905"]
38377       ],
38378       [
38379         "Cape Verde (Kabu Verdi)",
38380         "cv",
38381         "238"
38382       ],
38383       [
38384         "Caribbean Netherlands",
38385         "bq",
38386         "599",
38387         1
38388       ],
38389       [
38390         "Cayman Islands",
38391         "ky",
38392         "1345"
38393       ],
38394       [
38395         "Central African Republic (République centrafricaine)",
38396         "cf",
38397         "236"
38398       ],
38399       [
38400         "Chad (Tchad)",
38401         "td",
38402         "235"
38403       ],
38404       [
38405         "Chile",
38406         "cl",
38407         "56"
38408       ],
38409       [
38410         "China (中国)",
38411         "cn",
38412         "86"
38413       ],
38414       [
38415         "Christmas Island",
38416         "cx",
38417         "61",
38418         2
38419       ],
38420       [
38421         "Cocos (Keeling) Islands",
38422         "cc",
38423         "61",
38424         1
38425       ],
38426       [
38427         "Colombia",
38428         "co",
38429         "57"
38430       ],
38431       [
38432         "Comoros (‫جزر القمر‬‎)",
38433         "km",
38434         "269"
38435       ],
38436       [
38437         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38438         "cd",
38439         "243"
38440       ],
38441       [
38442         "Congo (Republic) (Congo-Brazzaville)",
38443         "cg",
38444         "242"
38445       ],
38446       [
38447         "Cook Islands",
38448         "ck",
38449         "682"
38450       ],
38451       [
38452         "Costa Rica",
38453         "cr",
38454         "506"
38455       ],
38456       [
38457         "Côte d’Ivoire",
38458         "ci",
38459         "225"
38460       ],
38461       [
38462         "Croatia (Hrvatska)",
38463         "hr",
38464         "385"
38465       ],
38466       [
38467         "Cuba",
38468         "cu",
38469         "53"
38470       ],
38471       [
38472         "Curaçao",
38473         "cw",
38474         "599",
38475         0
38476       ],
38477       [
38478         "Cyprus (Κύπρος)",
38479         "cy",
38480         "357"
38481       ],
38482       [
38483         "Czech Republic (Česká republika)",
38484         "cz",
38485         "420"
38486       ],
38487       [
38488         "Denmark (Danmark)",
38489         "dk",
38490         "45"
38491       ],
38492       [
38493         "Djibouti",
38494         "dj",
38495         "253"
38496       ],
38497       [
38498         "Dominica",
38499         "dm",
38500         "1767"
38501       ],
38502       [
38503         "Dominican Republic (República Dominicana)",
38504         "do",
38505         "1",
38506         2,
38507         ["809", "829", "849"]
38508       ],
38509       [
38510         "Ecuador",
38511         "ec",
38512         "593"
38513       ],
38514       [
38515         "Egypt (‫مصر‬‎)",
38516         "eg",
38517         "20"
38518       ],
38519       [
38520         "El Salvador",
38521         "sv",
38522         "503"
38523       ],
38524       [
38525         "Equatorial Guinea (Guinea Ecuatorial)",
38526         "gq",
38527         "240"
38528       ],
38529       [
38530         "Eritrea",
38531         "er",
38532         "291"
38533       ],
38534       [
38535         "Estonia (Eesti)",
38536         "ee",
38537         "372"
38538       ],
38539       [
38540         "Ethiopia",
38541         "et",
38542         "251"
38543       ],
38544       [
38545         "Falkland Islands (Islas Malvinas)",
38546         "fk",
38547         "500"
38548       ],
38549       [
38550         "Faroe Islands (Føroyar)",
38551         "fo",
38552         "298"
38553       ],
38554       [
38555         "Fiji",
38556         "fj",
38557         "679"
38558       ],
38559       [
38560         "Finland (Suomi)",
38561         "fi",
38562         "358",
38563         0
38564       ],
38565       [
38566         "France",
38567         "fr",
38568         "33"
38569       ],
38570       [
38571         "French Guiana (Guyane française)",
38572         "gf",
38573         "594"
38574       ],
38575       [
38576         "French Polynesia (Polynésie française)",
38577         "pf",
38578         "689"
38579       ],
38580       [
38581         "Gabon",
38582         "ga",
38583         "241"
38584       ],
38585       [
38586         "Gambia",
38587         "gm",
38588         "220"
38589       ],
38590       [
38591         "Georgia (საქართველო)",
38592         "ge",
38593         "995"
38594       ],
38595       [
38596         "Germany (Deutschland)",
38597         "de",
38598         "49"
38599       ],
38600       [
38601         "Ghana (Gaana)",
38602         "gh",
38603         "233"
38604       ],
38605       [
38606         "Gibraltar",
38607         "gi",
38608         "350"
38609       ],
38610       [
38611         "Greece (Ελλάδα)",
38612         "gr",
38613         "30"
38614       ],
38615       [
38616         "Greenland (Kalaallit Nunaat)",
38617         "gl",
38618         "299"
38619       ],
38620       [
38621         "Grenada",
38622         "gd",
38623         "1473"
38624       ],
38625       [
38626         "Guadeloupe",
38627         "gp",
38628         "590",
38629         0
38630       ],
38631       [
38632         "Guam",
38633         "gu",
38634         "1671"
38635       ],
38636       [
38637         "Guatemala",
38638         "gt",
38639         "502"
38640       ],
38641       [
38642         "Guernsey",
38643         "gg",
38644         "44",
38645         1
38646       ],
38647       [
38648         "Guinea (Guinée)",
38649         "gn",
38650         "224"
38651       ],
38652       [
38653         "Guinea-Bissau (Guiné Bissau)",
38654         "gw",
38655         "245"
38656       ],
38657       [
38658         "Guyana",
38659         "gy",
38660         "592"
38661       ],
38662       [
38663         "Haiti",
38664         "ht",
38665         "509"
38666       ],
38667       [
38668         "Honduras",
38669         "hn",
38670         "504"
38671       ],
38672       [
38673         "Hong Kong (香港)",
38674         "hk",
38675         "852"
38676       ],
38677       [
38678         "Hungary (Magyarország)",
38679         "hu",
38680         "36"
38681       ],
38682       [
38683         "Iceland (Ísland)",
38684         "is",
38685         "354"
38686       ],
38687       [
38688         "India (भारत)",
38689         "in",
38690         "91"
38691       ],
38692       [
38693         "Indonesia",
38694         "id",
38695         "62"
38696       ],
38697       [
38698         "Iran (‫ایران‬‎)",
38699         "ir",
38700         "98"
38701       ],
38702       [
38703         "Iraq (‫العراق‬‎)",
38704         "iq",
38705         "964"
38706       ],
38707       [
38708         "Ireland",
38709         "ie",
38710         "353"
38711       ],
38712       [
38713         "Isle of Man",
38714         "im",
38715         "44",
38716         2
38717       ],
38718       [
38719         "Israel (‫ישראל‬‎)",
38720         "il",
38721         "972"
38722       ],
38723       [
38724         "Italy (Italia)",
38725         "it",
38726         "39",
38727         0
38728       ],
38729       [
38730         "Jamaica",
38731         "jm",
38732         "1876"
38733       ],
38734       [
38735         "Japan (日本)",
38736         "jp",
38737         "81"
38738       ],
38739       [
38740         "Jersey",
38741         "je",
38742         "44",
38743         3
38744       ],
38745       [
38746         "Jordan (‫الأردن‬‎)",
38747         "jo",
38748         "962"
38749       ],
38750       [
38751         "Kazakhstan (Казахстан)",
38752         "kz",
38753         "7",
38754         1
38755       ],
38756       [
38757         "Kenya",
38758         "ke",
38759         "254"
38760       ],
38761       [
38762         "Kiribati",
38763         "ki",
38764         "686"
38765       ],
38766       [
38767         "Kosovo",
38768         "xk",
38769         "383"
38770       ],
38771       [
38772         "Kuwait (‫الكويت‬‎)",
38773         "kw",
38774         "965"
38775       ],
38776       [
38777         "Kyrgyzstan (Кыргызстан)",
38778         "kg",
38779         "996"
38780       ],
38781       [
38782         "Laos (ລາວ)",
38783         "la",
38784         "856"
38785       ],
38786       [
38787         "Latvia (Latvija)",
38788         "lv",
38789         "371"
38790       ],
38791       [
38792         "Lebanon (‫لبنان‬‎)",
38793         "lb",
38794         "961"
38795       ],
38796       [
38797         "Lesotho",
38798         "ls",
38799         "266"
38800       ],
38801       [
38802         "Liberia",
38803         "lr",
38804         "231"
38805       ],
38806       [
38807         "Libya (‫ليبيا‬‎)",
38808         "ly",
38809         "218"
38810       ],
38811       [
38812         "Liechtenstein",
38813         "li",
38814         "423"
38815       ],
38816       [
38817         "Lithuania (Lietuva)",
38818         "lt",
38819         "370"
38820       ],
38821       [
38822         "Luxembourg",
38823         "lu",
38824         "352"
38825       ],
38826       [
38827         "Macau (澳門)",
38828         "mo",
38829         "853"
38830       ],
38831       [
38832         "Macedonia (FYROM) (Македонија)",
38833         "mk",
38834         "389"
38835       ],
38836       [
38837         "Madagascar (Madagasikara)",
38838         "mg",
38839         "261"
38840       ],
38841       [
38842         "Malawi",
38843         "mw",
38844         "265"
38845       ],
38846       [
38847         "Malaysia",
38848         "my",
38849         "60"
38850       ],
38851       [
38852         "Maldives",
38853         "mv",
38854         "960"
38855       ],
38856       [
38857         "Mali",
38858         "ml",
38859         "223"
38860       ],
38861       [
38862         "Malta",
38863         "mt",
38864         "356"
38865       ],
38866       [
38867         "Marshall Islands",
38868         "mh",
38869         "692"
38870       ],
38871       [
38872         "Martinique",
38873         "mq",
38874         "596"
38875       ],
38876       [
38877         "Mauritania (‫موريتانيا‬‎)",
38878         "mr",
38879         "222"
38880       ],
38881       [
38882         "Mauritius (Moris)",
38883         "mu",
38884         "230"
38885       ],
38886       [
38887         "Mayotte",
38888         "yt",
38889         "262",
38890         1
38891       ],
38892       [
38893         "Mexico (México)",
38894         "mx",
38895         "52"
38896       ],
38897       [
38898         "Micronesia",
38899         "fm",
38900         "691"
38901       ],
38902       [
38903         "Moldova (Republica Moldova)",
38904         "md",
38905         "373"
38906       ],
38907       [
38908         "Monaco",
38909         "mc",
38910         "377"
38911       ],
38912       [
38913         "Mongolia (Монгол)",
38914         "mn",
38915         "976"
38916       ],
38917       [
38918         "Montenegro (Crna Gora)",
38919         "me",
38920         "382"
38921       ],
38922       [
38923         "Montserrat",
38924         "ms",
38925         "1664"
38926       ],
38927       [
38928         "Morocco (‫المغرب‬‎)",
38929         "ma",
38930         "212",
38931         0
38932       ],
38933       [
38934         "Mozambique (Moçambique)",
38935         "mz",
38936         "258"
38937       ],
38938       [
38939         "Myanmar (Burma) (မြန်မာ)",
38940         "mm",
38941         "95"
38942       ],
38943       [
38944         "Namibia (Namibië)",
38945         "na",
38946         "264"
38947       ],
38948       [
38949         "Nauru",
38950         "nr",
38951         "674"
38952       ],
38953       [
38954         "Nepal (नेपाल)",
38955         "np",
38956         "977"
38957       ],
38958       [
38959         "Netherlands (Nederland)",
38960         "nl",
38961         "31"
38962       ],
38963       [
38964         "New Caledonia (Nouvelle-Calédonie)",
38965         "nc",
38966         "687"
38967       ],
38968       [
38969         "New Zealand",
38970         "nz",
38971         "64"
38972       ],
38973       [
38974         "Nicaragua",
38975         "ni",
38976         "505"
38977       ],
38978       [
38979         "Niger (Nijar)",
38980         "ne",
38981         "227"
38982       ],
38983       [
38984         "Nigeria",
38985         "ng",
38986         "234"
38987       ],
38988       [
38989         "Niue",
38990         "nu",
38991         "683"
38992       ],
38993       [
38994         "Norfolk Island",
38995         "nf",
38996         "672"
38997       ],
38998       [
38999         "North Korea (조선 민주주의 인민 공화국)",
39000         "kp",
39001         "850"
39002       ],
39003       [
39004         "Northern Mariana Islands",
39005         "mp",
39006         "1670"
39007       ],
39008       [
39009         "Norway (Norge)",
39010         "no",
39011         "47",
39012         0
39013       ],
39014       [
39015         "Oman (‫عُمان‬‎)",
39016         "om",
39017         "968"
39018       ],
39019       [
39020         "Pakistan (‫پاکستان‬‎)",
39021         "pk",
39022         "92"
39023       ],
39024       [
39025         "Palau",
39026         "pw",
39027         "680"
39028       ],
39029       [
39030         "Palestine (‫فلسطين‬‎)",
39031         "ps",
39032         "970"
39033       ],
39034       [
39035         "Panama (Panamá)",
39036         "pa",
39037         "507"
39038       ],
39039       [
39040         "Papua New Guinea",
39041         "pg",
39042         "675"
39043       ],
39044       [
39045         "Paraguay",
39046         "py",
39047         "595"
39048       ],
39049       [
39050         "Peru (Perú)",
39051         "pe",
39052         "51"
39053       ],
39054       [
39055         "Philippines",
39056         "ph",
39057         "63"
39058       ],
39059       [
39060         "Poland (Polska)",
39061         "pl",
39062         "48"
39063       ],
39064       [
39065         "Portugal",
39066         "pt",
39067         "351"
39068       ],
39069       [
39070         "Puerto Rico",
39071         "pr",
39072         "1",
39073         3,
39074         ["787", "939"]
39075       ],
39076       [
39077         "Qatar (‫قطر‬‎)",
39078         "qa",
39079         "974"
39080       ],
39081       [
39082         "Réunion (La Réunion)",
39083         "re",
39084         "262",
39085         0
39086       ],
39087       [
39088         "Romania (România)",
39089         "ro",
39090         "40"
39091       ],
39092       [
39093         "Russia (Россия)",
39094         "ru",
39095         "7",
39096         0
39097       ],
39098       [
39099         "Rwanda",
39100         "rw",
39101         "250"
39102       ],
39103       [
39104         "Saint Barthélemy",
39105         "bl",
39106         "590",
39107         1
39108       ],
39109       [
39110         "Saint Helena",
39111         "sh",
39112         "290"
39113       ],
39114       [
39115         "Saint Kitts and Nevis",
39116         "kn",
39117         "1869"
39118       ],
39119       [
39120         "Saint Lucia",
39121         "lc",
39122         "1758"
39123       ],
39124       [
39125         "Saint Martin (Saint-Martin (partie française))",
39126         "mf",
39127         "590",
39128         2
39129       ],
39130       [
39131         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39132         "pm",
39133         "508"
39134       ],
39135       [
39136         "Saint Vincent and the Grenadines",
39137         "vc",
39138         "1784"
39139       ],
39140       [
39141         "Samoa",
39142         "ws",
39143         "685"
39144       ],
39145       [
39146         "San Marino",
39147         "sm",
39148         "378"
39149       ],
39150       [
39151         "São Tomé and Príncipe (São Tomé e Príncipe)",
39152         "st",
39153         "239"
39154       ],
39155       [
39156         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39157         "sa",
39158         "966"
39159       ],
39160       [
39161         "Senegal (Sénégal)",
39162         "sn",
39163         "221"
39164       ],
39165       [
39166         "Serbia (Србија)",
39167         "rs",
39168         "381"
39169       ],
39170       [
39171         "Seychelles",
39172         "sc",
39173         "248"
39174       ],
39175       [
39176         "Sierra Leone",
39177         "sl",
39178         "232"
39179       ],
39180       [
39181         "Singapore",
39182         "sg",
39183         "65"
39184       ],
39185       [
39186         "Sint Maarten",
39187         "sx",
39188         "1721"
39189       ],
39190       [
39191         "Slovakia (Slovensko)",
39192         "sk",
39193         "421"
39194       ],
39195       [
39196         "Slovenia (Slovenija)",
39197         "si",
39198         "386"
39199       ],
39200       [
39201         "Solomon Islands",
39202         "sb",
39203         "677"
39204       ],
39205       [
39206         "Somalia (Soomaaliya)",
39207         "so",
39208         "252"
39209       ],
39210       [
39211         "South Africa",
39212         "za",
39213         "27"
39214       ],
39215       [
39216         "South Korea (대한민국)",
39217         "kr",
39218         "82"
39219       ],
39220       [
39221         "South Sudan (‫جنوب السودان‬‎)",
39222         "ss",
39223         "211"
39224       ],
39225       [
39226         "Spain (España)",
39227         "es",
39228         "34"
39229       ],
39230       [
39231         "Sri Lanka (ශ්‍රී ලංකාව)",
39232         "lk",
39233         "94"
39234       ],
39235       [
39236         "Sudan (‫السودان‬‎)",
39237         "sd",
39238         "249"
39239       ],
39240       [
39241         "Suriname",
39242         "sr",
39243         "597"
39244       ],
39245       [
39246         "Svalbard and Jan Mayen",
39247         "sj",
39248         "47",
39249         1
39250       ],
39251       [
39252         "Swaziland",
39253         "sz",
39254         "268"
39255       ],
39256       [
39257         "Sweden (Sverige)",
39258         "se",
39259         "46"
39260       ],
39261       [
39262         "Switzerland (Schweiz)",
39263         "ch",
39264         "41"
39265       ],
39266       [
39267         "Syria (‫سوريا‬‎)",
39268         "sy",
39269         "963"
39270       ],
39271       [
39272         "Taiwan (台灣)",
39273         "tw",
39274         "886"
39275       ],
39276       [
39277         "Tajikistan",
39278         "tj",
39279         "992"
39280       ],
39281       [
39282         "Tanzania",
39283         "tz",
39284         "255"
39285       ],
39286       [
39287         "Thailand (ไทย)",
39288         "th",
39289         "66"
39290       ],
39291       [
39292         "Timor-Leste",
39293         "tl",
39294         "670"
39295       ],
39296       [
39297         "Togo",
39298         "tg",
39299         "228"
39300       ],
39301       [
39302         "Tokelau",
39303         "tk",
39304         "690"
39305       ],
39306       [
39307         "Tonga",
39308         "to",
39309         "676"
39310       ],
39311       [
39312         "Trinidad and Tobago",
39313         "tt",
39314         "1868"
39315       ],
39316       [
39317         "Tunisia (‫تونس‬‎)",
39318         "tn",
39319         "216"
39320       ],
39321       [
39322         "Turkey (Türkiye)",
39323         "tr",
39324         "90"
39325       ],
39326       [
39327         "Turkmenistan",
39328         "tm",
39329         "993"
39330       ],
39331       [
39332         "Turks and Caicos Islands",
39333         "tc",
39334         "1649"
39335       ],
39336       [
39337         "Tuvalu",
39338         "tv",
39339         "688"
39340       ],
39341       [
39342         "U.S. Virgin Islands",
39343         "vi",
39344         "1340"
39345       ],
39346       [
39347         "Uganda",
39348         "ug",
39349         "256"
39350       ],
39351       [
39352         "Ukraine (Україна)",
39353         "ua",
39354         "380"
39355       ],
39356       [
39357         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39358         "ae",
39359         "971"
39360       ],
39361       [
39362         "United Kingdom",
39363         "gb",
39364         "44",
39365         0
39366       ],
39367       [
39368         "United States",
39369         "us",
39370         "1",
39371         0
39372       ],
39373       [
39374         "Uruguay",
39375         "uy",
39376         "598"
39377       ],
39378       [
39379         "Uzbekistan (Oʻzbekiston)",
39380         "uz",
39381         "998"
39382       ],
39383       [
39384         "Vanuatu",
39385         "vu",
39386         "678"
39387       ],
39388       [
39389         "Vatican City (Città del Vaticano)",
39390         "va",
39391         "39",
39392         1
39393       ],
39394       [
39395         "Venezuela",
39396         "ve",
39397         "58"
39398       ],
39399       [
39400         "Vietnam (Việt Nam)",
39401         "vn",
39402         "84"
39403       ],
39404       [
39405         "Wallis and Futuna (Wallis-et-Futuna)",
39406         "wf",
39407         "681"
39408       ],
39409       [
39410         "Western Sahara (‫الصحراء الغربية‬‎)",
39411         "eh",
39412         "212",
39413         1
39414       ],
39415       [
39416         "Yemen (‫اليمن‬‎)",
39417         "ye",
39418         "967"
39419       ],
39420       [
39421         "Zambia",
39422         "zm",
39423         "260"
39424       ],
39425       [
39426         "Zimbabwe",
39427         "zw",
39428         "263"
39429       ],
39430       [
39431         "Åland Islands",
39432         "ax",
39433         "358",
39434         1
39435       ]
39436   ];
39437   
39438   return d;
39439 }/**
39440 *    This script refer to:
39441 *    Title: International Telephone Input
39442 *    Author: Jack O'Connor
39443 *    Code version:  v12.1.12
39444 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39445 **/
39446
39447 /**
39448  * @class Roo.bootstrap.PhoneInput
39449  * @extends Roo.bootstrap.TriggerField
39450  * An input with International dial-code selection
39451  
39452  * @cfg {String} defaultDialCode default '+852'
39453  * @cfg {Array} preferedCountries default []
39454   
39455  * @constructor
39456  * Create a new PhoneInput.
39457  * @param {Object} config Configuration options
39458  */
39459
39460 Roo.bootstrap.PhoneInput = function(config) {
39461     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39462 };
39463
39464 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39465         
39466         listWidth: undefined,
39467         
39468         selectedClass: 'active',
39469         
39470         invalidClass : "has-warning",
39471         
39472         validClass: 'has-success',
39473         
39474         allowed: '0123456789',
39475         
39476         /**
39477          * @cfg {String} defaultDialCode The default dial code when initializing the input
39478          */
39479         defaultDialCode: '+852',
39480         
39481         /**
39482          * @cfg {Array} preferedCountries A list of iso2 in array (e.g. ['hk','us']). Those related countries will show at the top of the input's choices
39483          */
39484         preferedCountries: false,
39485         
39486         getAutoCreate : function()
39487         {
39488             var data = Roo.bootstrap.PhoneInputData();
39489             var align = this.labelAlign || this.parentLabelAlign();
39490             var id = Roo.id();
39491             
39492             this.allCountries = [];
39493             this.dialCodeMapping = [];
39494             
39495             for (var i = 0; i < data.length; i++) {
39496               var c = data[i];
39497               this.allCountries[i] = {
39498                 name: c[0],
39499                 iso2: c[1],
39500                 dialCode: c[2],
39501                 priority: c[3] || 0,
39502                 areaCodes: c[4] || null
39503               };
39504               this.dialCodeMapping[c[2]] = {
39505                   name: c[0],
39506                   iso2: c[1],
39507                   priority: c[3] || 0,
39508                   areaCodes: c[4] || null
39509               };
39510             }
39511             
39512             var cfg = {
39513                 cls: 'form-group',
39514                 cn: []
39515             };
39516             
39517             var input =  {
39518                 tag: 'input',
39519                 id : id,
39520                 cls : 'form-control tel-input',
39521                 autocomplete: 'new-password'
39522             };
39523             
39524             var hiddenInput = {
39525                 tag: 'input',
39526                 type: 'hidden',
39527                 cls: 'hidden-tel-input'
39528             };
39529             
39530             if (this.name) {
39531                 hiddenInput.name = this.name;
39532             }
39533             
39534             if (this.disabled) {
39535                 input.disabled = true;
39536             }
39537             
39538             var flag_container = {
39539                 tag: 'div',
39540                 cls: 'flag-box',
39541                 cn: [
39542                     {
39543                         tag: 'div',
39544                         cls: 'flag'
39545                     },
39546                     {
39547                         tag: 'div',
39548                         cls: 'caret'
39549                     }
39550                 ]
39551             };
39552             
39553             var box = {
39554                 tag: 'div',
39555                 cls: this.hasFeedback ? 'has-feedback' : '',
39556                 cn: [
39557                     hiddenInput,
39558                     input,
39559                     {
39560                         tag: 'input',
39561                         cls: 'dial-code-holder',
39562                         disabled: true
39563                     }
39564                 ]
39565             };
39566             
39567             var container = {
39568                 cls: 'roo-select2-container input-group',
39569                 cn: [
39570                     flag_container,
39571                     box
39572                 ]
39573             };
39574             
39575             if (this.fieldLabel.length) {
39576                 var indicator = {
39577                     tag: 'i',
39578                     tooltip: 'This field is required'
39579                 };
39580                 
39581                 var label = {
39582                     tag: 'label',
39583                     'for':  id,
39584                     cls: 'control-label',
39585                     cn: []
39586                 };
39587                 
39588                 var label_text = {
39589                     tag: 'span',
39590                     html: this.fieldLabel
39591                 };
39592                 
39593                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39594                 label.cn = [
39595                     indicator,
39596                     label_text
39597                 ];
39598                 
39599                 if(this.indicatorpos == 'right') {
39600                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39601                     label.cn = [
39602                         label_text,
39603                         indicator
39604                     ];
39605                 }
39606                 
39607                 if(align == 'left') {
39608                     container = {
39609                         tag: 'div',
39610                         cn: [
39611                             container
39612                         ]
39613                     };
39614                     
39615                     if(this.labelWidth > 12){
39616                         label.style = "width: " + this.labelWidth + 'px';
39617                     }
39618                     if(this.labelWidth < 13 && this.labelmd == 0){
39619                         this.labelmd = this.labelWidth;
39620                     }
39621                     if(this.labellg > 0){
39622                         label.cls += ' col-lg-' + this.labellg;
39623                         input.cls += ' col-lg-' + (12 - this.labellg);
39624                     }
39625                     if(this.labelmd > 0){
39626                         label.cls += ' col-md-' + this.labelmd;
39627                         container.cls += ' col-md-' + (12 - this.labelmd);
39628                     }
39629                     if(this.labelsm > 0){
39630                         label.cls += ' col-sm-' + this.labelsm;
39631                         container.cls += ' col-sm-' + (12 - this.labelsm);
39632                     }
39633                     if(this.labelxs > 0){
39634                         label.cls += ' col-xs-' + this.labelxs;
39635                         container.cls += ' col-xs-' + (12 - this.labelxs);
39636                     }
39637                 }
39638             }
39639             
39640             cfg.cn = [
39641                 label,
39642                 container
39643             ];
39644             
39645             var settings = this;
39646             
39647             ['xs','sm','md','lg'].map(function(size){
39648                 if (settings[size]) {
39649                     cfg.cls += ' col-' + size + '-' + settings[size];
39650                 }
39651             });
39652             
39653             this.store = new Roo.data.Store({
39654                 proxy : new Roo.data.MemoryProxy({}),
39655                 reader : new Roo.data.JsonReader({
39656                     fields : [
39657                         {
39658                             'name' : 'name',
39659                             'type' : 'string'
39660                         },
39661                         {
39662                             'name' : 'iso2',
39663                             'type' : 'string'
39664                         },
39665                         {
39666                             'name' : 'dialCode',
39667                             'type' : 'string'
39668                         },
39669                         {
39670                             'name' : 'priority',
39671                             'type' : 'string'
39672                         },
39673                         {
39674                             'name' : 'areaCodes',
39675                             'type' : 'string'
39676                         }
39677                     ]
39678                 })
39679             });
39680             
39681             if(!this.preferedCountries) {
39682                 this.preferedCountries = [
39683                     'hk',
39684                     'gb',
39685                     'us'
39686                 ];
39687             }
39688             
39689             var p = this.preferedCountries.reverse();
39690             
39691             if(p) {
39692                 for (var i = 0; i < p.length; i++) {
39693                     for (var j = 0; j < this.allCountries.length; j++) {
39694                         if(this.allCountries[j].iso2 == p[i]) {
39695                             var t = this.allCountries[j];
39696                             this.allCountries.splice(j,1);
39697                             this.allCountries.unshift(t);
39698                         }
39699                     } 
39700                 }
39701             }
39702             
39703             this.store.proxy.data = {
39704                 success: true,
39705                 data: this.allCountries
39706             };
39707             
39708             return cfg;
39709         },
39710         
39711         initEvents : function()
39712         {
39713             this.createList();
39714             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39715             
39716             this.indicator = this.indicatorEl();
39717             this.flag = this.flagEl();
39718             this.dialCodeHolder = this.dialCodeHolderEl();
39719             
39720             this.trigger = this.el.select('div.flag-box',true).first();
39721             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39722             
39723             var _this = this;
39724             
39725             (function(){
39726                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39727                 _this.list.setWidth(lw);
39728             }).defer(100);
39729             
39730             this.list.on('mouseover', this.onViewOver, this);
39731             this.list.on('mousemove', this.onViewMove, this);
39732             this.inputEl().on("keyup", this.onKeyUp, this);
39733             
39734             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
39735
39736             this.view = new Roo.View(this.list, this.tpl, {
39737                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
39738             });
39739             
39740             this.view.on('click', this.onViewClick, this);
39741             this.setValue(this.defaultDialCode);
39742         },
39743         
39744         onTriggerClick : function(e)
39745         {
39746             Roo.log('trigger click');
39747             if(this.disabled){
39748                 return;
39749             }
39750             
39751             if(this.isExpanded()){
39752                 this.collapse();
39753                 this.hasFocus = false;
39754             }else {
39755                 this.store.load({});
39756                 this.hasFocus = true;
39757                 this.expand();
39758             }
39759         },
39760         
39761         isExpanded : function()
39762         {
39763             return this.list.isVisible();
39764         },
39765         
39766         collapse : function()
39767         {
39768             if(!this.isExpanded()){
39769                 return;
39770             }
39771             this.list.hide();
39772             Roo.get(document).un('mousedown', this.collapseIf, this);
39773             Roo.get(document).un('mousewheel', this.collapseIf, this);
39774             this.fireEvent('collapse', this);
39775             this.validate();
39776         },
39777         
39778         expand : function()
39779         {
39780             Roo.log('expand');
39781
39782             if(this.isExpanded() || !this.hasFocus){
39783                 return;
39784             }
39785             
39786             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
39787             this.list.setWidth(lw);
39788             
39789             this.list.show();
39790             this.restrictHeight();
39791             
39792             Roo.get(document).on('mousedown', this.collapseIf, this);
39793             Roo.get(document).on('mousewheel', this.collapseIf, this);
39794             
39795             this.fireEvent('expand', this);
39796         },
39797         
39798         restrictHeight : function()
39799         {
39800             this.list.alignTo(this.inputEl(), this.listAlign);
39801             this.list.alignTo(this.inputEl(), this.listAlign);
39802         },
39803         
39804         onViewOver : function(e, t)
39805         {
39806             if(this.inKeyMode){
39807                 return;
39808             }
39809             var item = this.view.findItemFromChild(t);
39810             
39811             if(item){
39812                 var index = this.view.indexOf(item);
39813                 this.select(index, false);
39814             }
39815         },
39816
39817         // private
39818         onViewClick : function(view, doFocus, el, e)
39819         {
39820             var index = this.view.getSelectedIndexes()[0];
39821             
39822             var r = this.store.getAt(index);
39823             
39824             if(r){
39825                 this.onSelect(r, index);
39826             }
39827             if(doFocus !== false && !this.blockFocus){
39828                 this.inputEl().focus();
39829             }
39830         },
39831         
39832         onViewMove : function(e, t)
39833         {
39834             this.inKeyMode = false;
39835         },
39836         
39837         select : function(index, scrollIntoView)
39838         {
39839             this.selectedIndex = index;
39840             this.view.select(index);
39841             if(scrollIntoView !== false){
39842                 var el = this.view.getNode(index);
39843                 if(el){
39844                     this.list.scrollChildIntoView(el, false);
39845                 }
39846             }
39847         },
39848         
39849         createList : function()
39850         {
39851             this.list = Roo.get(document.body).createChild({
39852                 tag: 'ul',
39853                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
39854                 style: 'display:none'
39855             });
39856             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
39857         },
39858         
39859         collapseIf : function(e)
39860         {
39861             var in_combo  = e.within(this.el);
39862             var in_list =  e.within(this.list);
39863             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
39864             
39865             if (in_combo || in_list || is_list) {
39866                 return;
39867             }
39868             this.collapse();
39869         },
39870         
39871         onSelect : function(record, index)
39872         {
39873             if(this.fireEvent('beforeselect', this, record, index) !== false){
39874                 
39875                 this.setFlagClass(record.data.iso2);
39876                 this.setDialCode(record.data.dialCode);
39877                 this.hasFocus = false;
39878                 this.collapse();
39879                 this.fireEvent('select', this, record, index);
39880             }
39881         },
39882         
39883         flagEl : function()
39884         {
39885             var flag = this.el.select('div.flag',true).first();
39886             if(!flag){
39887                 return false;
39888             }
39889             return flag;
39890         },
39891         
39892         dialCodeHolderEl : function()
39893         {
39894             var d = this.el.select('input.dial-code-holder',true).first();
39895             if(!d){
39896                 return false;
39897             }
39898             return d;
39899         },
39900         
39901         setDialCode : function(v)
39902         {
39903             this.dialCodeHolder.dom.value = '+'+v;
39904         },
39905         
39906         setFlagClass : function(n)
39907         {
39908             this.flag.dom.className = 'flag '+n;
39909         },
39910         
39911         getValue : function()
39912         {
39913             var v = this.inputEl().getValue();
39914             if(this.dialCodeHolder) {
39915                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
39916             }
39917             return v;
39918         },
39919         
39920         setValue : function(v)
39921         {
39922             var d = this.getDialCode(v);
39923             
39924             //invalid dial code
39925             if(v.length == 0 || !d || d.length == 0) {
39926                 if(this.rendered){
39927                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
39928                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
39929                 }
39930                 return;
39931             }
39932             
39933             //valid dial code
39934             this.setFlagClass(this.dialCodeMapping[d].iso2);
39935             this.setDialCode(d);
39936             this.inputEl().dom.value = v.replace('+'+d,'');
39937             this.hiddenEl().dom.value = this.getValue();
39938             
39939             this.validate();
39940         },
39941         
39942         getDialCode : function(v = '')
39943         {
39944             if (v.length == 0) {
39945                 return this.dialCodeHolder.dom.value;
39946             }
39947             
39948             var dialCode = "";
39949             if (v.charAt(0) != "+") {
39950                 return false;
39951             }
39952             var numericChars = "";
39953             for (var i = 1; i < v.length; i++) {
39954               var c = v.charAt(i);
39955               if (!isNaN(c)) {
39956                 numericChars += c;
39957                 if (this.dialCodeMapping[numericChars]) {
39958                   dialCode = v.substr(1, i);
39959                 }
39960                 if (numericChars.length == 4) {
39961                   break;
39962                 }
39963               }
39964             }
39965             return dialCode;
39966         },
39967         
39968         reset : function()
39969         {
39970             this.setValue(this.defaultDialCode);
39971             this.validate();
39972         },
39973         
39974         hiddenEl : function()
39975         {
39976             return this.el.select('input.hidden-tel-input',true).first();
39977         },
39978         
39979         onKeyUp : function(e){
39980             
39981             var k = e.getKey();
39982             var c = e.getCharCode();
39983             
39984             if(
39985                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
39986                     this.allowed.indexOf(String.fromCharCode(c)) === -1
39987             ){
39988                 e.stopEvent();
39989             }
39990             
39991             // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
39992             //     return;
39993             // }
39994             if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
39995                 e.stopEvent();
39996             }
39997             
39998             this.setValue(this.getValue());
39999         }
40000         
40001 });
40002 /**
40003  * @class Roo.bootstrap.MoneyField
40004  * @extends Roo.bootstrap.ComboBox
40005  * Bootstrap MoneyField class
40006  * 
40007  * @constructor
40008  * Create a new MoneyField.
40009  * @param {Object} config Configuration options
40010  */
40011
40012 Roo.bootstrap.MoneyField = function(config) {
40013     
40014     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
40015     
40016 };
40017
40018 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
40019     
40020     /**
40021      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40022      */
40023     allowDecimals : true,
40024     /**
40025      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40026      */
40027     decimalSeparator : ".",
40028     /**
40029      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40030      */
40031     decimalPrecision : 2,
40032     /**
40033      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40034      */
40035     allowNegative : true,
40036     /**
40037      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40038      */
40039     minValue : Number.NEGATIVE_INFINITY,
40040     /**
40041      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40042      */
40043     maxValue : Number.MAX_VALUE,
40044     /**
40045      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40046      */
40047     minText : "The minimum value for this field is {0}",
40048     /**
40049      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40050      */
40051     maxText : "The maximum value for this field is {0}",
40052     /**
40053      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40054      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40055      */
40056     nanText : "{0} is not a valid number",
40057     /**
40058      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
40059      */
40060     castInt : true,
40061     /**
40062      * @cfg {String} defaults currency of the MoneyField
40063      * value should be in lkey
40064      */
40065     defaultCurrency : false,
40066     
40067     
40068     inputlg : 9,
40069     inputmd : 9,
40070     inputsm : 9,
40071     inputxs : 6,
40072     
40073     store : false,
40074     
40075     getAutoCreate : function()
40076     {
40077         var align = this.labelAlign || this.parentLabelAlign();
40078         
40079         var id = Roo.id();
40080
40081         var cfg = {
40082             cls: 'form-group',
40083             cn: []
40084         };
40085
40086         var input =  {
40087             tag: 'input',
40088             id : id,
40089             cls : 'form-control roo-money-amount-input',
40090             autocomplete: 'new-password'
40091         };
40092         
40093         if (this.name) {
40094             input.name = this.name;
40095         }
40096
40097         if (this.disabled) {
40098             input.disabled = true;
40099         }
40100
40101         var clg = 12 - this.inputlg;
40102         var cmd = 12 - this.inputmd;
40103         var csm = 12 - this.inputsm;
40104         var cxs = 12 - this.inputxs;
40105         
40106         var container = {
40107             tag : 'div',
40108             cls : 'row roo-money-field',
40109             cn : [
40110                 {
40111                     tag : 'div',
40112                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40113                     cn : [
40114                         {
40115                             tag : 'div',
40116                             cls: 'roo-select2-container input-group',
40117                             cn: [
40118                                 {
40119                                     tag : 'input',
40120                                     cls : 'form-control roo-money-currency-input',
40121                                     autocomplete: 'new-password',
40122                                     readOnly : 1,
40123                                     name : this.currencyName
40124                                 },
40125                                 {
40126                                     tag :'span',
40127                                     cls : 'input-group-addon',
40128                                     cn : [
40129                                         {
40130                                             tag: 'span',
40131                                             cls: 'caret'
40132                                         }
40133                                     ]
40134                                 }
40135                             ]
40136                         }
40137                     ]
40138                 },
40139                 {
40140                     tag : 'div',
40141                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40142                     cn : [
40143                         {
40144                             tag: 'div',
40145                             cls: this.hasFeedback ? 'has-feedback' : '',
40146                             cn: [
40147                                 input
40148                             ]
40149                         }
40150                     ]
40151                 }
40152             ]
40153             
40154         };
40155         
40156         if (this.fieldLabel.length) {
40157             var indicator = {
40158                 tag: 'i',
40159                 tooltip: 'This field is required'
40160             };
40161
40162             var label = {
40163                 tag: 'label',
40164                 'for':  id,
40165                 cls: 'control-label',
40166                 cn: []
40167             };
40168
40169             var label_text = {
40170                 tag: 'span',
40171                 html: this.fieldLabel
40172             };
40173
40174             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40175             label.cn = [
40176                 indicator,
40177                 label_text
40178             ];
40179
40180             if(this.indicatorpos == 'right') {
40181                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40182                 label.cn = [
40183                     label_text,
40184                     indicator
40185                 ];
40186             }
40187
40188             if(align == 'left') {
40189                 container = {
40190                     tag: 'div',
40191                     cn: [
40192                         container
40193                     ]
40194                 };
40195
40196                 if(this.labelWidth > 12){
40197                     label.style = "width: " + this.labelWidth + 'px';
40198                 }
40199                 if(this.labelWidth < 13 && this.labelmd == 0){
40200                     this.labelmd = this.labelWidth;
40201                 }
40202                 if(this.labellg > 0){
40203                     label.cls += ' col-lg-' + this.labellg;
40204                     input.cls += ' col-lg-' + (12 - this.labellg);
40205                 }
40206                 if(this.labelmd > 0){
40207                     label.cls += ' col-md-' + this.labelmd;
40208                     container.cls += ' col-md-' + (12 - this.labelmd);
40209                 }
40210                 if(this.labelsm > 0){
40211                     label.cls += ' col-sm-' + this.labelsm;
40212                     container.cls += ' col-sm-' + (12 - this.labelsm);
40213                 }
40214                 if(this.labelxs > 0){
40215                     label.cls += ' col-xs-' + this.labelxs;
40216                     container.cls += ' col-xs-' + (12 - this.labelxs);
40217                 }
40218             }
40219         }
40220
40221         cfg.cn = [
40222             label,
40223             container
40224         ];
40225
40226         var settings = this;
40227
40228         ['xs','sm','md','lg'].map(function(size){
40229             if (settings[size]) {
40230                 cfg.cls += ' col-' + size + '-' + settings[size];
40231             }
40232         });
40233         
40234         return cfg;
40235         
40236     },
40237     
40238     initEvents : function()
40239     {
40240         this.indicator = this.indicatorEl();
40241         
40242         this.initCurrencyEvent();
40243         
40244         this.initNumberEvent();
40245         
40246     },
40247     
40248     initCurrencyEvent : function()
40249     {
40250         if (!this.store) {
40251             throw "can not find store for combo";
40252         }
40253         
40254         this.store = Roo.factory(this.store, Roo.data);
40255         this.store.parent = this;
40256         
40257         this.createList();
40258         
40259         this.triggerEl = this.el.select('.input-group-addon', true).first();
40260         
40261         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40262         
40263         var _this = this;
40264         
40265         (function(){
40266             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40267             _this.list.setWidth(lw);
40268         }).defer(100);
40269         
40270         this.list.on('mouseover', this.onViewOver, this);
40271         this.list.on('mousemove', this.onViewMove, this);
40272         this.list.on('scroll', this.onViewScroll, this);
40273         
40274         if(!this.tpl){
40275             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40276         }
40277         
40278         this.view = new Roo.View(this.list, this.tpl, {
40279             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40280         });
40281         
40282         this.view.on('click', this.onViewClick, this);
40283         
40284         this.store.on('beforeload', this.onBeforeLoad, this);
40285         this.store.on('load', this.onLoad, this);
40286         this.store.on('loadexception', this.onLoadException, this);
40287         
40288         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40289             "up" : function(e){
40290                 this.inKeyMode = true;
40291                 this.selectPrev();
40292             },
40293
40294             "down" : function(e){
40295                 if(!this.isExpanded()){
40296                     this.onTriggerClick();
40297                 }else{
40298                     this.inKeyMode = true;
40299                     this.selectNext();
40300                 }
40301             },
40302
40303             "enter" : function(e){
40304                 this.collapse();
40305                 
40306                 if(this.fireEvent("specialkey", this, e)){
40307                     this.onViewClick(false);
40308                 }
40309                 
40310                 return true;
40311             },
40312
40313             "esc" : function(e){
40314                 this.collapse();
40315             },
40316
40317             "tab" : function(e){
40318                 this.collapse();
40319                 
40320                 if(this.fireEvent("specialkey", this, e)){
40321                     this.onViewClick(false);
40322                 }
40323                 
40324                 return true;
40325             },
40326
40327             scope : this,
40328
40329             doRelay : function(foo, bar, hname){
40330                 if(hname == 'down' || this.scope.isExpanded()){
40331                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40332                 }
40333                 return true;
40334             },
40335
40336             forceKeyDown: true
40337         });
40338         
40339         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40340         
40341     },
40342     
40343     initNumberEvent : function(e)
40344     {
40345         this.inputEl().on("keydown" , this.fireKey,  this);
40346         this.inputEl().on("focus", this.onFocus,  this);
40347         this.inputEl().on("blur", this.onBlur,  this);
40348         
40349         this.inputEl().relayEvent('keyup', this);
40350         
40351         if(this.indicator){
40352             this.indicator.addClass('invisible');
40353         }
40354  
40355         this.originalValue = this.getValue();
40356         
40357         if(this.validationEvent == 'keyup'){
40358             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40359             this.inputEl().on('keyup', this.filterValidation, this);
40360         }
40361         else if(this.validationEvent !== false){
40362             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40363         }
40364         
40365         if(this.selectOnFocus){
40366             this.on("focus", this.preFocus, this);
40367             
40368         }
40369         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40370             this.inputEl().on("keypress", this.filterKeys, this);
40371         } else {
40372             this.inputEl().relayEvent('keypress', this);
40373         }
40374         
40375         var allowed = "0123456789";
40376         
40377         if(this.allowDecimals){
40378             allowed += this.decimalSeparator;
40379         }
40380         
40381         if(this.allowNegative){
40382             allowed += "-";
40383         }
40384         
40385         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40386         
40387         var keyPress = function(e){
40388             
40389             var k = e.getKey();
40390             
40391             var c = e.getCharCode();
40392             
40393             if(
40394                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40395                     allowed.indexOf(String.fromCharCode(c)) === -1
40396             ){
40397                 e.stopEvent();
40398                 return;
40399             }
40400             
40401             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40402                 return;
40403             }
40404             
40405             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40406                 e.stopEvent();
40407             }
40408         };
40409         
40410         this.inputEl().on("keypress", keyPress, this);
40411         
40412     },
40413     
40414     onTriggerClick : function(e)
40415     {   
40416         if(this.disabled){
40417             return;
40418         }
40419         
40420         this.page = 0;
40421         this.loadNext = false;
40422         
40423         if(this.isExpanded()){
40424             this.collapse();
40425             return;
40426         }
40427         
40428         this.hasFocus = true;
40429         
40430         if(this.triggerAction == 'all') {
40431             this.doQuery(this.allQuery, true);
40432             return;
40433         }
40434         
40435         this.doQuery(this.getRawValue());
40436     },
40437     
40438     getCurrency : function()
40439     {   
40440         var v = this.currencyEl().getValue();
40441         
40442         return v;
40443     },
40444     
40445     restrictHeight : function()
40446     {
40447         this.list.alignTo(this.currencyEl(), this.listAlign);
40448         this.list.alignTo(this.currencyEl(), this.listAlign);
40449     },
40450     
40451     onViewClick : function(view, doFocus, el, e)
40452     {
40453         var index = this.view.getSelectedIndexes()[0];
40454         
40455         var r = this.store.getAt(index);
40456         
40457         if(r){
40458             this.onSelect(r, index);
40459         }
40460     },
40461     
40462     onSelect : function(record, index){
40463         
40464         if(this.fireEvent('beforeselect', this, record, index) !== false){
40465         
40466             this.setFromCurrencyData(index > -1 ? record.data : false);
40467             
40468             this.collapse();
40469             
40470             this.fireEvent('select', this, record, index);
40471         }
40472     },
40473     
40474     setFromCurrencyData : function(o)
40475     {
40476         var currency = '';
40477         
40478         this.lastCurrency = o;
40479         
40480         if (this.currencyField) {
40481             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40482         } else {
40483             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40484         }
40485         
40486         this.lastSelectionText = currency;
40487         
40488         //setting default currency
40489         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40490             this.setCurrency(this.defaultCurrency);
40491             return;
40492         }
40493         
40494         this.setCurrency(currency);
40495     },
40496     
40497     setFromData : function(o)
40498     {
40499         var c = {};
40500         
40501         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40502         
40503         this.setFromCurrencyData(c);
40504         
40505         var value = '';
40506         
40507         if (this.name) {
40508             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40509         } else {
40510             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40511         }
40512         
40513         this.setValue(value);
40514         
40515     },
40516     
40517     setCurrency : function(v)
40518     {   
40519         this.currencyValue = v;
40520         
40521         if(this.rendered){
40522             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40523             this.validate();
40524         }
40525     },
40526     
40527     setValue : function(v)
40528     {
40529         v = this.fixPrecision(v);
40530         
40531         v = String(v).replace(".", this.decimalSeparator);
40532         
40533         this.value = v;
40534         
40535         if(this.rendered){
40536             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40537             this.validate();
40538         }
40539     },
40540     
40541     getRawValue : function()
40542     {
40543         var v = this.inputEl().getValue();
40544         
40545         return v;
40546     },
40547     
40548     getValue : function()
40549     {
40550         return this.fixPrecision(this.parseValue(this.getRawValue()));
40551     },
40552     
40553     parseValue : function(value)
40554     {
40555         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40556         return isNaN(value) ? '' : value;
40557     },
40558     
40559     fixPrecision : function(value)
40560     {
40561         var nan = isNaN(value);
40562         
40563         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40564             return nan ? '' : value;
40565         }
40566         
40567         return parseFloat(value).toFixed(this.decimalPrecision);
40568     },
40569     
40570     decimalPrecisionFcn : function(v)
40571     {
40572         return Math.floor(v);
40573     },
40574     
40575     validateValue : function(value)
40576     {
40577         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40578             return false;
40579         }
40580         
40581         var num = this.parseValue(value);
40582         
40583         if(isNaN(num)){
40584             this.markInvalid(String.format(this.nanText, value));
40585             return false;
40586         }
40587         
40588         if(num < this.minValue){
40589             this.markInvalid(String.format(this.minText, this.minValue));
40590             return false;
40591         }
40592         
40593         if(num > this.maxValue){
40594             this.markInvalid(String.format(this.maxText, this.maxValue));
40595             return false;
40596         }
40597         
40598         return true;
40599     },
40600     
40601     validate : function()
40602     {
40603         if(this.disabled || this.allowBlank){
40604             this.markValid();
40605             return true;
40606         }
40607         
40608         var currency = this.getCurrency();
40609         
40610         if(this.validateValue(this.getRawValue()) && currency.length){
40611             this.markValid();
40612             return true;
40613         }
40614         
40615         this.markInvalid();
40616         return false;
40617     },
40618     
40619     getName: function()
40620     {
40621         return this.name;
40622     },
40623     
40624     beforeBlur : function()
40625     {
40626         if(!this.castInt){
40627             return;
40628         }
40629         
40630         var v = this.parseValue(this.getRawValue());
40631         
40632         if(v){
40633             this.setValue(v);
40634         }
40635     },
40636     
40637     onBlur : function()
40638     {
40639         this.beforeBlur();
40640         
40641         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40642             //this.el.removeClass(this.focusClass);
40643         }
40644         
40645         this.hasFocus = false;
40646         
40647         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40648             this.validate();
40649         }
40650         
40651         var v = this.getValue();
40652         
40653         if(String(v) !== String(this.startValue)){
40654             this.fireEvent('change', this, v, this.startValue);
40655         }
40656         
40657         this.fireEvent("blur", this);
40658     },
40659     
40660     inputEl : function()
40661     {
40662         return this.el.select('.roo-money-amount-input', true).first();
40663     },
40664     
40665     currencyEl : function()
40666     {
40667         return this.el.select('.roo-money-currency-input', true).first();
40668     }
40669     
40670 });