roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /*
2  * - LGPL
3  *
4  * base class for bootstrap elements.
5  * 
6  */
7
8 Roo.bootstrap = Roo.bootstrap || {};
9 /**
10  * @class Roo.bootstrap.Component
11  * @extends Roo.Component
12  * Bootstrap Component base class
13  * @cfg {String} cls css class
14  * @cfg {String} style any extra css
15  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
16  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
17  * @cfg {string} dataId cutomer id
18  * @cfg {string} name Specifies name attribute
19  * @cfg {string} tooltip  Text for the tooltip
20  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
21  * 
22  * @constructor
23  * Do not use directly - it does not do anything..
24  * @param {Object} config The config object
25  */
26
27
28
29 Roo.bootstrap.Component = function(config){
30     Roo.bootstrap.Component.superclass.constructor.call(this, config);
31        
32     this.addEvents({
33         /**
34          * @event childrenrendered
35          * Fires when the children have been rendered..
36          * @param {Roo.bootstrap.Component} this
37          */
38         "childrenrendered" : true
39         
40         
41         
42     });
43     
44     
45 };
46
47 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
48     
49     
50     allowDomMove : false, // to stop relocations in parent onRender...
51     
52     cls : false,
53     
54     style : false,
55     
56     autoCreate : false,
57     
58     tooltip : null,
59     /**
60      * Initialize Events for the element
61      */
62     initEvents : function() { },
63     
64     xattr : false,
65     
66     parentId : false,
67     
68     can_build_overlaid : true,
69     
70     container_method : false,
71     
72     dataId : false,
73     
74     name : false,
75     
76     parent: function() {
77         // returns the parent component..
78         return Roo.ComponentMgr.get(this.parentId)
79         
80         
81     },
82     
83     // private
84     onRender : function(ct, position)
85     {
86        // Roo.log("Call onRender: " + this.xtype);
87         
88         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
89         
90         if(this.el){
91             if (this.el.attr('xtype')) {
92                 this.el.attr('xtypex', this.el.attr('xtype'));
93                 this.el.dom.removeAttribute('xtype');
94                 
95                 this.initEvents();
96             }
97             
98             return;
99         }
100         
101          
102         
103         var cfg = Roo.apply({},  this.getAutoCreate());
104         
105         cfg.id = this.id || Roo.id();
106         
107         // fill in the extra attributes 
108         if (this.xattr && typeof(this.xattr) =='object') {
109             for (var i in this.xattr) {
110                 cfg[i] = this.xattr[i];
111             }
112         }
113         
114         if(this.dataId){
115             cfg.dataId = this.dataId;
116         }
117         
118         if (this.cls) {
119             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
120         }
121         
122         if (this.style) { // fixme needs to support more complex style data.
123             cfg.style = this.style;
124         }
125         
126         if(this.name){
127             cfg.name = this.name;
128         }
129         
130         this.el = ct.createChild(cfg, position);
131         
132         if (this.tooltip) {
133             this.tooltipEl().attr('tooltip', this.tooltip);
134         }
135         
136         if(this.tabIndex !== undefined){
137             this.el.dom.setAttribute('tabIndex', this.tabIndex);
138         }
139         
140         this.initEvents();
141         
142     },
143     /**
144      * Fetch the element to add children to
145      * @return {Roo.Element} defaults to this.el
146      */
147     getChildContainer : function()
148     {
149         return this.el;
150     },
151     /**
152      * Fetch the element to display the tooltip on.
153      * @return {Roo.Element} defaults to this.el
154      */
155     tooltipEl : function()
156     {
157         return this.el;
158     },
159         
160     addxtype  : function(tree,cntr)
161     {
162         var cn = this;
163         
164         cn = Roo.factory(tree);
165         //Roo.log(['addxtype', cn]);
166            
167         cn.parentType = this.xtype; //??
168         cn.parentId = this.id;
169         
170         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
171         if (typeof(cn.container_method) == 'string') {
172             cntr = cn.container_method;
173         }
174         
175         
176         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
177         
178         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
179         
180         var build_from_html =  Roo.XComponent.build_from_html;
181           
182         var is_body  = (tree.xtype == 'Body') ;
183           
184         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
185           
186         var self_cntr_el = Roo.get(this[cntr](false));
187         
188         // do not try and build conditional elements 
189         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
190             return false;
191         }
192         
193         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
194             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
195                 return this.addxtypeChild(tree,cntr, is_body);
196             }
197             
198             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
199                 
200             if(echild){
201                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
202             }
203             
204             Roo.log('skipping render');
205             return cn;
206             
207         }
208         
209         var ret = false;
210         if (!build_from_html) {
211             return false;
212         }
213         
214         // this i think handles overlaying multiple children of the same type
215         // with the sam eelement.. - which might be buggy..
216         while (true) {
217             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
218             
219             if (!echild) {
220                 break;
221             }
222             
223             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
224                 break;
225             }
226             
227             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
228         }
229        
230         return ret;
231     },
232     
233     
234     addxtypeChild : function (tree, cntr, is_body)
235     {
236         Roo.debug && Roo.log('addxtypeChild:' + cntr);
237         var cn = this;
238         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
239         
240         
241         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
242                     (typeof(tree['flexy:foreach']) != 'undefined');
243           
244     
245         
246         skip_children = false;
247         // render the element if it's not BODY.
248         if (!is_body) {
249             
250             // 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         
6135     },
6136     
6137     onContextMenu : function(e, t)
6138     {
6139         this.processEvent("contextmenu", e);
6140     },
6141     
6142     processEvent : function(name, e)
6143     {
6144         if (name != 'touchstart' ) {
6145             this.fireEvent(name, e);    
6146         }
6147         
6148         var t = e.getTarget();
6149         
6150         var cell = Roo.get(t);
6151         
6152         if(!cell){
6153             return;
6154         }
6155         
6156         if(cell.findParent('tfoot', false, true)){
6157             return;
6158         }
6159         
6160         if(cell.findParent('thead', false, true)){
6161             
6162             if(e.getTarget().nodeName.toLowerCase() != 'th'){
6163                 cell = Roo.get(t).findParent('th', false, true);
6164                 if (!cell) {
6165                     Roo.log("failed to find th in thead?");
6166                     Roo.log(e.getTarget());
6167                     return;
6168                 }
6169             }
6170             
6171             var cellIndex = cell.dom.cellIndex;
6172             
6173             var ename = name == 'touchstart' ? 'click' : name;
6174             this.fireEvent("header" + ename, this, cellIndex, e);
6175             
6176             return;
6177         }
6178         
6179         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6180             cell = Roo.get(t).findParent('td', false, true);
6181             if (!cell) {
6182                 Roo.log("failed to find th in tbody?");
6183                 Roo.log(e.getTarget());
6184                 return;
6185             }
6186         }
6187         
6188         var row = cell.findParent('tr', false, true);
6189         var cellIndex = cell.dom.cellIndex;
6190         var rowIndex = row.dom.rowIndex - 1;
6191         
6192         if(row !== false){
6193             
6194             this.fireEvent("row" + name, this, rowIndex, e);
6195             
6196             if(cell !== false){
6197             
6198                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
6199             }
6200         }
6201         
6202     },
6203     
6204     onMouseover : function(e, el)
6205     {
6206         var cell = Roo.get(el);
6207         
6208         if(!cell){
6209             return;
6210         }
6211         
6212         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6213             cell = cell.findParent('td', false, true);
6214         }
6215         
6216         var row = cell.findParent('tr', false, true);
6217         var cellIndex = cell.dom.cellIndex;
6218         var rowIndex = row.dom.rowIndex - 1; // start from 0
6219         
6220         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
6221         
6222     },
6223     
6224     onMouseout : function(e, el)
6225     {
6226         var cell = Roo.get(el);
6227         
6228         if(!cell){
6229             return;
6230         }
6231         
6232         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6233             cell = cell.findParent('td', false, true);
6234         }
6235         
6236         var row = cell.findParent('tr', false, true);
6237         var cellIndex = cell.dom.cellIndex;
6238         var rowIndex = row.dom.rowIndex - 1; // start from 0
6239         
6240         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
6241         
6242     },
6243     
6244     onClick : function(e, el)
6245     {
6246         var cell = Roo.get(el);
6247         
6248         if(!cell || (!this.cellSelection && !this.rowSelection)){
6249             return;
6250         }
6251         
6252         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6253             cell = cell.findParent('td', false, true);
6254         }
6255         
6256         if(!cell || typeof(cell) == 'undefined'){
6257             return;
6258         }
6259         
6260         var row = cell.findParent('tr', false, true);
6261         
6262         if(!row || typeof(row) == 'undefined'){
6263             return;
6264         }
6265         
6266         var cellIndex = cell.dom.cellIndex;
6267         var rowIndex = this.getRowIndex(row);
6268         
6269         // why??? - should these not be based on SelectionModel?
6270         if(this.cellSelection){
6271             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
6272         }
6273         
6274         if(this.rowSelection){
6275             this.fireEvent('rowclick', this, row, rowIndex, e);
6276         }
6277         
6278         
6279     },
6280         
6281     onDblClick : function(e,el)
6282     {
6283         var cell = Roo.get(el);
6284         
6285         if(!cell || (!this.cellSelection && !this.rowSelection)){
6286             return;
6287         }
6288         
6289         if(e.getTarget().nodeName.toLowerCase() != 'td'){
6290             cell = cell.findParent('td', false, true);
6291         }
6292         
6293         if(!cell || typeof(cell) == 'undefined'){
6294             return;
6295         }
6296         
6297         var row = cell.findParent('tr', false, true);
6298         
6299         if(!row || typeof(row) == 'undefined'){
6300             return;
6301         }
6302         
6303         var cellIndex = cell.dom.cellIndex;
6304         var rowIndex = this.getRowIndex(row);
6305         
6306         if(this.cellSelection){
6307             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
6308         }
6309         
6310         if(this.rowSelection){
6311             this.fireEvent('rowdblclick', this, row, rowIndex, e);
6312         }
6313     },
6314     
6315     sort : function(e,el)
6316     {
6317         var col = Roo.get(el);
6318         
6319         if(!col.hasClass('sortable')){
6320             return;
6321         }
6322         
6323         var sort = col.attr('sort');
6324         var dir = 'ASC';
6325         
6326         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
6327             dir = 'DESC';
6328         }
6329         
6330         this.store.sortInfo = {field : sort, direction : dir};
6331         
6332         if (this.footer) {
6333             Roo.log("calling footer first");
6334             this.footer.onClick('first');
6335         } else {
6336         
6337             this.store.load({ params : { start : 0 } });
6338         }
6339     },
6340     
6341     renderHeader : function()
6342     {
6343         var header = {
6344             tag: 'thead',
6345             cn : []
6346         };
6347         
6348         var cm = this.cm;
6349         this.totalWidth = 0;
6350         
6351         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6352             
6353             var config = cm.config[i];
6354             
6355             var c = {
6356                 tag: 'th',
6357                 style : '',
6358                 html: cm.getColumnHeader(i)
6359             };
6360             
6361             var hh = '';
6362             
6363             if(typeof(config.sortable) != 'undefined' && config.sortable){
6364                 c.cls = 'sortable';
6365                 c.html = '<i class="glyphicon"></i>' + c.html;
6366             }
6367             
6368             if(typeof(config.lgHeader) != 'undefined'){
6369                 hh += '<span class="hidden-xs hidden-sm hidden-md">' + config.lgHeader + '</span>';
6370             }
6371             
6372             if(typeof(config.mdHeader) != 'undefined'){
6373                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
6374             }
6375             
6376             if(typeof(config.smHeader) != 'undefined'){
6377                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
6378             }
6379             
6380             if(typeof(config.xsHeader) != 'undefined'){
6381                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
6382             }
6383             
6384             if(hh.length){
6385                 c.html = hh;
6386             }
6387             
6388             if(typeof(config.tooltip) != 'undefined'){
6389                 c.tooltip = config.tooltip;
6390             }
6391             
6392             if(typeof(config.colspan) != 'undefined'){
6393                 c.colspan = config.colspan;
6394             }
6395             
6396             if(typeof(config.hidden) != 'undefined' && config.hidden){
6397                 c.style += ' display:none;';
6398             }
6399             
6400             if(typeof(config.dataIndex) != 'undefined'){
6401                 c.sort = config.dataIndex;
6402             }
6403             
6404            
6405             
6406             if(typeof(config.align) != 'undefined' && config.align.length){
6407                 c.style += ' text-align:' + config.align + ';';
6408             }
6409             
6410             if(typeof(config.width) != 'undefined'){
6411                 c.style += ' width:' + config.width + 'px;';
6412                 this.totalWidth += config.width;
6413             } else {
6414                 this.totalWidth += 100; // assume minimum of 100 per column?
6415             }
6416             
6417             if(typeof(config.cls) != 'undefined'){
6418                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
6419             }
6420             
6421             ['xs','sm','md','lg'].map(function(size){
6422                 
6423                 if(typeof(config[size]) == 'undefined'){
6424                     return;
6425                 }
6426                 
6427                 if (!config[size]) { // 0 = hidden
6428                     c.cls += ' hidden-' + size;
6429                     return;
6430                 }
6431                 
6432                 c.cls += ' col-' + size + '-' + config[size];
6433
6434             });
6435             
6436             header.cn.push(c)
6437         }
6438         
6439         return header;
6440     },
6441     
6442     renderBody : function()
6443     {
6444         var body = {
6445             tag: 'tbody',
6446             cn : [
6447                 {
6448                     tag: 'tr',
6449                     cn : [
6450                         {
6451                             tag : 'td',
6452                             colspan :  this.cm.getColumnCount()
6453                         }
6454                     ]
6455                 }
6456             ]
6457         };
6458         
6459         return body;
6460     },
6461     
6462     renderFooter : function()
6463     {
6464         var footer = {
6465             tag: 'tfoot',
6466             cn : [
6467                 {
6468                     tag: 'tr',
6469                     cn : [
6470                         {
6471                             tag : 'td',
6472                             colspan :  this.cm.getColumnCount()
6473                         }
6474                     ]
6475                 }
6476             ]
6477         };
6478         
6479         return footer;
6480     },
6481     
6482     
6483     
6484     onLoad : function()
6485     {
6486 //        Roo.log('ds onload');
6487         this.clear();
6488         
6489         var _this = this;
6490         var cm = this.cm;
6491         var ds = this.store;
6492         
6493         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
6494             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
6495             if (_this.store.sortInfo) {
6496                     
6497                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
6498                     e.select('i', true).addClass(['glyphicon-arrow-up']);
6499                 }
6500                 
6501                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
6502                     e.select('i', true).addClass(['glyphicon-arrow-down']);
6503                 }
6504             }
6505         });
6506         
6507         var tbody =  this.mainBody;
6508               
6509         if(ds.getCount() > 0){
6510             ds.data.each(function(d,rowIndex){
6511                 var row =  this.renderRow(cm, ds, rowIndex);
6512                 
6513                 tbody.createChild(row);
6514                 
6515                 var _this = this;
6516                 
6517                 if(row.cellObjects.length){
6518                     Roo.each(row.cellObjects, function(r){
6519                         _this.renderCellObject(r);
6520                     })
6521                 }
6522                 
6523             }, this);
6524         }
6525         
6526         Roo.each(this.el.select('tbody td', true).elements, function(e){
6527             e.on('mouseover', _this.onMouseover, _this);
6528         });
6529         
6530         Roo.each(this.el.select('tbody td', true).elements, function(e){
6531             e.on('mouseout', _this.onMouseout, _this);
6532         });
6533         this.fireEvent('rowsrendered', this);
6534         //if(this.loadMask){
6535         //    this.maskEl.hide();
6536         //}
6537         
6538         this.autoSize();
6539     },
6540     
6541     
6542     onUpdate : function(ds,record)
6543     {
6544         this.refreshRow(record);
6545         this.autoSize();
6546     },
6547     
6548     onRemove : function(ds, record, index, isUpdate){
6549         if(isUpdate !== true){
6550             this.fireEvent("beforerowremoved", this, index, record);
6551         }
6552         var bt = this.mainBody.dom;
6553         
6554         var rows = this.el.select('tbody > tr', true).elements;
6555         
6556         if(typeof(rows[index]) != 'undefined'){
6557             bt.removeChild(rows[index].dom);
6558         }
6559         
6560 //        if(bt.rows[index]){
6561 //            bt.removeChild(bt.rows[index]);
6562 //        }
6563         
6564         if(isUpdate !== true){
6565             //this.stripeRows(index);
6566             //this.syncRowHeights(index, index);
6567             //this.layout();
6568             this.fireEvent("rowremoved", this, index, record);
6569         }
6570     },
6571     
6572     onAdd : function(ds, records, rowIndex)
6573     {
6574         //Roo.log('on Add called');
6575         // - note this does not handle multiple adding very well..
6576         var bt = this.mainBody.dom;
6577         for (var i =0 ; i < records.length;i++) {
6578             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
6579             //Roo.log(records[i]);
6580             //Roo.log(this.store.getAt(rowIndex+i));
6581             this.insertRow(this.store, rowIndex + i, false);
6582             return;
6583         }
6584         
6585     },
6586     
6587     
6588     refreshRow : function(record){
6589         var ds = this.store, index;
6590         if(typeof record == 'number'){
6591             index = record;
6592             record = ds.getAt(index);
6593         }else{
6594             index = ds.indexOf(record);
6595         }
6596         this.insertRow(ds, index, true);
6597         this.autoSize();
6598         this.onRemove(ds, record, index+1, true);
6599         this.autoSize();
6600         //this.syncRowHeights(index, index);
6601         //this.layout();
6602         this.fireEvent("rowupdated", this, index, record);
6603     },
6604     
6605     insertRow : function(dm, rowIndex, isUpdate){
6606         
6607         if(!isUpdate){
6608             this.fireEvent("beforerowsinserted", this, rowIndex);
6609         }
6610             //var s = this.getScrollState();
6611         var row = this.renderRow(this.cm, this.store, rowIndex);
6612         // insert before rowIndex..
6613         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
6614         
6615         var _this = this;
6616                 
6617         if(row.cellObjects.length){
6618             Roo.each(row.cellObjects, function(r){
6619                 _this.renderCellObject(r);
6620             })
6621         }
6622             
6623         if(!isUpdate){
6624             this.fireEvent("rowsinserted", this, rowIndex);
6625             //this.syncRowHeights(firstRow, lastRow);
6626             //this.stripeRows(firstRow);
6627             //this.layout();
6628         }
6629         
6630     },
6631     
6632     
6633     getRowDom : function(rowIndex)
6634     {
6635         var rows = this.el.select('tbody > tr', true).elements;
6636         
6637         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
6638         
6639     },
6640     // returns the object tree for a tr..
6641   
6642     
6643     renderRow : function(cm, ds, rowIndex) 
6644     {
6645         
6646         var d = ds.getAt(rowIndex);
6647         
6648         var row = {
6649             tag : 'tr',
6650             cn : []
6651         };
6652             
6653         var cellObjects = [];
6654         
6655         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
6656             var config = cm.config[i];
6657             
6658             var renderer = cm.getRenderer(i);
6659             var value = '';
6660             var id = false;
6661             
6662             if(typeof(renderer) !== 'undefined'){
6663                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
6664             }
6665             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
6666             // and are rendered into the cells after the row is rendered - using the id for the element.
6667             
6668             if(typeof(value) === 'object'){
6669                 id = Roo.id();
6670                 cellObjects.push({
6671                     container : id,
6672                     cfg : value 
6673                 })
6674             }
6675             
6676             var rowcfg = {
6677                 record: d,
6678                 rowIndex : rowIndex,
6679                 colIndex : i,
6680                 rowClass : ''
6681             };
6682
6683             this.fireEvent('rowclass', this, rowcfg);
6684             
6685             var td = {
6686                 tag: 'td',
6687                 cls : rowcfg.rowClass,
6688                 style: '',
6689                 html: (typeof(value) === 'object') ? '' : value
6690             };
6691             
6692             if (id) {
6693                 td.id = id;
6694             }
6695             
6696             if(typeof(config.colspan) != 'undefined'){
6697                 td.colspan = config.colspan;
6698             }
6699             
6700             if(typeof(config.hidden) != 'undefined' && config.hidden){
6701                 td.style += ' display:none;';
6702             }
6703             
6704             if(typeof(config.align) != 'undefined' && config.align.length){
6705                 td.style += ' text-align:' + config.align + ';';
6706             }
6707             
6708             if(typeof(config.width) != 'undefined'){
6709                 td.style += ' width:' +  config.width + 'px;';
6710             }
6711             
6712             if(typeof(config.cursor) != 'undefined'){
6713                 td.style += ' cursor:' +  config.cursor + ';';
6714             }
6715             
6716             if(typeof(config.cls) != 'undefined'){
6717                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
6718             }
6719             
6720             ['xs','sm','md','lg'].map(function(size){
6721                 
6722                 if(typeof(config[size]) == 'undefined'){
6723                     return;
6724                 }
6725                 
6726                 if (!config[size]) { // 0 = hidden
6727                     td.cls += ' hidden-' + size;
6728                     return;
6729                 }
6730                 
6731                 td.cls += ' col-' + size + '-' + config[size];
6732
6733             });
6734              
6735             row.cn.push(td);
6736            
6737         }
6738         
6739         row.cellObjects = cellObjects;
6740         
6741         return row;
6742           
6743     },
6744     
6745     
6746     
6747     onBeforeLoad : function()
6748     {
6749         //Roo.log('ds onBeforeLoad');
6750         
6751         //this.clear();
6752         
6753         //if(this.loadMask){
6754         //    this.maskEl.show();
6755         //}
6756     },
6757      /**
6758      * Remove all rows
6759      */
6760     clear : function()
6761     {
6762         this.el.select('tbody', true).first().dom.innerHTML = '';
6763     },
6764     /**
6765      * Show or hide a row.
6766      * @param {Number} rowIndex to show or hide
6767      * @param {Boolean} state hide
6768      */
6769     setRowVisibility : function(rowIndex, state)
6770     {
6771         var bt = this.mainBody.dom;
6772         
6773         var rows = this.el.select('tbody > tr', true).elements;
6774         
6775         if(typeof(rows[rowIndex]) == 'undefined'){
6776             return;
6777         }
6778         rows[rowIndex].dom.style.display = state ? '' : 'none';
6779     },
6780     
6781     
6782     getSelectionModel : function(){
6783         if(!this.selModel){
6784             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
6785         }
6786         return this.selModel;
6787     },
6788     /*
6789      * Render the Roo.bootstrap object from renderder
6790      */
6791     renderCellObject : function(r)
6792     {
6793         var _this = this;
6794         
6795         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
6796         
6797         var t = r.cfg.render(r.container);
6798         
6799         if(r.cfg.cn){
6800             Roo.each(r.cfg.cn, function(c){
6801                 var child = {
6802                     container: t.getChildContainer(),
6803                     cfg: c
6804                 };
6805                 _this.renderCellObject(child);
6806             })
6807         }
6808     },
6809     
6810     getRowIndex : function(row)
6811     {
6812         var rowIndex = -1;
6813         
6814         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
6815             if(el != row){
6816                 return;
6817             }
6818             
6819             rowIndex = index;
6820         });
6821         
6822         return rowIndex;
6823     },
6824      /**
6825      * Returns the grid's underlying element = used by panel.Grid
6826      * @return {Element} The element
6827      */
6828     getGridEl : function(){
6829         return this.el;
6830     },
6831      /**
6832      * Forces a resize - used by panel.Grid
6833      * @return {Element} The element
6834      */
6835     autoSize : function()
6836     {
6837         //var ctr = Roo.get(this.container.dom.parentElement);
6838         var ctr = Roo.get(this.el.dom);
6839         
6840         var thd = this.getGridEl().select('thead',true).first();
6841         var tbd = this.getGridEl().select('tbody', true).first();
6842         var tfd = this.getGridEl().select('tfoot', true).first();
6843         
6844         var cw = ctr.getWidth();
6845         
6846         if (tbd) {
6847             
6848             tbd.setSize(ctr.getWidth(),
6849                         ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
6850             );
6851             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
6852             cw -= barsize;
6853         }
6854         cw = Math.max(cw, this.totalWidth);
6855         this.getGridEl().select('tr',true).setWidth(cw);
6856         // resize 'expandable coloumn?
6857         
6858         return; // we doe not have a view in this design..
6859         
6860     },
6861     onBodyScroll: function()
6862     {
6863         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
6864         this.mainHead.setStyle({
6865             'position' : 'relative',
6866             'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
6867         });
6868         
6869         if(this.lazyLoad){
6870             
6871             var scrollHeight = this.mainBody.dom.scrollHeight;
6872             
6873             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
6874             
6875             var height = this.mainBody.getHeight();
6876             
6877             if(scrollHeight - height == scrollTop) {
6878                 
6879                 var total = this.ds.getTotalCount();
6880                 
6881                 if(this.footer.cursor + this.footer.pageSize < total){
6882                     
6883                     this.footer.ds.load({
6884                         params : {
6885                             start : this.footer.cursor + this.footer.pageSize,
6886                             limit : this.footer.pageSize
6887                         },
6888                         add : true
6889                     });
6890                 }
6891             }
6892             
6893         }
6894     }
6895 });
6896
6897  
6898
6899  /*
6900  * - LGPL
6901  *
6902  * table cell
6903  * 
6904  */
6905
6906 /**
6907  * @class Roo.bootstrap.TableCell
6908  * @extends Roo.bootstrap.Component
6909  * Bootstrap TableCell class
6910  * @cfg {String} html cell contain text
6911  * @cfg {String} cls cell class
6912  * @cfg {String} tag cell tag (td|th) default td
6913  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
6914  * @cfg {String} align Aligns the content in a cell
6915  * @cfg {String} axis Categorizes cells
6916  * @cfg {String} bgcolor Specifies the background color of a cell
6917  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
6918  * @cfg {Number} colspan Specifies the number of columns a cell should span
6919  * @cfg {String} headers Specifies one or more header cells a cell is related to
6920  * @cfg {Number} height Sets the height of a cell
6921  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
6922  * @cfg {Number} rowspan Sets the number of rows a cell should span
6923  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
6924  * @cfg {String} valign Vertical aligns the content in a cell
6925  * @cfg {Number} width Specifies the width of a cell
6926  * 
6927  * @constructor
6928  * Create a new TableCell
6929  * @param {Object} config The config object
6930  */
6931
6932 Roo.bootstrap.TableCell = function(config){
6933     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
6934 };
6935
6936 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
6937     
6938     html: false,
6939     cls: false,
6940     tag: false,
6941     abbr: false,
6942     align: false,
6943     axis: false,
6944     bgcolor: false,
6945     charoff: false,
6946     colspan: false,
6947     headers: false,
6948     height: false,
6949     nowrap: false,
6950     rowspan: false,
6951     scope: false,
6952     valign: false,
6953     width: false,
6954     
6955     
6956     getAutoCreate : function(){
6957         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
6958         
6959         cfg = {
6960             tag: 'td'
6961         };
6962         
6963         if(this.tag){
6964             cfg.tag = this.tag;
6965         }
6966         
6967         if (this.html) {
6968             cfg.html=this.html
6969         }
6970         if (this.cls) {
6971             cfg.cls=this.cls
6972         }
6973         if (this.abbr) {
6974             cfg.abbr=this.abbr
6975         }
6976         if (this.align) {
6977             cfg.align=this.align
6978         }
6979         if (this.axis) {
6980             cfg.axis=this.axis
6981         }
6982         if (this.bgcolor) {
6983             cfg.bgcolor=this.bgcolor
6984         }
6985         if (this.charoff) {
6986             cfg.charoff=this.charoff
6987         }
6988         if (this.colspan) {
6989             cfg.colspan=this.colspan
6990         }
6991         if (this.headers) {
6992             cfg.headers=this.headers
6993         }
6994         if (this.height) {
6995             cfg.height=this.height
6996         }
6997         if (this.nowrap) {
6998             cfg.nowrap=this.nowrap
6999         }
7000         if (this.rowspan) {
7001             cfg.rowspan=this.rowspan
7002         }
7003         if (this.scope) {
7004             cfg.scope=this.scope
7005         }
7006         if (this.valign) {
7007             cfg.valign=this.valign
7008         }
7009         if (this.width) {
7010             cfg.width=this.width
7011         }
7012         
7013         
7014         return cfg;
7015     }
7016    
7017 });
7018
7019  
7020
7021  /*
7022  * - LGPL
7023  *
7024  * table row
7025  * 
7026  */
7027
7028 /**
7029  * @class Roo.bootstrap.TableRow
7030  * @extends Roo.bootstrap.Component
7031  * Bootstrap TableRow class
7032  * @cfg {String} cls row class
7033  * @cfg {String} align Aligns the content in a table row
7034  * @cfg {String} bgcolor Specifies a background color for a table row
7035  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
7036  * @cfg {String} valign Vertical aligns the content in a table row
7037  * 
7038  * @constructor
7039  * Create a new TableRow
7040  * @param {Object} config The config object
7041  */
7042
7043 Roo.bootstrap.TableRow = function(config){
7044     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
7045 };
7046
7047 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
7048     
7049     cls: false,
7050     align: false,
7051     bgcolor: false,
7052     charoff: false,
7053     valign: false,
7054     
7055     getAutoCreate : function(){
7056         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
7057         
7058         cfg = {
7059             tag: 'tr'
7060         };
7061             
7062         if(this.cls){
7063             cfg.cls = this.cls;
7064         }
7065         if(this.align){
7066             cfg.align = this.align;
7067         }
7068         if(this.bgcolor){
7069             cfg.bgcolor = this.bgcolor;
7070         }
7071         if(this.charoff){
7072             cfg.charoff = this.charoff;
7073         }
7074         if(this.valign){
7075             cfg.valign = this.valign;
7076         }
7077         
7078         return cfg;
7079     }
7080    
7081 });
7082
7083  
7084
7085  /*
7086  * - LGPL
7087  *
7088  * table body
7089  * 
7090  */
7091
7092 /**
7093  * @class Roo.bootstrap.TableBody
7094  * @extends Roo.bootstrap.Component
7095  * Bootstrap TableBody class
7096  * @cfg {String} cls element class
7097  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
7098  * @cfg {String} align Aligns the content inside the element
7099  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
7100  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
7101  * 
7102  * @constructor
7103  * Create a new TableBody
7104  * @param {Object} config The config object
7105  */
7106
7107 Roo.bootstrap.TableBody = function(config){
7108     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
7109 };
7110
7111 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
7112     
7113     cls: false,
7114     tag: false,
7115     align: false,
7116     charoff: false,
7117     valign: false,
7118     
7119     getAutoCreate : function(){
7120         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
7121         
7122         cfg = {
7123             tag: 'tbody'
7124         };
7125             
7126         if (this.cls) {
7127             cfg.cls=this.cls
7128         }
7129         if(this.tag){
7130             cfg.tag = this.tag;
7131         }
7132         
7133         if(this.align){
7134             cfg.align = this.align;
7135         }
7136         if(this.charoff){
7137             cfg.charoff = this.charoff;
7138         }
7139         if(this.valign){
7140             cfg.valign = this.valign;
7141         }
7142         
7143         return cfg;
7144     }
7145     
7146     
7147 //    initEvents : function()
7148 //    {
7149 //        
7150 //        if(!this.store){
7151 //            return;
7152 //        }
7153 //        
7154 //        this.store = Roo.factory(this.store, Roo.data);
7155 //        this.store.on('load', this.onLoad, this);
7156 //        
7157 //        this.store.load();
7158 //        
7159 //    },
7160 //    
7161 //    onLoad: function () 
7162 //    {   
7163 //        this.fireEvent('load', this);
7164 //    }
7165 //    
7166 //   
7167 });
7168
7169  
7170
7171  /*
7172  * Based on:
7173  * Ext JS Library 1.1.1
7174  * Copyright(c) 2006-2007, Ext JS, LLC.
7175  *
7176  * Originally Released Under LGPL - original licence link has changed is not relivant.
7177  *
7178  * Fork - LGPL
7179  * <script type="text/javascript">
7180  */
7181
7182 // as we use this in bootstrap.
7183 Roo.namespace('Roo.form');
7184  /**
7185  * @class Roo.form.Action
7186  * Internal Class used to handle form actions
7187  * @constructor
7188  * @param {Roo.form.BasicForm} el The form element or its id
7189  * @param {Object} config Configuration options
7190  */
7191
7192  
7193  
7194 // define the action interface
7195 Roo.form.Action = function(form, options){
7196     this.form = form;
7197     this.options = options || {};
7198 };
7199 /**
7200  * Client Validation Failed
7201  * @const 
7202  */
7203 Roo.form.Action.CLIENT_INVALID = 'client';
7204 /**
7205  * Server Validation Failed
7206  * @const 
7207  */
7208 Roo.form.Action.SERVER_INVALID = 'server';
7209  /**
7210  * Connect to Server Failed
7211  * @const 
7212  */
7213 Roo.form.Action.CONNECT_FAILURE = 'connect';
7214 /**
7215  * Reading Data from Server Failed
7216  * @const 
7217  */
7218 Roo.form.Action.LOAD_FAILURE = 'load';
7219
7220 Roo.form.Action.prototype = {
7221     type : 'default',
7222     failureType : undefined,
7223     response : undefined,
7224     result : undefined,
7225
7226     // interface method
7227     run : function(options){
7228
7229     },
7230
7231     // interface method
7232     success : function(response){
7233
7234     },
7235
7236     // interface method
7237     handleResponse : function(response){
7238
7239     },
7240
7241     // default connection failure
7242     failure : function(response){
7243         
7244         this.response = response;
7245         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7246         this.form.afterAction(this, false);
7247     },
7248
7249     processResponse : function(response){
7250         this.response = response;
7251         if(!response.responseText){
7252             return true;
7253         }
7254         this.result = this.handleResponse(response);
7255         return this.result;
7256     },
7257
7258     // utility functions used internally
7259     getUrl : function(appendParams){
7260         var url = this.options.url || this.form.url || this.form.el.dom.action;
7261         if(appendParams){
7262             var p = this.getParams();
7263             if(p){
7264                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
7265             }
7266         }
7267         return url;
7268     },
7269
7270     getMethod : function(){
7271         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
7272     },
7273
7274     getParams : function(){
7275         var bp = this.form.baseParams;
7276         var p = this.options.params;
7277         if(p){
7278             if(typeof p == "object"){
7279                 p = Roo.urlEncode(Roo.applyIf(p, bp));
7280             }else if(typeof p == 'string' && bp){
7281                 p += '&' + Roo.urlEncode(bp);
7282             }
7283         }else if(bp){
7284             p = Roo.urlEncode(bp);
7285         }
7286         return p;
7287     },
7288
7289     createCallback : function(){
7290         return {
7291             success: this.success,
7292             failure: this.failure,
7293             scope: this,
7294             timeout: (this.form.timeout*1000),
7295             upload: this.form.fileUpload ? this.success : undefined
7296         };
7297     }
7298 };
7299
7300 Roo.form.Action.Submit = function(form, options){
7301     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
7302 };
7303
7304 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
7305     type : 'submit',
7306
7307     haveProgress : false,
7308     uploadComplete : false,
7309     
7310     // uploadProgress indicator.
7311     uploadProgress : function()
7312     {
7313         if (!this.form.progressUrl) {
7314             return;
7315         }
7316         
7317         if (!this.haveProgress) {
7318             Roo.MessageBox.progress("Uploading", "Uploading");
7319         }
7320         if (this.uploadComplete) {
7321            Roo.MessageBox.hide();
7322            return;
7323         }
7324         
7325         this.haveProgress = true;
7326    
7327         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
7328         
7329         var c = new Roo.data.Connection();
7330         c.request({
7331             url : this.form.progressUrl,
7332             params: {
7333                 id : uid
7334             },
7335             method: 'GET',
7336             success : function(req){
7337                //console.log(data);
7338                 var rdata = false;
7339                 var edata;
7340                 try  {
7341                    rdata = Roo.decode(req.responseText)
7342                 } catch (e) {
7343                     Roo.log("Invalid data from server..");
7344                     Roo.log(edata);
7345                     return;
7346                 }
7347                 if (!rdata || !rdata.success) {
7348                     Roo.log(rdata);
7349                     Roo.MessageBox.alert(Roo.encode(rdata));
7350                     return;
7351                 }
7352                 var data = rdata.data;
7353                 
7354                 if (this.uploadComplete) {
7355                    Roo.MessageBox.hide();
7356                    return;
7357                 }
7358                    
7359                 if (data){
7360                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
7361                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
7362                     );
7363                 }
7364                 this.uploadProgress.defer(2000,this);
7365             },
7366        
7367             failure: function(data) {
7368                 Roo.log('progress url failed ');
7369                 Roo.log(data);
7370             },
7371             scope : this
7372         });
7373            
7374     },
7375     
7376     
7377     run : function()
7378     {
7379         // run get Values on the form, so it syncs any secondary forms.
7380         this.form.getValues();
7381         
7382         var o = this.options;
7383         var method = this.getMethod();
7384         var isPost = method == 'POST';
7385         if(o.clientValidation === false || this.form.isValid()){
7386             
7387             if (this.form.progressUrl) {
7388                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
7389                     (new Date() * 1) + '' + Math.random());
7390                     
7391             } 
7392             
7393             
7394             Roo.Ajax.request(Roo.apply(this.createCallback(), {
7395                 form:this.form.el.dom,
7396                 url:this.getUrl(!isPost),
7397                 method: method,
7398                 params:isPost ? this.getParams() : null,
7399                 isUpload: this.form.fileUpload
7400             }));
7401             
7402             this.uploadProgress();
7403
7404         }else if (o.clientValidation !== false){ // client validation failed
7405             this.failureType = Roo.form.Action.CLIENT_INVALID;
7406             this.form.afterAction(this, false);
7407         }
7408     },
7409
7410     success : function(response)
7411     {
7412         this.uploadComplete= true;
7413         if (this.haveProgress) {
7414             Roo.MessageBox.hide();
7415         }
7416         
7417         
7418         var result = this.processResponse(response);
7419         if(result === true || result.success){
7420             this.form.afterAction(this, true);
7421             return;
7422         }
7423         if(result.errors){
7424             this.form.markInvalid(result.errors);
7425             this.failureType = Roo.form.Action.SERVER_INVALID;
7426         }
7427         this.form.afterAction(this, false);
7428     },
7429     failure : function(response)
7430     {
7431         this.uploadComplete= true;
7432         if (this.haveProgress) {
7433             Roo.MessageBox.hide();
7434         }
7435         
7436         this.response = response;
7437         this.failureType = Roo.form.Action.CONNECT_FAILURE;
7438         this.form.afterAction(this, false);
7439     },
7440     
7441     handleResponse : function(response){
7442         if(this.form.errorReader){
7443             var rs = this.form.errorReader.read(response);
7444             var errors = [];
7445             if(rs.records){
7446                 for(var i = 0, len = rs.records.length; i < len; i++) {
7447                     var r = rs.records[i];
7448                     errors[i] = r.data;
7449                 }
7450             }
7451             if(errors.length < 1){
7452                 errors = null;
7453             }
7454             return {
7455                 success : rs.success,
7456                 errors : errors
7457             };
7458         }
7459         var ret = false;
7460         try {
7461             ret = Roo.decode(response.responseText);
7462         } catch (e) {
7463             ret = {
7464                 success: false,
7465                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
7466                 errors : []
7467             };
7468         }
7469         return ret;
7470         
7471     }
7472 });
7473
7474
7475 Roo.form.Action.Load = function(form, options){
7476     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
7477     this.reader = this.form.reader;
7478 };
7479
7480 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
7481     type : 'load',
7482
7483     run : function(){
7484         
7485         Roo.Ajax.request(Roo.apply(
7486                 this.createCallback(), {
7487                     method:this.getMethod(),
7488                     url:this.getUrl(false),
7489                     params:this.getParams()
7490         }));
7491     },
7492
7493     success : function(response){
7494         
7495         var result = this.processResponse(response);
7496         if(result === true || !result.success || !result.data){
7497             this.failureType = Roo.form.Action.LOAD_FAILURE;
7498             this.form.afterAction(this, false);
7499             return;
7500         }
7501         this.form.clearInvalid();
7502         this.form.setValues(result.data);
7503         this.form.afterAction(this, true);
7504     },
7505
7506     handleResponse : function(response){
7507         if(this.form.reader){
7508             var rs = this.form.reader.read(response);
7509             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
7510             return {
7511                 success : rs.success,
7512                 data : data
7513             };
7514         }
7515         return Roo.decode(response.responseText);
7516     }
7517 });
7518
7519 Roo.form.Action.ACTION_TYPES = {
7520     'load' : Roo.form.Action.Load,
7521     'submit' : Roo.form.Action.Submit
7522 };/*
7523  * - LGPL
7524  *
7525  * form
7526  *
7527  */
7528
7529 /**
7530  * @class Roo.bootstrap.Form
7531  * @extends Roo.bootstrap.Component
7532  * Bootstrap Form class
7533  * @cfg {String} method  GET | POST (default POST)
7534  * @cfg {String} labelAlign top | left (default top)
7535  * @cfg {String} align left  | right - for navbars
7536  * @cfg {Boolean} loadMask load mask when submit (default true)
7537
7538  *
7539  * @constructor
7540  * Create a new Form
7541  * @param {Object} config The config object
7542  */
7543
7544
7545 Roo.bootstrap.Form = function(config){
7546     Roo.bootstrap.Form.superclass.constructor.call(this, config);
7547     
7548     Roo.bootstrap.Form.popover.apply();
7549     
7550     this.addEvents({
7551         /**
7552          * @event clientvalidation
7553          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
7554          * @param {Form} this
7555          * @param {Boolean} valid true if the form has passed client-side validation
7556          */
7557         clientvalidation: true,
7558         /**
7559          * @event beforeaction
7560          * Fires before any action is performed. Return false to cancel the action.
7561          * @param {Form} this
7562          * @param {Action} action The action to be performed
7563          */
7564         beforeaction: true,
7565         /**
7566          * @event actionfailed
7567          * Fires when an action fails.
7568          * @param {Form} this
7569          * @param {Action} action The action that failed
7570          */
7571         actionfailed : true,
7572         /**
7573          * @event actioncomplete
7574          * Fires when an action is completed.
7575          * @param {Form} this
7576          * @param {Action} action The action that completed
7577          */
7578         actioncomplete : true
7579     });
7580
7581 };
7582
7583 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
7584
7585      /**
7586      * @cfg {String} method
7587      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
7588      */
7589     method : 'POST',
7590     /**
7591      * @cfg {String} url
7592      * The URL to use for form actions if one isn't supplied in the action options.
7593      */
7594     /**
7595      * @cfg {Boolean} fileUpload
7596      * Set to true if this form is a file upload.
7597      */
7598
7599     /**
7600      * @cfg {Object} baseParams
7601      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
7602      */
7603
7604     /**
7605      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
7606      */
7607     timeout: 30,
7608     /**
7609      * @cfg {Sting} align (left|right) for navbar forms
7610      */
7611     align : 'left',
7612
7613     // private
7614     activeAction : null,
7615
7616     /**
7617      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
7618      * element by passing it or its id or mask the form itself by passing in true.
7619      * @type Mixed
7620      */
7621     waitMsgTarget : false,
7622
7623     loadMask : true,
7624     
7625     /**
7626      * @cfg {Boolean} errorMask (true|false) default false
7627      */
7628     errorMask : false,
7629     
7630     /**
7631      * @cfg {Number} maskOffset Default 100
7632      */
7633     maskOffset : 100,
7634     
7635     /**
7636      * @cfg {Boolean} maskBody
7637      */
7638     maskBody : false,
7639
7640     getAutoCreate : function(){
7641
7642         var cfg = {
7643             tag: 'form',
7644             method : this.method || 'POST',
7645             id : this.id || Roo.id(),
7646             cls : ''
7647         };
7648         if (this.parent().xtype.match(/^Nav/)) {
7649             cfg.cls = 'navbar-form navbar-' + this.align;
7650
7651         }
7652
7653         if (this.labelAlign == 'left' ) {
7654             cfg.cls += ' form-horizontal';
7655         }
7656
7657
7658         return cfg;
7659     },
7660     initEvents : function()
7661     {
7662         this.el.on('submit', this.onSubmit, this);
7663         // this was added as random key presses on the form where triggering form submit.
7664         this.el.on('keypress', function(e) {
7665             if (e.getCharCode() != 13) {
7666                 return true;
7667             }
7668             // we might need to allow it for textareas.. and some other items.
7669             // check e.getTarget().
7670
7671             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
7672                 return true;
7673             }
7674
7675             Roo.log("keypress blocked");
7676
7677             e.preventDefault();
7678             return false;
7679         });
7680         
7681     },
7682     // private
7683     onSubmit : function(e){
7684         e.stopEvent();
7685     },
7686
7687      /**
7688      * Returns true if client-side validation on the form is successful.
7689      * @return Boolean
7690      */
7691     isValid : function(){
7692         var items = this.getItems();
7693         var valid = true;
7694         var target = false;
7695         
7696         items.each(function(f){
7697             if(f.validate()){
7698                 return;
7699             }
7700             valid = false;
7701
7702             if(!target && f.el.isVisible(true)){
7703                 target = f;
7704             }
7705            
7706         });
7707         
7708         if(this.errorMask && !valid){
7709             Roo.bootstrap.Form.popover.mask(this, target);
7710         }
7711         
7712         return valid;
7713     },
7714     
7715     /**
7716      * Returns true if any fields in this form have changed since their original load.
7717      * @return Boolean
7718      */
7719     isDirty : function(){
7720         var dirty = false;
7721         var items = this.getItems();
7722         items.each(function(f){
7723            if(f.isDirty()){
7724                dirty = true;
7725                return false;
7726            }
7727            return true;
7728         });
7729         return dirty;
7730     },
7731      /**
7732      * Performs a predefined action (submit or load) or custom actions you define on this form.
7733      * @param {String} actionName The name of the action type
7734      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
7735      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
7736      * accept other config options):
7737      * <pre>
7738 Property          Type             Description
7739 ----------------  ---------------  ----------------------------------------------------------------------------------
7740 url               String           The url for the action (defaults to the form's url)
7741 method            String           The form method to use (defaults to the form's method, or POST if not defined)
7742 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
7743 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
7744                                    validate the form on the client (defaults to false)
7745      * </pre>
7746      * @return {BasicForm} this
7747      */
7748     doAction : function(action, options){
7749         if(typeof action == 'string'){
7750             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
7751         }
7752         if(this.fireEvent('beforeaction', this, action) !== false){
7753             this.beforeAction(action);
7754             action.run.defer(100, action);
7755         }
7756         return this;
7757     },
7758
7759     // private
7760     beforeAction : function(action){
7761         var o = action.options;
7762         
7763         if(this.loadMask){
7764             
7765             if(this.maskBody){
7766                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
7767             } else {
7768                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7769             }
7770         }
7771         // not really supported yet.. ??
7772
7773         //if(this.waitMsgTarget === true){
7774         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
7775         //}else if(this.waitMsgTarget){
7776         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
7777         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
7778         //}else {
7779         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
7780        // }
7781
7782     },
7783
7784     // private
7785     afterAction : function(action, success){
7786         this.activeAction = null;
7787         var o = action.options;
7788
7789         if(this.loadMask){
7790             
7791             if(this.maskBody){
7792                 Roo.get(document.body).unmask();
7793             } else {
7794                 this.el.unmask();
7795             }
7796         }
7797         
7798         //if(this.waitMsgTarget === true){
7799 //            this.el.unmask();
7800         //}else if(this.waitMsgTarget){
7801         //    this.waitMsgTarget.unmask();
7802         //}else{
7803         //    Roo.MessageBox.updateProgress(1);
7804         //    Roo.MessageBox.hide();
7805        // }
7806         //
7807         if(success){
7808             if(o.reset){
7809                 this.reset();
7810             }
7811             Roo.callback(o.success, o.scope, [this, action]);
7812             this.fireEvent('actioncomplete', this, action);
7813
7814         }else{
7815
7816             // failure condition..
7817             // we have a scenario where updates need confirming.
7818             // eg. if a locking scenario exists..
7819             // we look for { errors : { needs_confirm : true }} in the response.
7820             if (
7821                 (typeof(action.result) != 'undefined')  &&
7822                 (typeof(action.result.errors) != 'undefined')  &&
7823                 (typeof(action.result.errors.needs_confirm) != 'undefined')
7824            ){
7825                 var _t = this;
7826                 Roo.log("not supported yet");
7827                  /*
7828
7829                 Roo.MessageBox.confirm(
7830                     "Change requires confirmation",
7831                     action.result.errorMsg,
7832                     function(r) {
7833                         if (r != 'yes') {
7834                             return;
7835                         }
7836                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
7837                     }
7838
7839                 );
7840                 */
7841
7842
7843                 return;
7844             }
7845
7846             Roo.callback(o.failure, o.scope, [this, action]);
7847             // show an error message if no failed handler is set..
7848             if (!this.hasListener('actionfailed')) {
7849                 Roo.log("need to add dialog support");
7850                 /*
7851                 Roo.MessageBox.alert("Error",
7852                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
7853                         action.result.errorMsg :
7854                         "Saving Failed, please check your entries or try again"
7855                 );
7856                 */
7857             }
7858
7859             this.fireEvent('actionfailed', this, action);
7860         }
7861
7862     },
7863     /**
7864      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
7865      * @param {String} id The value to search for
7866      * @return Field
7867      */
7868     findField : function(id){
7869         var items = this.getItems();
7870         var field = items.get(id);
7871         if(!field){
7872              items.each(function(f){
7873                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
7874                     field = f;
7875                     return false;
7876                 }
7877                 return true;
7878             });
7879         }
7880         return field || null;
7881     },
7882      /**
7883      * Mark fields in this form invalid in bulk.
7884      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
7885      * @return {BasicForm} this
7886      */
7887     markInvalid : function(errors){
7888         if(errors instanceof Array){
7889             for(var i = 0, len = errors.length; i < len; i++){
7890                 var fieldError = errors[i];
7891                 var f = this.findField(fieldError.id);
7892                 if(f){
7893                     f.markInvalid(fieldError.msg);
7894                 }
7895             }
7896         }else{
7897             var field, id;
7898             for(id in errors){
7899                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
7900                     field.markInvalid(errors[id]);
7901                 }
7902             }
7903         }
7904         //Roo.each(this.childForms || [], function (f) {
7905         //    f.markInvalid(errors);
7906         //});
7907
7908         return this;
7909     },
7910
7911     /**
7912      * Set values for fields in this form in bulk.
7913      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
7914      * @return {BasicForm} this
7915      */
7916     setValues : function(values){
7917         if(values instanceof Array){ // array of objects
7918             for(var i = 0, len = values.length; i < len; i++){
7919                 var v = values[i];
7920                 var f = this.findField(v.id);
7921                 if(f){
7922                     f.setValue(v.value);
7923                     if(this.trackResetOnLoad){
7924                         f.originalValue = f.getValue();
7925                     }
7926                 }
7927             }
7928         }else{ // object hash
7929             var field, id;
7930             for(id in values){
7931                 if(typeof values[id] != 'function' && (field = this.findField(id))){
7932
7933                     if (field.setFromData &&
7934                         field.valueField &&
7935                         field.displayField &&
7936                         // combos' with local stores can
7937                         // be queried via setValue()
7938                         // to set their value..
7939                         (field.store && !field.store.isLocal)
7940                         ) {
7941                         // it's a combo
7942                         var sd = { };
7943                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
7944                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
7945                         field.setFromData(sd);
7946
7947                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
7948                         
7949                         field.setFromData(values);
7950                         
7951                     } else {
7952                         field.setValue(values[id]);
7953                     }
7954
7955
7956                     if(this.trackResetOnLoad){
7957                         field.originalValue = field.getValue();
7958                     }
7959                 }
7960             }
7961         }
7962
7963         //Roo.each(this.childForms || [], function (f) {
7964         //    f.setValues(values);
7965         //});
7966
7967         return this;
7968     },
7969
7970     /**
7971      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
7972      * they are returned as an array.
7973      * @param {Boolean} asString
7974      * @return {Object}
7975      */
7976     getValues : function(asString){
7977         //if (this.childForms) {
7978             // copy values from the child forms
7979         //    Roo.each(this.childForms, function (f) {
7980         //        this.setValues(f.getValues());
7981         //    }, this);
7982         //}
7983
7984
7985
7986         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
7987         if(asString === true){
7988             return fs;
7989         }
7990         return Roo.urlDecode(fs);
7991     },
7992
7993     /**
7994      * Returns the fields in this form as an object with key/value pairs.
7995      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
7996      * @return {Object}
7997      */
7998     getFieldValues : function(with_hidden)
7999     {
8000         var items = this.getItems();
8001         var ret = {};
8002         items.each(function(f){
8003             
8004             if (!f.getName()) {
8005                 return;
8006             }
8007             
8008             var v = f.getValue();
8009             
8010             if (f.inputType =='radio') {
8011                 if (typeof(ret[f.getName()]) == 'undefined') {
8012                     ret[f.getName()] = ''; // empty..
8013                 }
8014
8015                 if (!f.el.dom.checked) {
8016                     return;
8017
8018                 }
8019                 v = f.el.dom.value;
8020
8021             }
8022             
8023             if(f.xtype == 'MoneyField'){
8024                 ret[f.currencyName] = f.getCurrency();
8025             }
8026
8027             // not sure if this supported any more..
8028             if ((typeof(v) == 'object') && f.getRawValue) {
8029                 v = f.getRawValue() ; // dates..
8030             }
8031             // combo boxes where name != hiddenName...
8032             if (f.name !== false && f.name != '' && f.name != f.getName()) {
8033                 ret[f.name] = f.getRawValue();
8034             }
8035             ret[f.getName()] = v;
8036         });
8037
8038         return ret;
8039     },
8040
8041     /**
8042      * Clears all invalid messages in this form.
8043      * @return {BasicForm} this
8044      */
8045     clearInvalid : function(){
8046         var items = this.getItems();
8047
8048         items.each(function(f){
8049            f.clearInvalid();
8050         });
8051
8052
8053
8054         return this;
8055     },
8056
8057     /**
8058      * Resets this form.
8059      * @return {BasicForm} this
8060      */
8061     reset : function(){
8062         var items = this.getItems();
8063         items.each(function(f){
8064             f.reset();
8065         });
8066
8067         Roo.each(this.childForms || [], function (f) {
8068             f.reset();
8069         });
8070
8071
8072         return this;
8073     },
8074     
8075     getItems : function()
8076     {
8077         var r=new Roo.util.MixedCollection(false, function(o){
8078             return o.id || (o.id = Roo.id());
8079         });
8080         var iter = function(el) {
8081             if (el.inputEl) {
8082                 r.add(el);
8083             }
8084             if (!el.items) {
8085                 return;
8086             }
8087             Roo.each(el.items,function(e) {
8088                 iter(e);
8089             });
8090
8091
8092         };
8093
8094         iter(this);
8095         return r;
8096         
8097     }
8098
8099 });
8100
8101 Roo.apply(Roo.bootstrap.Form, {
8102     
8103     popover : {
8104         
8105         padding : 5,
8106         
8107         isApplied : false,
8108         
8109         isMasked : false,
8110         
8111         form : false,
8112         
8113         target : false,
8114         
8115         toolTip : false,
8116         
8117         intervalID : false,
8118         
8119         maskEl : false,
8120         
8121         apply : function()
8122         {
8123             if(this.isApplied){
8124                 return;
8125             }
8126             
8127             this.maskEl = {
8128                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
8129                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
8130                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
8131                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
8132             };
8133             
8134             this.maskEl.top.enableDisplayMode("block");
8135             this.maskEl.left.enableDisplayMode("block");
8136             this.maskEl.bottom.enableDisplayMode("block");
8137             this.maskEl.right.enableDisplayMode("block");
8138             
8139             this.toolTip = new Roo.bootstrap.Tooltip({
8140                 cls : 'roo-form-error-popover',
8141                 alignment : {
8142                     'left' : ['r-l', [-2,0], 'right'],
8143                     'right' : ['l-r', [2,0], 'left'],
8144                     'bottom' : ['tl-bl', [0,2], 'top'],
8145                     'top' : [ 'bl-tl', [0,-2], 'bottom']
8146                 }
8147             });
8148             
8149             this.toolTip.render(Roo.get(document.body));
8150
8151             this.toolTip.el.enableDisplayMode("block");
8152             
8153             Roo.get(document.body).on('click', function(){
8154                 this.unmask();
8155             }, this);
8156             
8157             Roo.get(document.body).on('touchstart', function(){
8158                 this.unmask();
8159             }, this);
8160             
8161             this.isApplied = true
8162         },
8163         
8164         mask : function(form, target)
8165         {
8166             this.form = form;
8167             
8168             this.target = target;
8169             
8170             if(!this.form.errorMask || !target.el){
8171                 return;
8172             }
8173             
8174             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
8175             
8176             Roo.log(scrollable);
8177             
8178             var ot = this.target.el.calcOffsetsTo(scrollable);
8179             
8180             var scrollTo = ot[1] - this.form.maskOffset;
8181             
8182             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
8183             
8184             scrollable.scrollTo('top', scrollTo);
8185             
8186             var box = this.target.el.getBox();
8187             Roo.log(box);
8188             var zIndex = Roo.bootstrap.Modal.zIndex++;
8189
8190             
8191             this.maskEl.top.setStyle('position', 'absolute');
8192             this.maskEl.top.setStyle('z-index', zIndex);
8193             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
8194             this.maskEl.top.setLeft(0);
8195             this.maskEl.top.setTop(0);
8196             this.maskEl.top.show();
8197             
8198             this.maskEl.left.setStyle('position', 'absolute');
8199             this.maskEl.left.setStyle('z-index', zIndex);
8200             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
8201             this.maskEl.left.setLeft(0);
8202             this.maskEl.left.setTop(box.y - this.padding);
8203             this.maskEl.left.show();
8204
8205             this.maskEl.bottom.setStyle('position', 'absolute');
8206             this.maskEl.bottom.setStyle('z-index', zIndex);
8207             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
8208             this.maskEl.bottom.setLeft(0);
8209             this.maskEl.bottom.setTop(box.bottom + this.padding);
8210             this.maskEl.bottom.show();
8211
8212             this.maskEl.right.setStyle('position', 'absolute');
8213             this.maskEl.right.setStyle('z-index', zIndex);
8214             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
8215             this.maskEl.right.setLeft(box.right + this.padding);
8216             this.maskEl.right.setTop(box.y - this.padding);
8217             this.maskEl.right.show();
8218
8219             this.toolTip.bindEl = this.target.el;
8220
8221             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
8222
8223             var tip = this.target.blankText;
8224
8225             if(this.target.getValue() !== '' ) {
8226                 
8227                 if (this.target.invalidText.length) {
8228                     tip = this.target.invalidText;
8229                 } else if (this.target.regexText.length){
8230                     tip = this.target.regexText;
8231                 }
8232             }
8233
8234             this.toolTip.show(tip);
8235
8236             this.intervalID = window.setInterval(function() {
8237                 Roo.bootstrap.Form.popover.unmask();
8238             }, 10000);
8239
8240             window.onwheel = function(){ return false;};
8241             
8242             (function(){ this.isMasked = true; }).defer(500, this);
8243             
8244         },
8245         
8246         unmask : function()
8247         {
8248             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
8249                 return;
8250             }
8251             
8252             this.maskEl.top.setStyle('position', 'absolute');
8253             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
8254             this.maskEl.top.hide();
8255
8256             this.maskEl.left.setStyle('position', 'absolute');
8257             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
8258             this.maskEl.left.hide();
8259
8260             this.maskEl.bottom.setStyle('position', 'absolute');
8261             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
8262             this.maskEl.bottom.hide();
8263
8264             this.maskEl.right.setStyle('position', 'absolute');
8265             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
8266             this.maskEl.right.hide();
8267             
8268             this.toolTip.hide();
8269             
8270             this.toolTip.el.hide();
8271             
8272             window.onwheel = function(){ return true;};
8273             
8274             if(this.intervalID){
8275                 window.clearInterval(this.intervalID);
8276                 this.intervalID = false;
8277             }
8278             
8279             this.isMasked = false;
8280             
8281         }
8282         
8283     }
8284     
8285 });
8286
8287 /*
8288  * Based on:
8289  * Ext JS Library 1.1.1
8290  * Copyright(c) 2006-2007, Ext JS, LLC.
8291  *
8292  * Originally Released Under LGPL - original licence link has changed is not relivant.
8293  *
8294  * Fork - LGPL
8295  * <script type="text/javascript">
8296  */
8297 /**
8298  * @class Roo.form.VTypes
8299  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
8300  * @singleton
8301  */
8302 Roo.form.VTypes = function(){
8303     // closure these in so they are only created once.
8304     var alpha = /^[a-zA-Z_]+$/;
8305     var alphanum = /^[a-zA-Z0-9_]+$/;
8306     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
8307     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
8308
8309     // All these messages and functions are configurable
8310     return {
8311         /**
8312          * The function used to validate email addresses
8313          * @param {String} value The email address
8314          */
8315         'email' : function(v){
8316             return email.test(v);
8317         },
8318         /**
8319          * The error text to display when the email validation function returns false
8320          * @type String
8321          */
8322         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
8323         /**
8324          * The keystroke filter mask to be applied on email input
8325          * @type RegExp
8326          */
8327         'emailMask' : /[a-z0-9_\.\-@]/i,
8328
8329         /**
8330          * The function used to validate URLs
8331          * @param {String} value The URL
8332          */
8333         'url' : function(v){
8334             return url.test(v);
8335         },
8336         /**
8337          * The error text to display when the url validation function returns false
8338          * @type String
8339          */
8340         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
8341         
8342         /**
8343          * The function used to validate alpha values
8344          * @param {String} value The value
8345          */
8346         'alpha' : function(v){
8347             return alpha.test(v);
8348         },
8349         /**
8350          * The error text to display when the alpha validation function returns false
8351          * @type String
8352          */
8353         'alphaText' : 'This field should only contain letters and _',
8354         /**
8355          * The keystroke filter mask to be applied on alpha input
8356          * @type RegExp
8357          */
8358         'alphaMask' : /[a-z_]/i,
8359
8360         /**
8361          * The function used to validate alphanumeric values
8362          * @param {String} value The value
8363          */
8364         'alphanum' : function(v){
8365             return alphanum.test(v);
8366         },
8367         /**
8368          * The error text to display when the alphanumeric validation function returns false
8369          * @type String
8370          */
8371         'alphanumText' : 'This field should only contain letters, numbers and _',
8372         /**
8373          * The keystroke filter mask to be applied on alphanumeric input
8374          * @type RegExp
8375          */
8376         'alphanumMask' : /[a-z0-9_]/i
8377     };
8378 }();/*
8379  * - LGPL
8380  *
8381  * Input
8382  * 
8383  */
8384
8385 /**
8386  * @class Roo.bootstrap.Input
8387  * @extends Roo.bootstrap.Component
8388  * Bootstrap Input class
8389  * @cfg {Boolean} disabled is it disabled
8390  * @cfg {String} inputType button | checkbox | email | file | hidden | image | number | password | radio | range | reset | search | submit | text
8391  * @cfg {String} name name of the input
8392  * @cfg {string} fieldLabel - the label associated
8393  * @cfg {string} placeholder - placeholder to put in text.
8394  * @cfg {string}  before - input group add on before
8395  * @cfg {string} after - input group add on after
8396  * @cfg {string} size - (lg|sm) or leave empty..
8397  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
8398  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
8399  * @cfg {Number} md colspan out of 12 for computer-sized screens
8400  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
8401  * @cfg {string} value default value of the input
8402  * @cfg {Number} labelWidth set the width of label 
8403  * @cfg {Number} labellg set the width of label (1-12)
8404  * @cfg {Number} labelmd set the width of label (1-12)
8405  * @cfg {Number} labelsm set the width of label (1-12)
8406  * @cfg {Number} labelxs set the width of label (1-12)
8407  * @cfg {String} labelAlign (top|left)
8408  * @cfg {Boolean} readOnly Specifies that the field should be read-only
8409  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
8410  * @cfg {String} indicatorpos (left|right) default left
8411
8412  * @cfg {String} align (left|center|right) Default left
8413  * @cfg {Boolean} forceFeedback (true|false) Default false
8414  * 
8415  * 
8416  * 
8417  * 
8418  * @constructor
8419  * Create a new Input
8420  * @param {Object} config The config object
8421  */
8422
8423 Roo.bootstrap.Input = function(config){
8424     
8425     Roo.bootstrap.Input.superclass.constructor.call(this, config);
8426     
8427     this.addEvents({
8428         /**
8429          * @event focus
8430          * Fires when this field receives input focus.
8431          * @param {Roo.form.Field} this
8432          */
8433         focus : true,
8434         /**
8435          * @event blur
8436          * Fires when this field loses input focus.
8437          * @param {Roo.form.Field} this
8438          */
8439         blur : true,
8440         /**
8441          * @event specialkey
8442          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
8443          * {@link Roo.EventObject#getKey} to determine which key was pressed.
8444          * @param {Roo.form.Field} this
8445          * @param {Roo.EventObject} e The event object
8446          */
8447         specialkey : true,
8448         /**
8449          * @event change
8450          * Fires just before the field blurs if the field value has changed.
8451          * @param {Roo.form.Field} this
8452          * @param {Mixed} newValue The new value
8453          * @param {Mixed} oldValue The original value
8454          */
8455         change : true,
8456         /**
8457          * @event invalid
8458          * Fires after the field has been marked as invalid.
8459          * @param {Roo.form.Field} this
8460          * @param {String} msg The validation message
8461          */
8462         invalid : true,
8463         /**
8464          * @event valid
8465          * Fires after the field has been validated with no errors.
8466          * @param {Roo.form.Field} this
8467          */
8468         valid : true,
8469          /**
8470          * @event keyup
8471          * Fires after the key up
8472          * @param {Roo.form.Field} this
8473          * @param {Roo.EventObject}  e The event Object
8474          */
8475         keyup : true
8476     });
8477 };
8478
8479 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
8480      /**
8481      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
8482       automatic validation (defaults to "keyup").
8483      */
8484     validationEvent : "keyup",
8485      /**
8486      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
8487      */
8488     validateOnBlur : true,
8489     /**
8490      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
8491      */
8492     validationDelay : 250,
8493      /**
8494      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
8495      */
8496     focusClass : "x-form-focus",  // not needed???
8497     
8498        
8499     /**
8500      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
8501      */
8502     invalidClass : "has-warning",
8503     
8504     /**
8505      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
8506      */
8507     validClass : "has-success",
8508     
8509     /**
8510      * @cfg {Boolean} hasFeedback (true|false) default true
8511      */
8512     hasFeedback : true,
8513     
8514     /**
8515      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8516      */
8517     invalidFeedbackClass : "glyphicon-warning-sign",
8518     
8519     /**
8520      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
8521      */
8522     validFeedbackClass : "glyphicon-ok",
8523     
8524     /**
8525      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
8526      */
8527     selectOnFocus : false,
8528     
8529      /**
8530      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
8531      */
8532     maskRe : null,
8533        /**
8534      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
8535      */
8536     vtype : null,
8537     
8538       /**
8539      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
8540      */
8541     disableKeyFilter : false,
8542     
8543        /**
8544      * @cfg {Boolean} disabled True to disable the field (defaults to false).
8545      */
8546     disabled : false,
8547      /**
8548      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
8549      */
8550     allowBlank : true,
8551     /**
8552      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
8553      */
8554     blankText : "Please complete this mandatory field",
8555     
8556      /**
8557      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
8558      */
8559     minLength : 0,
8560     /**
8561      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
8562      */
8563     maxLength : Number.MAX_VALUE,
8564     /**
8565      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
8566      */
8567     minLengthText : "The minimum length for this field is {0}",
8568     /**
8569      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
8570      */
8571     maxLengthText : "The maximum length for this field is {0}",
8572   
8573     
8574     /**
8575      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
8576      * If available, this function will be called only after the basic validators all return true, and will be passed the
8577      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
8578      */
8579     validator : null,
8580     /**
8581      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
8582      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
8583      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
8584      */
8585     regex : null,
8586     /**
8587      * @cfg {String} regexText -- Depricated - use Invalid Text
8588      */
8589     regexText : "",
8590     
8591     /**
8592      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
8593      */
8594     invalidText : "",
8595     
8596     
8597     
8598     autocomplete: false,
8599     
8600     
8601     fieldLabel : '',
8602     inputType : 'text',
8603     
8604     name : false,
8605     placeholder: false,
8606     before : false,
8607     after : false,
8608     size : false,
8609     hasFocus : false,
8610     preventMark: false,
8611     isFormField : true,
8612     value : '',
8613     labelWidth : 2,
8614     labelAlign : false,
8615     readOnly : false,
8616     align : false,
8617     formatedValue : false,
8618     forceFeedback : false,
8619     
8620     indicatorpos : 'left',
8621     
8622     labellg : 0,
8623     labelmd : 0,
8624     labelsm : 0,
8625     labelxs : 0,
8626     
8627     parentLabelAlign : function()
8628     {
8629         var parent = this;
8630         while (parent.parent()) {
8631             parent = parent.parent();
8632             if (typeof(parent.labelAlign) !='undefined') {
8633                 return parent.labelAlign;
8634             }
8635         }
8636         return 'left';
8637         
8638     },
8639     
8640     getAutoCreate : function()
8641     {
8642         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
8643         
8644         var id = Roo.id();
8645         
8646         var cfg = {};
8647         
8648         if(this.inputType != 'hidden'){
8649             cfg.cls = 'form-group' //input-group
8650         }
8651         
8652         var input =  {
8653             tag: 'input',
8654             id : id,
8655             type : this.inputType,
8656             value : this.value,
8657             cls : 'form-control',
8658             placeholder : this.placeholder || '',
8659             autocomplete : this.autocomplete || 'new-password'
8660         };
8661         
8662         if(this.align){
8663             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
8664         }
8665         
8666         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
8667             input.maxLength = this.maxLength;
8668         }
8669         
8670         if (this.disabled) {
8671             input.disabled=true;
8672         }
8673         
8674         if (this.readOnly) {
8675             input.readonly=true;
8676         }
8677         
8678         if (this.name) {
8679             input.name = this.name;
8680         }
8681         
8682         if (this.size) {
8683             input.cls += ' input-' + this.size;
8684         }
8685         
8686         var settings=this;
8687         ['xs','sm','md','lg'].map(function(size){
8688             if (settings[size]) {
8689                 cfg.cls += ' col-' + size + '-' + settings[size];
8690             }
8691         });
8692         
8693         var inputblock = input;
8694         
8695         var feedback = {
8696             tag: 'span',
8697             cls: 'glyphicon form-control-feedback'
8698         };
8699             
8700         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8701             
8702             inputblock = {
8703                 cls : 'has-feedback',
8704                 cn :  [
8705                     input,
8706                     feedback
8707                 ] 
8708             };  
8709         }
8710         
8711         if (this.before || this.after) {
8712             
8713             inputblock = {
8714                 cls : 'input-group',
8715                 cn :  [] 
8716             };
8717             
8718             if (this.before && typeof(this.before) == 'string') {
8719                 
8720                 inputblock.cn.push({
8721                     tag :'span',
8722                     cls : 'roo-input-before input-group-addon',
8723                     html : this.before
8724                 });
8725             }
8726             if (this.before && typeof(this.before) == 'object') {
8727                 this.before = Roo.factory(this.before);
8728                 
8729                 inputblock.cn.push({
8730                     tag :'span',
8731                     cls : 'roo-input-before input-group-' +
8732                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8733                 });
8734             }
8735             
8736             inputblock.cn.push(input);
8737             
8738             if (this.after && typeof(this.after) == 'string') {
8739                 inputblock.cn.push({
8740                     tag :'span',
8741                     cls : 'roo-input-after input-group-addon',
8742                     html : this.after
8743                 });
8744             }
8745             if (this.after && typeof(this.after) == 'object') {
8746                 this.after = Roo.factory(this.after);
8747                 
8748                 inputblock.cn.push({
8749                     tag :'span',
8750                     cls : 'roo-input-after input-group-' +
8751                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
8752                 });
8753             }
8754             
8755             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
8756                 inputblock.cls += ' has-feedback';
8757                 inputblock.cn.push(feedback);
8758             }
8759         };
8760         
8761         if (align ==='left' && this.fieldLabel.length) {
8762             
8763             cfg.cls += ' roo-form-group-label-left';
8764             
8765             cfg.cn = [
8766                 {
8767                     tag : 'i',
8768                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8769                     tooltip : 'This field is required'
8770                 },
8771                 {
8772                     tag: 'label',
8773                     'for' :  id,
8774                     cls : 'control-label',
8775                     html : this.fieldLabel
8776
8777                 },
8778                 {
8779                     cls : "", 
8780                     cn: [
8781                         inputblock
8782                     ]
8783                 }
8784             ];
8785             
8786             var labelCfg = cfg.cn[1];
8787             var contentCfg = cfg.cn[2];
8788             
8789             if(this.indicatorpos == 'right'){
8790                 cfg.cn = [
8791                     {
8792                         tag: 'label',
8793                         'for' :  id,
8794                         cls : 'control-label',
8795                         cn : [
8796                             {
8797                                 tag : 'span',
8798                                 html : this.fieldLabel
8799                             },
8800                             {
8801                                 tag : 'i',
8802                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8803                                 tooltip : 'This field is required'
8804                             }
8805                         ]
8806                     },
8807                     {
8808                         cls : "",
8809                         cn: [
8810                             inputblock
8811                         ]
8812                     }
8813
8814                 ];
8815                 
8816                 labelCfg = cfg.cn[0];
8817                 contentCfg = cfg.cn[1];
8818             
8819             }
8820             
8821             if(this.labelWidth > 12){
8822                 labelCfg.style = "width: " + this.labelWidth + 'px';
8823             }
8824             
8825             if(this.labelWidth < 13 && this.labelmd == 0){
8826                 this.labelmd = this.labelWidth;
8827             }
8828             
8829             if(this.labellg > 0){
8830                 labelCfg.cls += ' col-lg-' + this.labellg;
8831                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
8832             }
8833             
8834             if(this.labelmd > 0){
8835                 labelCfg.cls += ' col-md-' + this.labelmd;
8836                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
8837             }
8838             
8839             if(this.labelsm > 0){
8840                 labelCfg.cls += ' col-sm-' + this.labelsm;
8841                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
8842             }
8843             
8844             if(this.labelxs > 0){
8845                 labelCfg.cls += ' col-xs-' + this.labelxs;
8846                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
8847             }
8848             
8849             
8850         } else if ( this.fieldLabel.length) {
8851                 
8852             cfg.cn = [
8853                 {
8854                     tag : 'i',
8855                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
8856                     tooltip : 'This field is required'
8857                 },
8858                 {
8859                     tag: 'label',
8860                    //cls : 'input-group-addon',
8861                     html : this.fieldLabel
8862
8863                 },
8864
8865                inputblock
8866
8867            ];
8868            
8869            if(this.indicatorpos == 'right'){
8870                 
8871                 cfg.cn = [
8872                     {
8873                         tag: 'label',
8874                        //cls : 'input-group-addon',
8875                         html : this.fieldLabel
8876
8877                     },
8878                     {
8879                         tag : 'i',
8880                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
8881                         tooltip : 'This field is required'
8882                     },
8883
8884                    inputblock
8885
8886                ];
8887
8888             }
8889
8890         } else {
8891             
8892             cfg.cn = [
8893
8894                     inputblock
8895
8896             ];
8897                 
8898                 
8899         };
8900         
8901         if (this.parentType === 'Navbar' &&  this.parent().bar) {
8902            cfg.cls += ' navbar-form';
8903         }
8904         
8905         if (this.parentType === 'NavGroup') {
8906            cfg.cls += ' navbar-form';
8907            cfg.tag = 'li';
8908         }
8909         
8910         return cfg;
8911         
8912     },
8913     /**
8914      * return the real input element.
8915      */
8916     inputEl: function ()
8917     {
8918         return this.el.select('input.form-control',true).first();
8919     },
8920     
8921     tooltipEl : function()
8922     {
8923         return this.inputEl();
8924     },
8925     
8926     indicatorEl : function()
8927     {
8928         var indicator = this.el.select('i.roo-required-indicator',true).first();
8929         
8930         if(!indicator){
8931             return false;
8932         }
8933         
8934         return indicator;
8935         
8936     },
8937     
8938     setDisabled : function(v)
8939     {
8940         var i  = this.inputEl().dom;
8941         if (!v) {
8942             i.removeAttribute('disabled');
8943             return;
8944             
8945         }
8946         i.setAttribute('disabled','true');
8947     },
8948     initEvents : function()
8949     {
8950           
8951         this.inputEl().on("keydown" , this.fireKey,  this);
8952         this.inputEl().on("focus", this.onFocus,  this);
8953         this.inputEl().on("blur", this.onBlur,  this);
8954         
8955         this.inputEl().relayEvent('keyup', this);
8956         
8957         this.indicator = this.indicatorEl();
8958         
8959         if(this.indicator){
8960             this.indicator.addClass('invisible');
8961             
8962         }
8963  
8964         // reference to original value for reset
8965         this.originalValue = this.getValue();
8966         //Roo.form.TextField.superclass.initEvents.call(this);
8967         if(this.validationEvent == 'keyup'){
8968             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
8969             this.inputEl().on('keyup', this.filterValidation, this);
8970         }
8971         else if(this.validationEvent !== false){
8972             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
8973         }
8974         
8975         if(this.selectOnFocus){
8976             this.on("focus", this.preFocus, this);
8977             
8978         }
8979         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
8980             this.inputEl().on("keypress", this.filterKeys, this);
8981         } else {
8982             this.inputEl().relayEvent('keypress', this);
8983         }
8984        /* if(this.grow){
8985             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
8986             this.el.on("click", this.autoSize,  this);
8987         }
8988         */
8989         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
8990             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
8991         }
8992         
8993         if (typeof(this.before) == 'object') {
8994             this.before.render(this.el.select('.roo-input-before',true).first());
8995         }
8996         if (typeof(this.after) == 'object') {
8997             this.after.render(this.el.select('.roo-input-after',true).first());
8998         }
8999         
9000         
9001     },
9002     filterValidation : function(e){
9003         if(!e.isNavKeyPress()){
9004             this.validationTask.delay(this.validationDelay);
9005         }
9006     },
9007      /**
9008      * Validates the field value
9009      * @return {Boolean} True if the value is valid, else false
9010      */
9011     validate : function(){
9012         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
9013         if(this.disabled || this.validateValue(this.getRawValue())){
9014             this.markValid();
9015             return true;
9016         }
9017         
9018         this.markInvalid();
9019         return false;
9020     },
9021     
9022     
9023     /**
9024      * Validates a value according to the field's validation rules and marks the field as invalid
9025      * if the validation fails
9026      * @param {Mixed} value The value to validate
9027      * @return {Boolean} True if the value is valid, else false
9028      */
9029     validateValue : function(value){
9030         if(value.length < 1)  { // if it's blank
9031             if(this.allowBlank){
9032                 return true;
9033             }            
9034             return this.inputEl().hasClass('hide') ? true : false;
9035         }
9036         
9037         if(value.length < this.minLength){
9038             return false;
9039         }
9040         if(value.length > this.maxLength){
9041             return false;
9042         }
9043         if(this.vtype){
9044             var vt = Roo.form.VTypes;
9045             if(!vt[this.vtype](value, this)){
9046                 return false;
9047             }
9048         }
9049         if(typeof this.validator == "function"){
9050             var msg = this.validator(value);
9051             if(msg !== true){
9052                 return false;
9053             }
9054             if (typeof(msg) == 'string') {
9055                 this.invalidText = msg;
9056             }
9057         }
9058         
9059         if(this.regex && !this.regex.test(value)){
9060             return false;
9061         }
9062         
9063         return true;
9064     },
9065
9066     
9067     
9068      // private
9069     fireKey : function(e){
9070         //Roo.log('field ' + e.getKey());
9071         if(e.isNavKeyPress()){
9072             this.fireEvent("specialkey", this, e);
9073         }
9074     },
9075     focus : function (selectText){
9076         if(this.rendered){
9077             this.inputEl().focus();
9078             if(selectText === true){
9079                 this.inputEl().dom.select();
9080             }
9081         }
9082         return this;
9083     } ,
9084     
9085     onFocus : function(){
9086         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9087            // this.el.addClass(this.focusClass);
9088         }
9089         if(!this.hasFocus){
9090             this.hasFocus = true;
9091             this.startValue = this.getValue();
9092             this.fireEvent("focus", this);
9093         }
9094     },
9095     
9096     beforeBlur : Roo.emptyFn,
9097
9098     
9099     // private
9100     onBlur : function(){
9101         this.beforeBlur();
9102         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
9103             //this.el.removeClass(this.focusClass);
9104         }
9105         this.hasFocus = false;
9106         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
9107             this.validate();
9108         }
9109         var v = this.getValue();
9110         if(String(v) !== String(this.startValue)){
9111             this.fireEvent('change', this, v, this.startValue);
9112         }
9113         this.fireEvent("blur", this);
9114     },
9115     
9116     /**
9117      * Resets the current field value to the originally loaded value and clears any validation messages
9118      */
9119     reset : function(){
9120         this.setValue(this.originalValue);
9121         this.validate();
9122     },
9123      /**
9124      * Returns the name of the field
9125      * @return {Mixed} name The name field
9126      */
9127     getName: function(){
9128         return this.name;
9129     },
9130      /**
9131      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
9132      * @return {Mixed} value The field value
9133      */
9134     getValue : function(){
9135         
9136         var v = this.inputEl().getValue();
9137         
9138         return v;
9139     },
9140     /**
9141      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
9142      * @return {Mixed} value The field value
9143      */
9144     getRawValue : function(){
9145         var v = this.inputEl().getValue();
9146         
9147         return v;
9148     },
9149     
9150     /**
9151      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
9152      * @param {Mixed} value The value to set
9153      */
9154     setRawValue : function(v){
9155         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9156     },
9157     
9158     selectText : function(start, end){
9159         var v = this.getRawValue();
9160         if(v.length > 0){
9161             start = start === undefined ? 0 : start;
9162             end = end === undefined ? v.length : end;
9163             var d = this.inputEl().dom;
9164             if(d.setSelectionRange){
9165                 d.setSelectionRange(start, end);
9166             }else if(d.createTextRange){
9167                 var range = d.createTextRange();
9168                 range.moveStart("character", start);
9169                 range.moveEnd("character", v.length-end);
9170                 range.select();
9171             }
9172         }
9173     },
9174     
9175     /**
9176      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
9177      * @param {Mixed} value The value to set
9178      */
9179     setValue : function(v){
9180         this.value = v;
9181         if(this.rendered){
9182             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
9183             this.validate();
9184         }
9185     },
9186     
9187     /*
9188     processValue : function(value){
9189         if(this.stripCharsRe){
9190             var newValue = value.replace(this.stripCharsRe, '');
9191             if(newValue !== value){
9192                 this.setRawValue(newValue);
9193                 return newValue;
9194             }
9195         }
9196         return value;
9197     },
9198   */
9199     preFocus : function(){
9200         
9201         if(this.selectOnFocus){
9202             this.inputEl().dom.select();
9203         }
9204     },
9205     filterKeys : function(e){
9206         var k = e.getKey();
9207         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
9208             return;
9209         }
9210         var c = e.getCharCode(), cc = String.fromCharCode(c);
9211         if(Roo.isIE && (e.isSpecialKey() || !cc)){
9212             return;
9213         }
9214         if(!this.maskRe.test(cc)){
9215             e.stopEvent();
9216         }
9217     },
9218      /**
9219      * Clear any invalid styles/messages for this field
9220      */
9221     clearInvalid : function(){
9222         
9223         if(!this.el || this.preventMark){ // not rendered
9224             return;
9225         }
9226         
9227      
9228         this.el.removeClass(this.invalidClass);
9229         
9230         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9231             
9232             var feedback = this.el.select('.form-control-feedback', true).first();
9233             
9234             if(feedback){
9235                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9236             }
9237             
9238         }
9239         
9240         this.fireEvent('valid', this);
9241     },
9242     
9243      /**
9244      * Mark this field as valid
9245      */
9246     markValid : function()
9247     {
9248         if(!this.el  || this.preventMark){ // not rendered...
9249             return;
9250         }
9251         
9252         this.el.removeClass([this.invalidClass, this.validClass]);
9253         
9254         var feedback = this.el.select('.form-control-feedback', true).first();
9255             
9256         if(feedback){
9257             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9258         }
9259
9260         if(this.disabled){
9261             return;
9262         }
9263         
9264         if(this.allowBlank && !this.getRawValue().length){
9265             return;
9266         }
9267         
9268         if(this.indicator){
9269             this.indicator.removeClass('visible');
9270             this.indicator.addClass('invisible');
9271         }
9272         
9273         this.el.addClass(this.validClass);
9274         
9275         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9276             
9277             var feedback = this.el.select('.form-control-feedback', true).first();
9278             
9279             if(feedback){
9280                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9281                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9282             }
9283             
9284         }
9285         
9286         this.fireEvent('valid', this);
9287     },
9288     
9289      /**
9290      * Mark this field as invalid
9291      * @param {String} msg The validation message
9292      */
9293     markInvalid : function(msg)
9294     {
9295         if(!this.el  || this.preventMark){ // not rendered
9296             return;
9297         }
9298         
9299         this.el.removeClass([this.invalidClass, this.validClass]);
9300         
9301         var feedback = this.el.select('.form-control-feedback', true).first();
9302             
9303         if(feedback){
9304             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9305         }
9306
9307         if(this.disabled){
9308             return;
9309         }
9310         
9311         if(this.allowBlank && !this.getRawValue().length){
9312             return;
9313         }
9314         
9315         if(this.indicator){
9316             this.indicator.removeClass('invisible');
9317             this.indicator.addClass('visible');
9318         }
9319         
9320         this.el.addClass(this.invalidClass);
9321         
9322         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9323             
9324             var feedback = this.el.select('.form-control-feedback', true).first();
9325             
9326             if(feedback){
9327                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9328                 
9329                 if(this.getValue().length || this.forceFeedback){
9330                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9331                 }
9332                 
9333             }
9334             
9335         }
9336         
9337         this.fireEvent('invalid', this, msg);
9338     },
9339     // private
9340     SafariOnKeyDown : function(event)
9341     {
9342         // this is a workaround for a password hang bug on chrome/ webkit.
9343         if (this.inputEl().dom.type != 'password') {
9344             return;
9345         }
9346         
9347         var isSelectAll = false;
9348         
9349         if(this.inputEl().dom.selectionEnd > 0){
9350             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
9351         }
9352         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
9353             event.preventDefault();
9354             this.setValue('');
9355             return;
9356         }
9357         
9358         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
9359             
9360             event.preventDefault();
9361             // this is very hacky as keydown always get's upper case.
9362             //
9363             var cc = String.fromCharCode(event.getCharCode());
9364             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
9365             
9366         }
9367     },
9368     adjustWidth : function(tag, w){
9369         tag = tag.toLowerCase();
9370         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
9371             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
9372                 if(tag == 'input'){
9373                     return w + 2;
9374                 }
9375                 if(tag == 'textarea'){
9376                     return w-2;
9377                 }
9378             }else if(Roo.isOpera){
9379                 if(tag == 'input'){
9380                     return w + 2;
9381                 }
9382                 if(tag == 'textarea'){
9383                     return w-2;
9384                 }
9385             }
9386         }
9387         return w;
9388     },
9389     
9390     setFieldLabel : function(v)
9391     {
9392         if(!this.rendered){
9393             return;
9394         }
9395         
9396         if(this.indicator){
9397             var ar = this.el.select('label > span',true);
9398             
9399             if (ar.elements.length) {
9400                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9401                 this.fieldLabel = v;
9402                 return;
9403             }
9404             
9405             var br = this.el.select('label',true);
9406             
9407             if(br.elements.length) {
9408                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9409                 this.fieldLabel = v;
9410                 return;
9411             }
9412             
9413             Roo.log('Cannot Found any of label > span || label in input');
9414             return;
9415         }
9416         
9417         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
9418         this.fieldLabel = v;
9419         
9420         
9421     }
9422 });
9423
9424  
9425 /*
9426  * - LGPL
9427  *
9428  * Input
9429  * 
9430  */
9431
9432 /**
9433  * @class Roo.bootstrap.TextArea
9434  * @extends Roo.bootstrap.Input
9435  * Bootstrap TextArea class
9436  * @cfg {Number} cols Specifies the visible width of a text area
9437  * @cfg {Number} rows Specifies the visible number of lines in a text area
9438  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
9439  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
9440  * @cfg {string} html text
9441  * 
9442  * @constructor
9443  * Create a new TextArea
9444  * @param {Object} config The config object
9445  */
9446
9447 Roo.bootstrap.TextArea = function(config){
9448     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
9449    
9450 };
9451
9452 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
9453      
9454     cols : false,
9455     rows : 5,
9456     readOnly : false,
9457     warp : 'soft',
9458     resize : false,
9459     value: false,
9460     html: false,
9461     
9462     getAutoCreate : function(){
9463         
9464         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
9465         
9466         var id = Roo.id();
9467         
9468         var cfg = {};
9469         
9470         if(this.inputType != 'hidden'){
9471             cfg.cls = 'form-group' //input-group
9472         }
9473         
9474         var input =  {
9475             tag: 'textarea',
9476             id : id,
9477             warp : this.warp,
9478             rows : this.rows,
9479             value : this.value || '',
9480             html: this.html || '',
9481             cls : 'form-control',
9482             placeholder : this.placeholder || '' 
9483             
9484         };
9485         
9486         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
9487             input.maxLength = this.maxLength;
9488         }
9489         
9490         if(this.resize){
9491             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
9492         }
9493         
9494         if(this.cols){
9495             input.cols = this.cols;
9496         }
9497         
9498         if (this.readOnly) {
9499             input.readonly = true;
9500         }
9501         
9502         if (this.name) {
9503             input.name = this.name;
9504         }
9505         
9506         if (this.size) {
9507             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
9508         }
9509         
9510         var settings=this;
9511         ['xs','sm','md','lg'].map(function(size){
9512             if (settings[size]) {
9513                 cfg.cls += ' col-' + size + '-' + settings[size];
9514             }
9515         });
9516         
9517         var inputblock = input;
9518         
9519         if(this.hasFeedback && !this.allowBlank){
9520             
9521             var feedback = {
9522                 tag: 'span',
9523                 cls: 'glyphicon form-control-feedback'
9524             };
9525
9526             inputblock = {
9527                 cls : 'has-feedback',
9528                 cn :  [
9529                     input,
9530                     feedback
9531                 ] 
9532             };  
9533         }
9534         
9535         
9536         if (this.before || this.after) {
9537             
9538             inputblock = {
9539                 cls : 'input-group',
9540                 cn :  [] 
9541             };
9542             if (this.before) {
9543                 inputblock.cn.push({
9544                     tag :'span',
9545                     cls : 'input-group-addon',
9546                     html : this.before
9547                 });
9548             }
9549             
9550             inputblock.cn.push(input);
9551             
9552             if(this.hasFeedback && !this.allowBlank){
9553                 inputblock.cls += ' has-feedback';
9554                 inputblock.cn.push(feedback);
9555             }
9556             
9557             if (this.after) {
9558                 inputblock.cn.push({
9559                     tag :'span',
9560                     cls : 'input-group-addon',
9561                     html : this.after
9562                 });
9563             }
9564             
9565         }
9566         
9567         if (align ==='left' && this.fieldLabel.length) {
9568             cfg.cn = [
9569                 {
9570                     tag: 'label',
9571                     'for' :  id,
9572                     cls : 'control-label',
9573                     html : this.fieldLabel
9574                 },
9575                 {
9576                     cls : "",
9577                     cn: [
9578                         inputblock
9579                     ]
9580                 }
9581
9582             ];
9583             
9584             if(this.labelWidth > 12){
9585                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
9586             }
9587
9588             if(this.labelWidth < 13 && this.labelmd == 0){
9589                 this.labelmd = this.labelWidth;
9590             }
9591
9592             if(this.labellg > 0){
9593                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
9594                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
9595             }
9596
9597             if(this.labelmd > 0){
9598                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
9599                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
9600             }
9601
9602             if(this.labelsm > 0){
9603                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
9604                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
9605             }
9606
9607             if(this.labelxs > 0){
9608                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
9609                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
9610             }
9611             
9612         } else if ( this.fieldLabel.length) {
9613             cfg.cn = [
9614
9615                {
9616                    tag: 'label',
9617                    //cls : 'input-group-addon',
9618                    html : this.fieldLabel
9619
9620                },
9621
9622                inputblock
9623
9624            ];
9625
9626         } else {
9627
9628             cfg.cn = [
9629
9630                 inputblock
9631
9632             ];
9633                 
9634         }
9635         
9636         if (this.disabled) {
9637             input.disabled=true;
9638         }
9639         
9640         return cfg;
9641         
9642     },
9643     /**
9644      * return the real textarea element.
9645      */
9646     inputEl: function ()
9647     {
9648         return this.el.select('textarea.form-control',true).first();
9649     },
9650     
9651     /**
9652      * Clear any invalid styles/messages for this field
9653      */
9654     clearInvalid : function()
9655     {
9656         
9657         if(!this.el || this.preventMark){ // not rendered
9658             return;
9659         }
9660         
9661         var label = this.el.select('label', true).first();
9662         var icon = this.el.select('i.fa-star', true).first();
9663         
9664         if(label && icon){
9665             icon.remove();
9666         }
9667         
9668         this.el.removeClass(this.invalidClass);
9669         
9670         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9671             
9672             var feedback = this.el.select('.form-control-feedback', true).first();
9673             
9674             if(feedback){
9675                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
9676             }
9677             
9678         }
9679         
9680         this.fireEvent('valid', this);
9681     },
9682     
9683      /**
9684      * Mark this field as valid
9685      */
9686     markValid : function()
9687     {
9688         if(!this.el  || this.preventMark){ // not rendered
9689             return;
9690         }
9691         
9692         this.el.removeClass([this.invalidClass, this.validClass]);
9693         
9694         var feedback = this.el.select('.form-control-feedback', true).first();
9695             
9696         if(feedback){
9697             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9698         }
9699
9700         if(this.disabled || this.allowBlank){
9701             return;
9702         }
9703         
9704         var label = this.el.select('label', true).first();
9705         var icon = this.el.select('i.fa-star', true).first();
9706         
9707         if(label && icon){
9708             icon.remove();
9709         }
9710         
9711         this.el.addClass(this.validClass);
9712         
9713         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
9714             
9715             var feedback = this.el.select('.form-control-feedback', true).first();
9716             
9717             if(feedback){
9718                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9719                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
9720             }
9721             
9722         }
9723         
9724         this.fireEvent('valid', this);
9725     },
9726     
9727      /**
9728      * Mark this field as invalid
9729      * @param {String} msg The validation message
9730      */
9731     markInvalid : function(msg)
9732     {
9733         if(!this.el  || this.preventMark){ // not rendered
9734             return;
9735         }
9736         
9737         this.el.removeClass([this.invalidClass, this.validClass]);
9738         
9739         var feedback = this.el.select('.form-control-feedback', true).first();
9740             
9741         if(feedback){
9742             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9743         }
9744
9745         if(this.disabled || this.allowBlank){
9746             return;
9747         }
9748         
9749         var label = this.el.select('label', true).first();
9750         var icon = this.el.select('i.fa-star', true).first();
9751         
9752         if(!this.getValue().length && label && !icon){
9753             this.el.createChild({
9754                 tag : 'i',
9755                 cls : 'text-danger fa fa-lg fa-star',
9756                 tooltip : 'This field is required',
9757                 style : 'margin-right:5px;'
9758             }, label, true);
9759         }
9760
9761         this.el.addClass(this.invalidClass);
9762         
9763         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
9764             
9765             var feedback = this.el.select('.form-control-feedback', true).first();
9766             
9767             if(feedback){
9768                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
9769                 
9770                 if(this.getValue().length || this.forceFeedback){
9771                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
9772                 }
9773                 
9774             }
9775             
9776         }
9777         
9778         this.fireEvent('invalid', this, msg);
9779     }
9780 });
9781
9782  
9783 /*
9784  * - LGPL
9785  *
9786  * trigger field - base class for combo..
9787  * 
9788  */
9789  
9790 /**
9791  * @class Roo.bootstrap.TriggerField
9792  * @extends Roo.bootstrap.Input
9793  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
9794  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
9795  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
9796  * for which you can provide a custom implementation.  For example:
9797  * <pre><code>
9798 var trigger = new Roo.bootstrap.TriggerField();
9799 trigger.onTriggerClick = myTriggerFn;
9800 trigger.applyTo('my-field');
9801 </code></pre>
9802  *
9803  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
9804  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
9805  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
9806  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
9807  * @cfg {String} caret (search|calendar) a fontawesome for the trigger icon see http://fortawesome.github.io/Font-Awesome/icons/
9808
9809  * @constructor
9810  * Create a new TriggerField.
9811  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
9812  * to the base TextField)
9813  */
9814 Roo.bootstrap.TriggerField = function(config){
9815     this.mimicing = false;
9816     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
9817 };
9818
9819 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
9820     /**
9821      * @cfg {String} triggerClass A CSS class to apply to the trigger
9822      */
9823      /**
9824      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
9825      */
9826     hideTrigger:false,
9827
9828     /**
9829      * @cfg {Boolean} removable (true|false) special filter default false
9830      */
9831     removable : false,
9832     
9833     /** @cfg {Boolean} grow @hide */
9834     /** @cfg {Number} growMin @hide */
9835     /** @cfg {Number} growMax @hide */
9836
9837     /**
9838      * @hide 
9839      * @method
9840      */
9841     autoSize: Roo.emptyFn,
9842     // private
9843     monitorTab : true,
9844     // private
9845     deferHeight : true,
9846
9847     
9848     actionMode : 'wrap',
9849     
9850     caret : false,
9851     
9852     
9853     getAutoCreate : function(){
9854        
9855         var align = this.labelAlign || this.parentLabelAlign();
9856         
9857         var id = Roo.id();
9858         
9859         var cfg = {
9860             cls: 'form-group' //input-group
9861         };
9862         
9863         
9864         var input =  {
9865             tag: 'input',
9866             id : id,
9867             type : this.inputType,
9868             cls : 'form-control',
9869             autocomplete: 'new-password',
9870             placeholder : this.placeholder || '' 
9871             
9872         };
9873         if (this.name) {
9874             input.name = this.name;
9875         }
9876         if (this.size) {
9877             input.cls += ' input-' + this.size;
9878         }
9879         
9880         if (this.disabled) {
9881             input.disabled=true;
9882         }
9883         
9884         var inputblock = input;
9885         
9886         if(this.hasFeedback && !this.allowBlank){
9887             
9888             var feedback = {
9889                 tag: 'span',
9890                 cls: 'glyphicon form-control-feedback'
9891             };
9892             
9893             if(this.removable && !this.editable && !this.tickable){
9894                 inputblock = {
9895                     cls : 'has-feedback',
9896                     cn :  [
9897                         inputblock,
9898                         {
9899                             tag: 'button',
9900                             html : 'x',
9901                             cls : 'roo-combo-removable-btn close'
9902                         },
9903                         feedback
9904                     ] 
9905                 };
9906             } else {
9907                 inputblock = {
9908                     cls : 'has-feedback',
9909                     cn :  [
9910                         inputblock,
9911                         feedback
9912                     ] 
9913                 };
9914             }
9915
9916         } else {
9917             if(this.removable && !this.editable && !this.tickable){
9918                 inputblock = {
9919                     cls : 'roo-removable',
9920                     cn :  [
9921                         inputblock,
9922                         {
9923                             tag: 'button',
9924                             html : 'x',
9925                             cls : 'roo-combo-removable-btn close'
9926                         }
9927                     ] 
9928                 };
9929             }
9930         }
9931         
9932         if (this.before || this.after) {
9933             
9934             inputblock = {
9935                 cls : 'input-group',
9936                 cn :  [] 
9937             };
9938             if (this.before) {
9939                 inputblock.cn.push({
9940                     tag :'span',
9941                     cls : 'input-group-addon',
9942                     html : this.before
9943                 });
9944             }
9945             
9946             inputblock.cn.push(input);
9947             
9948             if(this.hasFeedback && !this.allowBlank){
9949                 inputblock.cls += ' has-feedback';
9950                 inputblock.cn.push(feedback);
9951             }
9952             
9953             if (this.after) {
9954                 inputblock.cn.push({
9955                     tag :'span',
9956                     cls : 'input-group-addon',
9957                     html : this.after
9958                 });
9959             }
9960             
9961         };
9962         
9963         var box = {
9964             tag: 'div',
9965             cn: [
9966                 {
9967                     tag: 'input',
9968                     type : 'hidden',
9969                     cls: 'form-hidden-field'
9970                 },
9971                 inputblock
9972             ]
9973             
9974         };
9975         
9976         if(this.multiple){
9977             box = {
9978                 tag: 'div',
9979                 cn: [
9980                     {
9981                         tag: 'input',
9982                         type : 'hidden',
9983                         cls: 'form-hidden-field'
9984                     },
9985                     {
9986                         tag: 'ul',
9987                         cls: 'roo-select2-choices',
9988                         cn:[
9989                             {
9990                                 tag: 'li',
9991                                 cls: 'roo-select2-search-field',
9992                                 cn: [
9993
9994                                     inputblock
9995                                 ]
9996                             }
9997                         ]
9998                     }
9999                 ]
10000             }
10001         };
10002         
10003         var combobox = {
10004             cls: 'roo-select2-container input-group',
10005             cn: [
10006                 box
10007 //                {
10008 //                    tag: 'ul',
10009 //                    cls: 'typeahead typeahead-long dropdown-menu',
10010 //                    style: 'display:none'
10011 //                }
10012             ]
10013         };
10014         
10015         if(!this.multiple && this.showToggleBtn){
10016             
10017             var caret = {
10018                         tag: 'span',
10019                         cls: 'caret'
10020              };
10021             if (this.caret != false) {
10022                 caret = {
10023                      tag: 'i',
10024                      cls: 'fa fa-' + this.caret
10025                 };
10026                 
10027             }
10028             
10029             combobox.cn.push({
10030                 tag :'span',
10031                 cls : 'input-group-addon btn dropdown-toggle',
10032                 cn : [
10033                     caret,
10034                     {
10035                         tag: 'span',
10036                         cls: 'combobox-clear',
10037                         cn  : [
10038                             {
10039                                 tag : 'i',
10040                                 cls: 'icon-remove'
10041                             }
10042                         ]
10043                     }
10044                 ]
10045
10046             })
10047         }
10048         
10049         if(this.multiple){
10050             combobox.cls += ' roo-select2-container-multi';
10051         }
10052         
10053         if (align ==='left' && this.fieldLabel.length) {
10054             
10055             cfg.cls += ' roo-form-group-label-left';
10056
10057             cfg.cn = [
10058                 {
10059                     tag : 'i',
10060                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10061                     tooltip : 'This field is required'
10062                 },
10063                 {
10064                     tag: 'label',
10065                     'for' :  id,
10066                     cls : 'control-label',
10067                     html : this.fieldLabel
10068
10069                 },
10070                 {
10071                     cls : "", 
10072                     cn: [
10073                         combobox
10074                     ]
10075                 }
10076
10077             ];
10078             
10079             var labelCfg = cfg.cn[1];
10080             var contentCfg = cfg.cn[2];
10081             
10082             if(this.indicatorpos == 'right'){
10083                 cfg.cn = [
10084                     {
10085                         tag: 'label',
10086                         'for' :  id,
10087                         cls : 'control-label',
10088                         cn : [
10089                             {
10090                                 tag : 'span',
10091                                 html : this.fieldLabel
10092                             },
10093                             {
10094                                 tag : 'i',
10095                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10096                                 tooltip : 'This field is required'
10097                             }
10098                         ]
10099                     },
10100                     {
10101                         cls : "", 
10102                         cn: [
10103                             combobox
10104                         ]
10105                     }
10106
10107                 ];
10108                 
10109                 labelCfg = cfg.cn[0];
10110                 contentCfg = cfg.cn[1];
10111             }
10112             
10113             if(this.labelWidth > 12){
10114                 labelCfg.style = "width: " + this.labelWidth + 'px';
10115             }
10116             
10117             if(this.labelWidth < 13 && this.labelmd == 0){
10118                 this.labelmd = this.labelWidth;
10119             }
10120             
10121             if(this.labellg > 0){
10122                 labelCfg.cls += ' col-lg-' + this.labellg;
10123                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10124             }
10125             
10126             if(this.labelmd > 0){
10127                 labelCfg.cls += ' col-md-' + this.labelmd;
10128                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10129             }
10130             
10131             if(this.labelsm > 0){
10132                 labelCfg.cls += ' col-sm-' + this.labelsm;
10133                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10134             }
10135             
10136             if(this.labelxs > 0){
10137                 labelCfg.cls += ' col-xs-' + this.labelxs;
10138                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10139             }
10140             
10141         } else if ( this.fieldLabel.length) {
10142 //                Roo.log(" label");
10143             cfg.cn = [
10144                 {
10145                    tag : 'i',
10146                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10147                    tooltip : 'This field is required'
10148                },
10149                {
10150                    tag: 'label',
10151                    //cls : 'input-group-addon',
10152                    html : this.fieldLabel
10153
10154                },
10155
10156                combobox
10157
10158             ];
10159             
10160             if(this.indicatorpos == 'right'){
10161                 
10162                 cfg.cn = [
10163                     {
10164                        tag: 'label',
10165                        cn : [
10166                            {
10167                                tag : 'span',
10168                                html : this.fieldLabel
10169                            },
10170                            {
10171                               tag : 'i',
10172                               cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10173                               tooltip : 'This field is required'
10174                            }
10175                        ]
10176
10177                     },
10178                     combobox
10179
10180                 ];
10181
10182             }
10183
10184         } else {
10185             
10186 //                Roo.log(" no label && no align");
10187                 cfg = combobox
10188                      
10189                 
10190         }
10191         
10192         var settings=this;
10193         ['xs','sm','md','lg'].map(function(size){
10194             if (settings[size]) {
10195                 cfg.cls += ' col-' + size + '-' + settings[size];
10196             }
10197         });
10198         
10199         return cfg;
10200         
10201     },
10202     
10203     
10204     
10205     // private
10206     onResize : function(w, h){
10207 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
10208 //        if(typeof w == 'number'){
10209 //            var x = w - this.trigger.getWidth();
10210 //            this.inputEl().setWidth(this.adjustWidth('input', x));
10211 //            this.trigger.setStyle('left', x+'px');
10212 //        }
10213     },
10214
10215     // private
10216     adjustSize : Roo.BoxComponent.prototype.adjustSize,
10217
10218     // private
10219     getResizeEl : function(){
10220         return this.inputEl();
10221     },
10222
10223     // private
10224     getPositionEl : function(){
10225         return this.inputEl();
10226     },
10227
10228     // private
10229     alignErrorIcon : function(){
10230         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
10231     },
10232
10233     // private
10234     initEvents : function(){
10235         
10236         this.createList();
10237         
10238         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
10239         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
10240         if(!this.multiple && this.showToggleBtn){
10241             this.trigger = this.el.select('span.dropdown-toggle',true).first();
10242             if(this.hideTrigger){
10243                 this.trigger.setDisplayed(false);
10244             }
10245             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
10246         }
10247         
10248         if(this.multiple){
10249             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
10250         }
10251         
10252         if(this.removable && !this.editable && !this.tickable){
10253             var close = this.closeTriggerEl();
10254             
10255             if(close){
10256                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
10257                 close.on('click', this.removeBtnClick, this, close);
10258             }
10259         }
10260         
10261         //this.trigger.addClassOnOver('x-form-trigger-over');
10262         //this.trigger.addClassOnClick('x-form-trigger-click');
10263         
10264         //if(!this.width){
10265         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
10266         //}
10267     },
10268     
10269     closeTriggerEl : function()
10270     {
10271         var close = this.el.select('.roo-combo-removable-btn', true).first();
10272         return close ? close : false;
10273     },
10274     
10275     removeBtnClick : function(e, h, el)
10276     {
10277         e.preventDefault();
10278         
10279         if(this.fireEvent("remove", this) !== false){
10280             this.reset();
10281             this.fireEvent("afterremove", this)
10282         }
10283     },
10284     
10285     createList : function()
10286     {
10287         this.list = Roo.get(document.body).createChild({
10288             tag: 'ul',
10289             cls: 'typeahead typeahead-long dropdown-menu',
10290             style: 'display:none'
10291         });
10292         
10293         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
10294         
10295     },
10296
10297     // private
10298     initTrigger : function(){
10299        
10300     },
10301
10302     // private
10303     onDestroy : function(){
10304         if(this.trigger){
10305             this.trigger.removeAllListeners();
10306           //  this.trigger.remove();
10307         }
10308         //if(this.wrap){
10309         //    this.wrap.remove();
10310         //}
10311         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
10312     },
10313
10314     // private
10315     onFocus : function(){
10316         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
10317         /*
10318         if(!this.mimicing){
10319             this.wrap.addClass('x-trigger-wrap-focus');
10320             this.mimicing = true;
10321             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
10322             if(this.monitorTab){
10323                 this.el.on("keydown", this.checkTab, this);
10324             }
10325         }
10326         */
10327     },
10328
10329     // private
10330     checkTab : function(e){
10331         if(e.getKey() == e.TAB){
10332             this.triggerBlur();
10333         }
10334     },
10335
10336     // private
10337     onBlur : function(){
10338         // do nothing
10339     },
10340
10341     // private
10342     mimicBlur : function(e, t){
10343         /*
10344         if(!this.wrap.contains(t) && this.validateBlur()){
10345             this.triggerBlur();
10346         }
10347         */
10348     },
10349
10350     // private
10351     triggerBlur : function(){
10352         this.mimicing = false;
10353         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
10354         if(this.monitorTab){
10355             this.el.un("keydown", this.checkTab, this);
10356         }
10357         //this.wrap.removeClass('x-trigger-wrap-focus');
10358         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
10359     },
10360
10361     // private
10362     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
10363     validateBlur : function(e, t){
10364         return true;
10365     },
10366
10367     // private
10368     onDisable : function(){
10369         this.inputEl().dom.disabled = true;
10370         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
10371         //if(this.wrap){
10372         //    this.wrap.addClass('x-item-disabled');
10373         //}
10374     },
10375
10376     // private
10377     onEnable : function(){
10378         this.inputEl().dom.disabled = false;
10379         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
10380         //if(this.wrap){
10381         //    this.el.removeClass('x-item-disabled');
10382         //}
10383     },
10384
10385     // private
10386     onShow : function(){
10387         var ae = this.getActionEl();
10388         
10389         if(ae){
10390             ae.dom.style.display = '';
10391             ae.dom.style.visibility = 'visible';
10392         }
10393     },
10394
10395     // private
10396     
10397     onHide : function(){
10398         var ae = this.getActionEl();
10399         ae.dom.style.display = 'none';
10400     },
10401
10402     /**
10403      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
10404      * by an implementing function.
10405      * @method
10406      * @param {EventObject} e
10407      */
10408     onTriggerClick : Roo.emptyFn
10409 });
10410  /*
10411  * Based on:
10412  * Ext JS Library 1.1.1
10413  * Copyright(c) 2006-2007, Ext JS, LLC.
10414  *
10415  * Originally Released Under LGPL - original licence link has changed is not relivant.
10416  *
10417  * Fork - LGPL
10418  * <script type="text/javascript">
10419  */
10420
10421
10422 /**
10423  * @class Roo.data.SortTypes
10424  * @singleton
10425  * Defines the default sorting (casting?) comparison functions used when sorting data.
10426  */
10427 Roo.data.SortTypes = {
10428     /**
10429      * Default sort that does nothing
10430      * @param {Mixed} s The value being converted
10431      * @return {Mixed} The comparison value
10432      */
10433     none : function(s){
10434         return s;
10435     },
10436     
10437     /**
10438      * The regular expression used to strip tags
10439      * @type {RegExp}
10440      * @property
10441      */
10442     stripTagsRE : /<\/?[^>]+>/gi,
10443     
10444     /**
10445      * Strips all HTML tags to sort on text only
10446      * @param {Mixed} s The value being converted
10447      * @return {String} The comparison value
10448      */
10449     asText : function(s){
10450         return String(s).replace(this.stripTagsRE, "");
10451     },
10452     
10453     /**
10454      * Strips all HTML tags to sort on text only - Case insensitive
10455      * @param {Mixed} s The value being converted
10456      * @return {String} The comparison value
10457      */
10458     asUCText : function(s){
10459         return String(s).toUpperCase().replace(this.stripTagsRE, "");
10460     },
10461     
10462     /**
10463      * Case insensitive string
10464      * @param {Mixed} s The value being converted
10465      * @return {String} The comparison value
10466      */
10467     asUCString : function(s) {
10468         return String(s).toUpperCase();
10469     },
10470     
10471     /**
10472      * Date sorting
10473      * @param {Mixed} s The value being converted
10474      * @return {Number} The comparison value
10475      */
10476     asDate : function(s) {
10477         if(!s){
10478             return 0;
10479         }
10480         if(s instanceof Date){
10481             return s.getTime();
10482         }
10483         return Date.parse(String(s));
10484     },
10485     
10486     /**
10487      * Float sorting
10488      * @param {Mixed} s The value being converted
10489      * @return {Float} The comparison value
10490      */
10491     asFloat : function(s) {
10492         var val = parseFloat(String(s).replace(/,/g, ""));
10493         if(isNaN(val)) {
10494             val = 0;
10495         }
10496         return val;
10497     },
10498     
10499     /**
10500      * Integer sorting
10501      * @param {Mixed} s The value being converted
10502      * @return {Number} The comparison value
10503      */
10504     asInt : function(s) {
10505         var val = parseInt(String(s).replace(/,/g, ""));
10506         if(isNaN(val)) {
10507             val = 0;
10508         }
10509         return val;
10510     }
10511 };/*
10512  * Based on:
10513  * Ext JS Library 1.1.1
10514  * Copyright(c) 2006-2007, Ext JS, LLC.
10515  *
10516  * Originally Released Under LGPL - original licence link has changed is not relivant.
10517  *
10518  * Fork - LGPL
10519  * <script type="text/javascript">
10520  */
10521
10522 /**
10523 * @class Roo.data.Record
10524  * Instances of this class encapsulate both record <em>definition</em> information, and record
10525  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
10526  * to access Records cached in an {@link Roo.data.Store} object.<br>
10527  * <p>
10528  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
10529  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
10530  * objects.<br>
10531  * <p>
10532  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
10533  * @constructor
10534  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
10535  * {@link #create}. The parameters are the same.
10536  * @param {Array} data An associative Array of data values keyed by the field name.
10537  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
10538  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
10539  * not specified an integer id is generated.
10540  */
10541 Roo.data.Record = function(data, id){
10542     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
10543     this.data = data;
10544 };
10545
10546 /**
10547  * Generate a constructor for a specific record layout.
10548  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
10549  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
10550  * Each field definition object may contain the following properties: <ul>
10551  * <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,
10552  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
10553  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
10554  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
10555  * is being used, then this is a string containing the javascript expression to reference the data relative to 
10556  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
10557  * to the data item relative to the record element. If the mapping expression is the same as the field name,
10558  * this may be omitted.</p></li>
10559  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
10560  * <ul><li>auto (Default, implies no conversion)</li>
10561  * <li>string</li>
10562  * <li>int</li>
10563  * <li>float</li>
10564  * <li>boolean</li>
10565  * <li>date</li></ul></p></li>
10566  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
10567  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
10568  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
10569  * by the Reader into an object that will be stored in the Record. It is passed the
10570  * following parameters:<ul>
10571  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
10572  * </ul></p></li>
10573  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
10574  * </ul>
10575  * <br>usage:<br><pre><code>
10576 var TopicRecord = Roo.data.Record.create(
10577     {name: 'title', mapping: 'topic_title'},
10578     {name: 'author', mapping: 'username'},
10579     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
10580     {name: 'lastPost', mapping: 'post_time', type: 'date'},
10581     {name: 'lastPoster', mapping: 'user2'},
10582     {name: 'excerpt', mapping: 'post_text'}
10583 );
10584
10585 var myNewRecord = new TopicRecord({
10586     title: 'Do my job please',
10587     author: 'noobie',
10588     totalPosts: 1,
10589     lastPost: new Date(),
10590     lastPoster: 'Animal',
10591     excerpt: 'No way dude!'
10592 });
10593 myStore.add(myNewRecord);
10594 </code></pre>
10595  * @method create
10596  * @static
10597  */
10598 Roo.data.Record.create = function(o){
10599     var f = function(){
10600         f.superclass.constructor.apply(this, arguments);
10601     };
10602     Roo.extend(f, Roo.data.Record);
10603     var p = f.prototype;
10604     p.fields = new Roo.util.MixedCollection(false, function(field){
10605         return field.name;
10606     });
10607     for(var i = 0, len = o.length; i < len; i++){
10608         p.fields.add(new Roo.data.Field(o[i]));
10609     }
10610     f.getField = function(name){
10611         return p.fields.get(name);  
10612     };
10613     return f;
10614 };
10615
10616 Roo.data.Record.AUTO_ID = 1000;
10617 Roo.data.Record.EDIT = 'edit';
10618 Roo.data.Record.REJECT = 'reject';
10619 Roo.data.Record.COMMIT = 'commit';
10620
10621 Roo.data.Record.prototype = {
10622     /**
10623      * Readonly flag - true if this record has been modified.
10624      * @type Boolean
10625      */
10626     dirty : false,
10627     editing : false,
10628     error: null,
10629     modified: null,
10630
10631     // private
10632     join : function(store){
10633         this.store = store;
10634     },
10635
10636     /**
10637      * Set the named field to the specified value.
10638      * @param {String} name The name of the field to set.
10639      * @param {Object} value The value to set the field to.
10640      */
10641     set : function(name, value){
10642         if(this.data[name] == value){
10643             return;
10644         }
10645         this.dirty = true;
10646         if(!this.modified){
10647             this.modified = {};
10648         }
10649         if(typeof this.modified[name] == 'undefined'){
10650             this.modified[name] = this.data[name];
10651         }
10652         this.data[name] = value;
10653         if(!this.editing && this.store){
10654             this.store.afterEdit(this);
10655         }       
10656     },
10657
10658     /**
10659      * Get the value of the named field.
10660      * @param {String} name The name of the field to get the value of.
10661      * @return {Object} The value of the field.
10662      */
10663     get : function(name){
10664         return this.data[name]; 
10665     },
10666
10667     // private
10668     beginEdit : function(){
10669         this.editing = true;
10670         this.modified = {}; 
10671     },
10672
10673     // private
10674     cancelEdit : function(){
10675         this.editing = false;
10676         delete this.modified;
10677     },
10678
10679     // private
10680     endEdit : function(){
10681         this.editing = false;
10682         if(this.dirty && this.store){
10683             this.store.afterEdit(this);
10684         }
10685     },
10686
10687     /**
10688      * Usually called by the {@link Roo.data.Store} which owns the Record.
10689      * Rejects all changes made to the Record since either creation, or the last commit operation.
10690      * Modified fields are reverted to their original values.
10691      * <p>
10692      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10693      * of reject operations.
10694      */
10695     reject : function(){
10696         var m = this.modified;
10697         for(var n in m){
10698             if(typeof m[n] != "function"){
10699                 this.data[n] = m[n];
10700             }
10701         }
10702         this.dirty = false;
10703         delete this.modified;
10704         this.editing = false;
10705         if(this.store){
10706             this.store.afterReject(this);
10707         }
10708     },
10709
10710     /**
10711      * Usually called by the {@link Roo.data.Store} which owns the Record.
10712      * Commits all changes made to the Record since either creation, or the last commit operation.
10713      * <p>
10714      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
10715      * of commit operations.
10716      */
10717     commit : function(){
10718         this.dirty = false;
10719         delete this.modified;
10720         this.editing = false;
10721         if(this.store){
10722             this.store.afterCommit(this);
10723         }
10724     },
10725
10726     // private
10727     hasError : function(){
10728         return this.error != null;
10729     },
10730
10731     // private
10732     clearError : function(){
10733         this.error = null;
10734     },
10735
10736     /**
10737      * Creates a copy of this record.
10738      * @param {String} id (optional) A new record id if you don't want to use this record's id
10739      * @return {Record}
10740      */
10741     copy : function(newId) {
10742         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
10743     }
10744 };/*
10745  * Based on:
10746  * Ext JS Library 1.1.1
10747  * Copyright(c) 2006-2007, Ext JS, LLC.
10748  *
10749  * Originally Released Under LGPL - original licence link has changed is not relivant.
10750  *
10751  * Fork - LGPL
10752  * <script type="text/javascript">
10753  */
10754
10755
10756
10757 /**
10758  * @class Roo.data.Store
10759  * @extends Roo.util.Observable
10760  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
10761  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
10762  * <p>
10763  * 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
10764  * has no knowledge of the format of the data returned by the Proxy.<br>
10765  * <p>
10766  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
10767  * instances from the data object. These records are cached and made available through accessor functions.
10768  * @constructor
10769  * Creates a new Store.
10770  * @param {Object} config A config object containing the objects needed for the Store to access data,
10771  * and read the data into Records.
10772  */
10773 Roo.data.Store = function(config){
10774     this.data = new Roo.util.MixedCollection(false);
10775     this.data.getKey = function(o){
10776         return o.id;
10777     };
10778     this.baseParams = {};
10779     // private
10780     this.paramNames = {
10781         "start" : "start",
10782         "limit" : "limit",
10783         "sort" : "sort",
10784         "dir" : "dir",
10785         "multisort" : "_multisort"
10786     };
10787
10788     if(config && config.data){
10789         this.inlineData = config.data;
10790         delete config.data;
10791     }
10792
10793     Roo.apply(this, config);
10794     
10795     if(this.reader){ // reader passed
10796         this.reader = Roo.factory(this.reader, Roo.data);
10797         this.reader.xmodule = this.xmodule || false;
10798         if(!this.recordType){
10799             this.recordType = this.reader.recordType;
10800         }
10801         if(this.reader.onMetaChange){
10802             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
10803         }
10804     }
10805
10806     if(this.recordType){
10807         this.fields = this.recordType.prototype.fields;
10808     }
10809     this.modified = [];
10810
10811     this.addEvents({
10812         /**
10813          * @event datachanged
10814          * Fires when the data cache has changed, and a widget which is using this Store
10815          * as a Record cache should refresh its view.
10816          * @param {Store} this
10817          */
10818         datachanged : true,
10819         /**
10820          * @event metachange
10821          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
10822          * @param {Store} this
10823          * @param {Object} meta The JSON metadata
10824          */
10825         metachange : true,
10826         /**
10827          * @event add
10828          * Fires when Records have been added to the Store
10829          * @param {Store} this
10830          * @param {Roo.data.Record[]} records The array of Records added
10831          * @param {Number} index The index at which the record(s) were added
10832          */
10833         add : true,
10834         /**
10835          * @event remove
10836          * Fires when a Record has been removed from the Store
10837          * @param {Store} this
10838          * @param {Roo.data.Record} record The Record that was removed
10839          * @param {Number} index The index at which the record was removed
10840          */
10841         remove : true,
10842         /**
10843          * @event update
10844          * Fires when a Record has been updated
10845          * @param {Store} this
10846          * @param {Roo.data.Record} record The Record that was updated
10847          * @param {String} operation The update operation being performed.  Value may be one of:
10848          * <pre><code>
10849  Roo.data.Record.EDIT
10850  Roo.data.Record.REJECT
10851  Roo.data.Record.COMMIT
10852          * </code></pre>
10853          */
10854         update : true,
10855         /**
10856          * @event clear
10857          * Fires when the data cache has been cleared.
10858          * @param {Store} this
10859          */
10860         clear : true,
10861         /**
10862          * @event beforeload
10863          * Fires before a request is made for a new data object.  If the beforeload handler returns false
10864          * the load action will be canceled.
10865          * @param {Store} this
10866          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10867          */
10868         beforeload : true,
10869         /**
10870          * @event beforeloadadd
10871          * Fires after a new set of Records has been loaded.
10872          * @param {Store} this
10873          * @param {Roo.data.Record[]} records The Records that were loaded
10874          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10875          */
10876         beforeloadadd : true,
10877         /**
10878          * @event load
10879          * Fires after a new set of Records has been loaded, before they are added to the store.
10880          * @param {Store} this
10881          * @param {Roo.data.Record[]} records The Records that were loaded
10882          * @param {Object} options The loading options that were specified (see {@link #load} for details)
10883          * @params {Object} return from reader
10884          */
10885         load : true,
10886         /**
10887          * @event loadexception
10888          * Fires if an exception occurs in the Proxy during loading.
10889          * Called with the signature of the Proxy's "loadexception" event.
10890          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
10891          * 
10892          * @param {Proxy} 
10893          * @param {Object} return from JsonData.reader() - success, totalRecords, records
10894          * @param {Object} load options 
10895          * @param {Object} jsonData from your request (normally this contains the Exception)
10896          */
10897         loadexception : true
10898     });
10899     
10900     if(this.proxy){
10901         this.proxy = Roo.factory(this.proxy, Roo.data);
10902         this.proxy.xmodule = this.xmodule || false;
10903         this.relayEvents(this.proxy,  ["loadexception"]);
10904     }
10905     this.sortToggle = {};
10906     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
10907
10908     Roo.data.Store.superclass.constructor.call(this);
10909
10910     if(this.inlineData){
10911         this.loadData(this.inlineData);
10912         delete this.inlineData;
10913     }
10914 };
10915
10916 Roo.extend(Roo.data.Store, Roo.util.Observable, {
10917      /**
10918     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
10919     * without a remote query - used by combo/forms at present.
10920     */
10921     
10922     /**
10923     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
10924     */
10925     /**
10926     * @cfg {Array} data Inline data to be loaded when the store is initialized.
10927     */
10928     /**
10929     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
10930     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
10931     */
10932     /**
10933     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
10934     * on any HTTP request
10935     */
10936     /**
10937     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
10938     */
10939     /**
10940     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
10941     */
10942     multiSort: false,
10943     /**
10944     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
10945     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
10946     */
10947     remoteSort : false,
10948
10949     /**
10950     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
10951      * loaded or when a record is removed. (defaults to false).
10952     */
10953     pruneModifiedRecords : false,
10954
10955     // private
10956     lastOptions : null,
10957
10958     /**
10959      * Add Records to the Store and fires the add event.
10960      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
10961      */
10962     add : function(records){
10963         records = [].concat(records);
10964         for(var i = 0, len = records.length; i < len; i++){
10965             records[i].join(this);
10966         }
10967         var index = this.data.length;
10968         this.data.addAll(records);
10969         this.fireEvent("add", this, records, index);
10970     },
10971
10972     /**
10973      * Remove a Record from the Store and fires the remove event.
10974      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
10975      */
10976     remove : function(record){
10977         var index = this.data.indexOf(record);
10978         this.data.removeAt(index);
10979         if(this.pruneModifiedRecords){
10980             this.modified.remove(record);
10981         }
10982         this.fireEvent("remove", this, record, index);
10983     },
10984
10985     /**
10986      * Remove all Records from the Store and fires the clear event.
10987      */
10988     removeAll : function(){
10989         this.data.clear();
10990         if(this.pruneModifiedRecords){
10991             this.modified = [];
10992         }
10993         this.fireEvent("clear", this);
10994     },
10995
10996     /**
10997      * Inserts Records to the Store at the given index and fires the add event.
10998      * @param {Number} index The start index at which to insert the passed Records.
10999      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
11000      */
11001     insert : function(index, records){
11002         records = [].concat(records);
11003         for(var i = 0, len = records.length; i < len; i++){
11004             this.data.insert(index, records[i]);
11005             records[i].join(this);
11006         }
11007         this.fireEvent("add", this, records, index);
11008     },
11009
11010     /**
11011      * Get the index within the cache of the passed Record.
11012      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
11013      * @return {Number} The index of the passed Record. Returns -1 if not found.
11014      */
11015     indexOf : function(record){
11016         return this.data.indexOf(record);
11017     },
11018
11019     /**
11020      * Get the index within the cache of the Record with the passed id.
11021      * @param {String} id The id of the Record to find.
11022      * @return {Number} The index of the Record. Returns -1 if not found.
11023      */
11024     indexOfId : function(id){
11025         return this.data.indexOfKey(id);
11026     },
11027
11028     /**
11029      * Get the Record with the specified id.
11030      * @param {String} id The id of the Record to find.
11031      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
11032      */
11033     getById : function(id){
11034         return this.data.key(id);
11035     },
11036
11037     /**
11038      * Get the Record at the specified index.
11039      * @param {Number} index The index of the Record to find.
11040      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
11041      */
11042     getAt : function(index){
11043         return this.data.itemAt(index);
11044     },
11045
11046     /**
11047      * Returns a range of Records between specified indices.
11048      * @param {Number} startIndex (optional) The starting index (defaults to 0)
11049      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
11050      * @return {Roo.data.Record[]} An array of Records
11051      */
11052     getRange : function(start, end){
11053         return this.data.getRange(start, end);
11054     },
11055
11056     // private
11057     storeOptions : function(o){
11058         o = Roo.apply({}, o);
11059         delete o.callback;
11060         delete o.scope;
11061         this.lastOptions = o;
11062     },
11063
11064     /**
11065      * Loads the Record cache from the configured Proxy using the configured Reader.
11066      * <p>
11067      * If using remote paging, then the first load call must specify the <em>start</em>
11068      * and <em>limit</em> properties in the options.params property to establish the initial
11069      * position within the dataset, and the number of Records to cache on each read from the Proxy.
11070      * <p>
11071      * <strong>It is important to note that for remote data sources, loading is asynchronous,
11072      * and this call will return before the new data has been loaded. Perform any post-processing
11073      * in a callback function, or in a "load" event handler.</strong>
11074      * <p>
11075      * @param {Object} options An object containing properties which control loading options:<ul>
11076      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
11077      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
11078      * passed the following arguments:<ul>
11079      * <li>r : Roo.data.Record[]</li>
11080      * <li>options: Options object from the load call</li>
11081      * <li>success: Boolean success indicator</li></ul></li>
11082      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
11083      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
11084      * </ul>
11085      */
11086     load : function(options){
11087         options = options || {};
11088         if(this.fireEvent("beforeload", this, options) !== false){
11089             this.storeOptions(options);
11090             var p = Roo.apply(options.params || {}, this.baseParams);
11091             // if meta was not loaded from remote source.. try requesting it.
11092             if (!this.reader.metaFromRemote) {
11093                 p._requestMeta = 1;
11094             }
11095             if(this.sortInfo && this.remoteSort){
11096                 var pn = this.paramNames;
11097                 p[pn["sort"]] = this.sortInfo.field;
11098                 p[pn["dir"]] = this.sortInfo.direction;
11099             }
11100             if (this.multiSort) {
11101                 var pn = this.paramNames;
11102                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
11103             }
11104             
11105             this.proxy.load(p, this.reader, this.loadRecords, this, options);
11106         }
11107     },
11108
11109     /**
11110      * Reloads the Record cache from the configured Proxy using the configured Reader and
11111      * the options from the last load operation performed.
11112      * @param {Object} options (optional) An object containing properties which may override the options
11113      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
11114      * the most recently used options are reused).
11115      */
11116     reload : function(options){
11117         this.load(Roo.applyIf(options||{}, this.lastOptions));
11118     },
11119
11120     // private
11121     // Called as a callback by the Reader during a load operation.
11122     loadRecords : function(o, options, success){
11123         if(!o || success === false){
11124             if(success !== false){
11125                 this.fireEvent("load", this, [], options, o);
11126             }
11127             if(options.callback){
11128                 options.callback.call(options.scope || this, [], options, false);
11129             }
11130             return;
11131         }
11132         // if data returned failure - throw an exception.
11133         if (o.success === false) {
11134             // show a message if no listener is registered.
11135             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
11136                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
11137             }
11138             // loadmask wil be hooked into this..
11139             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
11140             return;
11141         }
11142         var r = o.records, t = o.totalRecords || r.length;
11143         
11144         this.fireEvent("beforeloadadd", this, r, options, o);
11145         
11146         if(!options || options.add !== true){
11147             if(this.pruneModifiedRecords){
11148                 this.modified = [];
11149             }
11150             for(var i = 0, len = r.length; i < len; i++){
11151                 r[i].join(this);
11152             }
11153             if(this.snapshot){
11154                 this.data = this.snapshot;
11155                 delete this.snapshot;
11156             }
11157             this.data.clear();
11158             this.data.addAll(r);
11159             this.totalLength = t;
11160             this.applySort();
11161             this.fireEvent("datachanged", this);
11162         }else{
11163             this.totalLength = Math.max(t, this.data.length+r.length);
11164             this.add(r);
11165         }
11166         
11167         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
11168                 
11169             var e = new Roo.data.Record({});
11170
11171             e.set(this.parent.displayField, this.parent.emptyTitle);
11172             e.set(this.parent.valueField, '');
11173
11174             this.insert(0, e);
11175         }
11176             
11177         this.fireEvent("load", this, r, options, o);
11178         if(options.callback){
11179             options.callback.call(options.scope || this, r, options, true);
11180         }
11181     },
11182
11183
11184     /**
11185      * Loads data from a passed data block. A Reader which understands the format of the data
11186      * must have been configured in the constructor.
11187      * @param {Object} data The data block from which to read the Records.  The format of the data expected
11188      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
11189      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
11190      */
11191     loadData : function(o, append){
11192         var r = this.reader.readRecords(o);
11193         this.loadRecords(r, {add: append}, true);
11194     },
11195
11196     /**
11197      * Gets the number of cached records.
11198      * <p>
11199      * <em>If using paging, this may not be the total size of the dataset. If the data object
11200      * used by the Reader contains the dataset size, then the getTotalCount() function returns
11201      * the data set size</em>
11202      */
11203     getCount : function(){
11204         return this.data.length || 0;
11205     },
11206
11207     /**
11208      * Gets the total number of records in the dataset as returned by the server.
11209      * <p>
11210      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
11211      * the dataset size</em>
11212      */
11213     getTotalCount : function(){
11214         return this.totalLength || 0;
11215     },
11216
11217     /**
11218      * Returns the sort state of the Store as an object with two properties:
11219      * <pre><code>
11220  field {String} The name of the field by which the Records are sorted
11221  direction {String} The sort order, "ASC" or "DESC"
11222      * </code></pre>
11223      */
11224     getSortState : function(){
11225         return this.sortInfo;
11226     },
11227
11228     // private
11229     applySort : function(){
11230         if(this.sortInfo && !this.remoteSort){
11231             var s = this.sortInfo, f = s.field;
11232             var st = this.fields.get(f).sortType;
11233             var fn = function(r1, r2){
11234                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
11235                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
11236             };
11237             this.data.sort(s.direction, fn);
11238             if(this.snapshot && this.snapshot != this.data){
11239                 this.snapshot.sort(s.direction, fn);
11240             }
11241         }
11242     },
11243
11244     /**
11245      * Sets the default sort column and order to be used by the next load operation.
11246      * @param {String} fieldName The name of the field to sort by.
11247      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11248      */
11249     setDefaultSort : function(field, dir){
11250         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
11251     },
11252
11253     /**
11254      * Sort the Records.
11255      * If remote sorting is used, the sort is performed on the server, and the cache is
11256      * reloaded. If local sorting is used, the cache is sorted internally.
11257      * @param {String} fieldName The name of the field to sort by.
11258      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
11259      */
11260     sort : function(fieldName, dir){
11261         var f = this.fields.get(fieldName);
11262         if(!dir){
11263             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
11264             
11265             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
11266                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
11267             }else{
11268                 dir = f.sortDir;
11269             }
11270         }
11271         this.sortToggle[f.name] = dir;
11272         this.sortInfo = {field: f.name, direction: dir};
11273         if(!this.remoteSort){
11274             this.applySort();
11275             this.fireEvent("datachanged", this);
11276         }else{
11277             this.load(this.lastOptions);
11278         }
11279     },
11280
11281     /**
11282      * Calls the specified function for each of the Records in the cache.
11283      * @param {Function} fn The function to call. The Record is passed as the first parameter.
11284      * Returning <em>false</em> aborts and exits the iteration.
11285      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
11286      */
11287     each : function(fn, scope){
11288         this.data.each(fn, scope);
11289     },
11290
11291     /**
11292      * Gets all records modified since the last commit.  Modified records are persisted across load operations
11293      * (e.g., during paging).
11294      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
11295      */
11296     getModifiedRecords : function(){
11297         return this.modified;
11298     },
11299
11300     // private
11301     createFilterFn : function(property, value, anyMatch){
11302         if(!value.exec){ // not a regex
11303             value = String(value);
11304             if(value.length == 0){
11305                 return false;
11306             }
11307             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
11308         }
11309         return function(r){
11310             return value.test(r.data[property]);
11311         };
11312     },
11313
11314     /**
11315      * Sums the value of <i>property</i> for each record between start and end and returns the result.
11316      * @param {String} property A field on your records
11317      * @param {Number} start The record index to start at (defaults to 0)
11318      * @param {Number} end The last record index to include (defaults to length - 1)
11319      * @return {Number} The sum
11320      */
11321     sum : function(property, start, end){
11322         var rs = this.data.items, v = 0;
11323         start = start || 0;
11324         end = (end || end === 0) ? end : rs.length-1;
11325
11326         for(var i = start; i <= end; i++){
11327             v += (rs[i].data[property] || 0);
11328         }
11329         return v;
11330     },
11331
11332     /**
11333      * Filter the records by a specified property.
11334      * @param {String} field A field on your records
11335      * @param {String/RegExp} value Either a string that the field
11336      * should start with or a RegExp to test against the field
11337      * @param {Boolean} anyMatch True to match any part not just the beginning
11338      */
11339     filter : function(property, value, anyMatch){
11340         var fn = this.createFilterFn(property, value, anyMatch);
11341         return fn ? this.filterBy(fn) : this.clearFilter();
11342     },
11343
11344     /**
11345      * Filter by a function. The specified function will be called with each
11346      * record in this data source. If the function returns true the record is included,
11347      * otherwise it is filtered.
11348      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11349      * @param {Object} scope (optional) The scope of the function (defaults to this)
11350      */
11351     filterBy : function(fn, scope){
11352         this.snapshot = this.snapshot || this.data;
11353         this.data = this.queryBy(fn, scope||this);
11354         this.fireEvent("datachanged", this);
11355     },
11356
11357     /**
11358      * Query the records by a specified property.
11359      * @param {String} field A field on your records
11360      * @param {String/RegExp} value Either a string that the field
11361      * should start with or a RegExp to test against the field
11362      * @param {Boolean} anyMatch True to match any part not just the beginning
11363      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11364      */
11365     query : function(property, value, anyMatch){
11366         var fn = this.createFilterFn(property, value, anyMatch);
11367         return fn ? this.queryBy(fn) : this.data.clone();
11368     },
11369
11370     /**
11371      * Query by a function. The specified function will be called with each
11372      * record in this data source. If the function returns true the record is included
11373      * in the results.
11374      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
11375      * @param {Object} scope (optional) The scope of the function (defaults to this)
11376       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
11377      **/
11378     queryBy : function(fn, scope){
11379         var data = this.snapshot || this.data;
11380         return data.filterBy(fn, scope||this);
11381     },
11382
11383     /**
11384      * Collects unique values for a particular dataIndex from this store.
11385      * @param {String} dataIndex The property to collect
11386      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
11387      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
11388      * @return {Array} An array of the unique values
11389      **/
11390     collect : function(dataIndex, allowNull, bypassFilter){
11391         var d = (bypassFilter === true && this.snapshot) ?
11392                 this.snapshot.items : this.data.items;
11393         var v, sv, r = [], l = {};
11394         for(var i = 0, len = d.length; i < len; i++){
11395             v = d[i].data[dataIndex];
11396             sv = String(v);
11397             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
11398                 l[sv] = true;
11399                 r[r.length] = v;
11400             }
11401         }
11402         return r;
11403     },
11404
11405     /**
11406      * Revert to a view of the Record cache with no filtering applied.
11407      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
11408      */
11409     clearFilter : function(suppressEvent){
11410         if(this.snapshot && this.snapshot != this.data){
11411             this.data = this.snapshot;
11412             delete this.snapshot;
11413             if(suppressEvent !== true){
11414                 this.fireEvent("datachanged", this);
11415             }
11416         }
11417     },
11418
11419     // private
11420     afterEdit : function(record){
11421         if(this.modified.indexOf(record) == -1){
11422             this.modified.push(record);
11423         }
11424         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
11425     },
11426     
11427     // private
11428     afterReject : function(record){
11429         this.modified.remove(record);
11430         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
11431     },
11432
11433     // private
11434     afterCommit : function(record){
11435         this.modified.remove(record);
11436         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
11437     },
11438
11439     /**
11440      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
11441      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
11442      */
11443     commitChanges : function(){
11444         var m = this.modified.slice(0);
11445         this.modified = [];
11446         for(var i = 0, len = m.length; i < len; i++){
11447             m[i].commit();
11448         }
11449     },
11450
11451     /**
11452      * Cancel outstanding changes on all changed records.
11453      */
11454     rejectChanges : function(){
11455         var m = this.modified.slice(0);
11456         this.modified = [];
11457         for(var i = 0, len = m.length; i < len; i++){
11458             m[i].reject();
11459         }
11460     },
11461
11462     onMetaChange : function(meta, rtype, o){
11463         this.recordType = rtype;
11464         this.fields = rtype.prototype.fields;
11465         delete this.snapshot;
11466         this.sortInfo = meta.sortInfo || this.sortInfo;
11467         this.modified = [];
11468         this.fireEvent('metachange', this, this.reader.meta);
11469     },
11470     
11471     moveIndex : function(data, type)
11472     {
11473         var index = this.indexOf(data);
11474         
11475         var newIndex = index + type;
11476         
11477         this.remove(data);
11478         
11479         this.insert(newIndex, data);
11480         
11481     }
11482 });/*
11483  * Based on:
11484  * Ext JS Library 1.1.1
11485  * Copyright(c) 2006-2007, Ext JS, LLC.
11486  *
11487  * Originally Released Under LGPL - original licence link has changed is not relivant.
11488  *
11489  * Fork - LGPL
11490  * <script type="text/javascript">
11491  */
11492
11493 /**
11494  * @class Roo.data.SimpleStore
11495  * @extends Roo.data.Store
11496  * Small helper class to make creating Stores from Array data easier.
11497  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
11498  * @cfg {Array} fields An array of field definition objects, or field name strings.
11499  * @cfg {Array} data The multi-dimensional array of data
11500  * @constructor
11501  * @param {Object} config
11502  */
11503 Roo.data.SimpleStore = function(config){
11504     Roo.data.SimpleStore.superclass.constructor.call(this, {
11505         isLocal : true,
11506         reader: new Roo.data.ArrayReader({
11507                 id: config.id
11508             },
11509             Roo.data.Record.create(config.fields)
11510         ),
11511         proxy : new Roo.data.MemoryProxy(config.data)
11512     });
11513     this.load();
11514 };
11515 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
11516  * Based on:
11517  * Ext JS Library 1.1.1
11518  * Copyright(c) 2006-2007, Ext JS, LLC.
11519  *
11520  * Originally Released Under LGPL - original licence link has changed is not relivant.
11521  *
11522  * Fork - LGPL
11523  * <script type="text/javascript">
11524  */
11525
11526 /**
11527 /**
11528  * @extends Roo.data.Store
11529  * @class Roo.data.JsonStore
11530  * Small helper class to make creating Stores for JSON data easier. <br/>
11531 <pre><code>
11532 var store = new Roo.data.JsonStore({
11533     url: 'get-images.php',
11534     root: 'images',
11535     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
11536 });
11537 </code></pre>
11538  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
11539  * JsonReader and HttpProxy (unless inline data is provided).</b>
11540  * @cfg {Array} fields An array of field definition objects, or field name strings.
11541  * @constructor
11542  * @param {Object} config
11543  */
11544 Roo.data.JsonStore = function(c){
11545     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
11546         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
11547         reader: new Roo.data.JsonReader(c, c.fields)
11548     }));
11549 };
11550 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
11551  * Based on:
11552  * Ext JS Library 1.1.1
11553  * Copyright(c) 2006-2007, Ext JS, LLC.
11554  *
11555  * Originally Released Under LGPL - original licence link has changed is not relivant.
11556  *
11557  * Fork - LGPL
11558  * <script type="text/javascript">
11559  */
11560
11561  
11562 Roo.data.Field = function(config){
11563     if(typeof config == "string"){
11564         config = {name: config};
11565     }
11566     Roo.apply(this, config);
11567     
11568     if(!this.type){
11569         this.type = "auto";
11570     }
11571     
11572     var st = Roo.data.SortTypes;
11573     // named sortTypes are supported, here we look them up
11574     if(typeof this.sortType == "string"){
11575         this.sortType = st[this.sortType];
11576     }
11577     
11578     // set default sortType for strings and dates
11579     if(!this.sortType){
11580         switch(this.type){
11581             case "string":
11582                 this.sortType = st.asUCString;
11583                 break;
11584             case "date":
11585                 this.sortType = st.asDate;
11586                 break;
11587             default:
11588                 this.sortType = st.none;
11589         }
11590     }
11591
11592     // define once
11593     var stripRe = /[\$,%]/g;
11594
11595     // prebuilt conversion function for this field, instead of
11596     // switching every time we're reading a value
11597     if(!this.convert){
11598         var cv, dateFormat = this.dateFormat;
11599         switch(this.type){
11600             case "":
11601             case "auto":
11602             case undefined:
11603                 cv = function(v){ return v; };
11604                 break;
11605             case "string":
11606                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
11607                 break;
11608             case "int":
11609                 cv = function(v){
11610                     return v !== undefined && v !== null && v !== '' ?
11611                            parseInt(String(v).replace(stripRe, ""), 10) : '';
11612                     };
11613                 break;
11614             case "float":
11615                 cv = function(v){
11616                     return v !== undefined && v !== null && v !== '' ?
11617                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
11618                     };
11619                 break;
11620             case "bool":
11621             case "boolean":
11622                 cv = function(v){ return v === true || v === "true" || v == 1; };
11623                 break;
11624             case "date":
11625                 cv = function(v){
11626                     if(!v){
11627                         return '';
11628                     }
11629                     if(v instanceof Date){
11630                         return v;
11631                     }
11632                     if(dateFormat){
11633                         if(dateFormat == "timestamp"){
11634                             return new Date(v*1000);
11635                         }
11636                         return Date.parseDate(v, dateFormat);
11637                     }
11638                     var parsed = Date.parse(v);
11639                     return parsed ? new Date(parsed) : null;
11640                 };
11641              break;
11642             
11643         }
11644         this.convert = cv;
11645     }
11646 };
11647
11648 Roo.data.Field.prototype = {
11649     dateFormat: null,
11650     defaultValue: "",
11651     mapping: null,
11652     sortType : null,
11653     sortDir : "ASC"
11654 };/*
11655  * Based on:
11656  * Ext JS Library 1.1.1
11657  * Copyright(c) 2006-2007, Ext JS, LLC.
11658  *
11659  * Originally Released Under LGPL - original licence link has changed is not relivant.
11660  *
11661  * Fork - LGPL
11662  * <script type="text/javascript">
11663  */
11664  
11665 // Base class for reading structured data from a data source.  This class is intended to be
11666 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
11667
11668 /**
11669  * @class Roo.data.DataReader
11670  * Base class for reading structured data from a data source.  This class is intended to be
11671  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
11672  */
11673
11674 Roo.data.DataReader = function(meta, recordType){
11675     
11676     this.meta = meta;
11677     
11678     this.recordType = recordType instanceof Array ? 
11679         Roo.data.Record.create(recordType) : recordType;
11680 };
11681
11682 Roo.data.DataReader.prototype = {
11683      /**
11684      * Create an empty record
11685      * @param {Object} data (optional) - overlay some values
11686      * @return {Roo.data.Record} record created.
11687      */
11688     newRow :  function(d) {
11689         var da =  {};
11690         this.recordType.prototype.fields.each(function(c) {
11691             switch( c.type) {
11692                 case 'int' : da[c.name] = 0; break;
11693                 case 'date' : da[c.name] = new Date(); break;
11694                 case 'float' : da[c.name] = 0.0; break;
11695                 case 'boolean' : da[c.name] = false; break;
11696                 default : da[c.name] = ""; break;
11697             }
11698             
11699         });
11700         return new this.recordType(Roo.apply(da, d));
11701     }
11702     
11703 };/*
11704  * Based on:
11705  * Ext JS Library 1.1.1
11706  * Copyright(c) 2006-2007, Ext JS, LLC.
11707  *
11708  * Originally Released Under LGPL - original licence link has changed is not relivant.
11709  *
11710  * Fork - LGPL
11711  * <script type="text/javascript">
11712  */
11713
11714 /**
11715  * @class Roo.data.DataProxy
11716  * @extends Roo.data.Observable
11717  * This class is an abstract base class for implementations which provide retrieval of
11718  * unformatted data objects.<br>
11719  * <p>
11720  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
11721  * (of the appropriate type which knows how to parse the data object) to provide a block of
11722  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
11723  * <p>
11724  * Custom implementations must implement the load method as described in
11725  * {@link Roo.data.HttpProxy#load}.
11726  */
11727 Roo.data.DataProxy = function(){
11728     this.addEvents({
11729         /**
11730          * @event beforeload
11731          * Fires before a network request is made to retrieve a data object.
11732          * @param {Object} This DataProxy object.
11733          * @param {Object} params The params parameter to the load function.
11734          */
11735         beforeload : true,
11736         /**
11737          * @event load
11738          * Fires before the load method's callback is called.
11739          * @param {Object} This DataProxy object.
11740          * @param {Object} o The data object.
11741          * @param {Object} arg The callback argument object passed to the load function.
11742          */
11743         load : true,
11744         /**
11745          * @event loadexception
11746          * Fires if an Exception occurs during data retrieval.
11747          * @param {Object} This DataProxy object.
11748          * @param {Object} o The data object.
11749          * @param {Object} arg The callback argument object passed to the load function.
11750          * @param {Object} e The Exception.
11751          */
11752         loadexception : true
11753     });
11754     Roo.data.DataProxy.superclass.constructor.call(this);
11755 };
11756
11757 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
11758
11759     /**
11760      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
11761      */
11762 /*
11763  * Based on:
11764  * Ext JS Library 1.1.1
11765  * Copyright(c) 2006-2007, Ext JS, LLC.
11766  *
11767  * Originally Released Under LGPL - original licence link has changed is not relivant.
11768  *
11769  * Fork - LGPL
11770  * <script type="text/javascript">
11771  */
11772 /**
11773  * @class Roo.data.MemoryProxy
11774  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
11775  * to the Reader when its load method is called.
11776  * @constructor
11777  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
11778  */
11779 Roo.data.MemoryProxy = function(data){
11780     if (data.data) {
11781         data = data.data;
11782     }
11783     Roo.data.MemoryProxy.superclass.constructor.call(this);
11784     this.data = data;
11785 };
11786
11787 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
11788     
11789     /**
11790      * Load data from the requested source (in this case an in-memory
11791      * data object passed to the constructor), read the data object into
11792      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
11793      * process that block using the passed callback.
11794      * @param {Object} params This parameter is not used by the MemoryProxy class.
11795      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11796      * object into a block of Roo.data.Records.
11797      * @param {Function} callback The function into which to pass the block of Roo.data.records.
11798      * The function must be passed <ul>
11799      * <li>The Record block object</li>
11800      * <li>The "arg" argument from the load function</li>
11801      * <li>A boolean success indicator</li>
11802      * </ul>
11803      * @param {Object} scope The scope in which to call the callback
11804      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11805      */
11806     load : function(params, reader, callback, scope, arg){
11807         params = params || {};
11808         var result;
11809         try {
11810             result = reader.readRecords(this.data);
11811         }catch(e){
11812             this.fireEvent("loadexception", this, arg, null, e);
11813             callback.call(scope, null, arg, false);
11814             return;
11815         }
11816         callback.call(scope, result, arg, true);
11817     },
11818     
11819     // private
11820     update : function(params, records){
11821         
11822     }
11823 });/*
11824  * Based on:
11825  * Ext JS Library 1.1.1
11826  * Copyright(c) 2006-2007, Ext JS, LLC.
11827  *
11828  * Originally Released Under LGPL - original licence link has changed is not relivant.
11829  *
11830  * Fork - LGPL
11831  * <script type="text/javascript">
11832  */
11833 /**
11834  * @class Roo.data.HttpProxy
11835  * @extends Roo.data.DataProxy
11836  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
11837  * configured to reference a certain URL.<br><br>
11838  * <p>
11839  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
11840  * from which the running page was served.<br><br>
11841  * <p>
11842  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
11843  * <p>
11844  * Be aware that to enable the browser to parse an XML document, the server must set
11845  * the Content-Type header in the HTTP response to "text/xml".
11846  * @constructor
11847  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
11848  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
11849  * will be used to make the request.
11850  */
11851 Roo.data.HttpProxy = function(conn){
11852     Roo.data.HttpProxy.superclass.constructor.call(this);
11853     // is conn a conn config or a real conn?
11854     this.conn = conn;
11855     this.useAjax = !conn || !conn.events;
11856   
11857 };
11858
11859 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
11860     // thse are take from connection...
11861     
11862     /**
11863      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11864      */
11865     /**
11866      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11867      * extra parameters to each request made by this object. (defaults to undefined)
11868      */
11869     /**
11870      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11871      *  to each request made by this object. (defaults to undefined)
11872      */
11873     /**
11874      * @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)
11875      */
11876     /**
11877      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11878      */
11879      /**
11880      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11881      * @type Boolean
11882      */
11883   
11884
11885     /**
11886      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11887      * @type Boolean
11888      */
11889     /**
11890      * Return the {@link Roo.data.Connection} object being used by this Proxy.
11891      * @return {Connection} The Connection object. This object may be used to subscribe to events on
11892      * a finer-grained basis than the DataProxy events.
11893      */
11894     getConnection : function(){
11895         return this.useAjax ? Roo.Ajax : this.conn;
11896     },
11897
11898     /**
11899      * Load data from the configured {@link Roo.data.Connection}, read the data object into
11900      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
11901      * process that block using the passed callback.
11902      * @param {Object} params An object containing properties which are to be used as HTTP parameters
11903      * for the request to the remote server.
11904      * @param {Roo.data.DataReader} reader The Reader object which converts the data
11905      * object into a block of Roo.data.Records.
11906      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
11907      * The function must be passed <ul>
11908      * <li>The Record block object</li>
11909      * <li>The "arg" argument from the load function</li>
11910      * <li>A boolean success indicator</li>
11911      * </ul>
11912      * @param {Object} scope The scope in which to call the callback
11913      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
11914      */
11915     load : function(params, reader, callback, scope, arg){
11916         if(this.fireEvent("beforeload", this, params) !== false){
11917             var  o = {
11918                 params : params || {},
11919                 request: {
11920                     callback : callback,
11921                     scope : scope,
11922                     arg : arg
11923                 },
11924                 reader: reader,
11925                 callback : this.loadResponse,
11926                 scope: this
11927             };
11928             if(this.useAjax){
11929                 Roo.applyIf(o, this.conn);
11930                 if(this.activeRequest){
11931                     Roo.Ajax.abort(this.activeRequest);
11932                 }
11933                 this.activeRequest = Roo.Ajax.request(o);
11934             }else{
11935                 this.conn.request(o);
11936             }
11937         }else{
11938             callback.call(scope||this, null, arg, false);
11939         }
11940     },
11941
11942     // private
11943     loadResponse : function(o, success, response){
11944         delete this.activeRequest;
11945         if(!success){
11946             this.fireEvent("loadexception", this, o, response);
11947             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11948             return;
11949         }
11950         var result;
11951         try {
11952             result = o.reader.read(response);
11953         }catch(e){
11954             this.fireEvent("loadexception", this, o, response, e);
11955             o.request.callback.call(o.request.scope, null, o.request.arg, false);
11956             return;
11957         }
11958         
11959         this.fireEvent("load", this, o, o.request.arg);
11960         o.request.callback.call(o.request.scope, result, o.request.arg, true);
11961     },
11962
11963     // private
11964     update : function(dataSet){
11965
11966     },
11967
11968     // private
11969     updateResponse : function(dataSet){
11970
11971     }
11972 });/*
11973  * Based on:
11974  * Ext JS Library 1.1.1
11975  * Copyright(c) 2006-2007, Ext JS, LLC.
11976  *
11977  * Originally Released Under LGPL - original licence link has changed is not relivant.
11978  *
11979  * Fork - LGPL
11980  * <script type="text/javascript">
11981  */
11982
11983 /**
11984  * @class Roo.data.ScriptTagProxy
11985  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
11986  * other than the originating domain of the running page.<br><br>
11987  * <p>
11988  * <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
11989  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
11990  * <p>
11991  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
11992  * source code that is used as the source inside a &lt;script> tag.<br><br>
11993  * <p>
11994  * In order for the browser to process the returned data, the server must wrap the data object
11995  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
11996  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
11997  * depending on whether the callback name was passed:
11998  * <p>
11999  * <pre><code>
12000 boolean scriptTag = false;
12001 String cb = request.getParameter("callback");
12002 if (cb != null) {
12003     scriptTag = true;
12004     response.setContentType("text/javascript");
12005 } else {
12006     response.setContentType("application/x-json");
12007 }
12008 Writer out = response.getWriter();
12009 if (scriptTag) {
12010     out.write(cb + "(");
12011 }
12012 out.print(dataBlock.toJsonString());
12013 if (scriptTag) {
12014     out.write(");");
12015 }
12016 </pre></code>
12017  *
12018  * @constructor
12019  * @param {Object} config A configuration object.
12020  */
12021 Roo.data.ScriptTagProxy = function(config){
12022     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
12023     Roo.apply(this, config);
12024     this.head = document.getElementsByTagName("head")[0];
12025 };
12026
12027 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
12028
12029 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
12030     /**
12031      * @cfg {String} url The URL from which to request the data object.
12032      */
12033     /**
12034      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
12035      */
12036     timeout : 30000,
12037     /**
12038      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
12039      * the server the name of the callback function set up by the load call to process the returned data object.
12040      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
12041      * javascript output which calls this named function passing the data object as its only parameter.
12042      */
12043     callbackParam : "callback",
12044     /**
12045      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
12046      * name to the request.
12047      */
12048     nocache : true,
12049
12050     /**
12051      * Load data from the configured URL, read the data object into
12052      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
12053      * process that block using the passed callback.
12054      * @param {Object} params An object containing properties which are to be used as HTTP parameters
12055      * for the request to the remote server.
12056      * @param {Roo.data.DataReader} reader The Reader object which converts the data
12057      * object into a block of Roo.data.Records.
12058      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
12059      * The function must be passed <ul>
12060      * <li>The Record block object</li>
12061      * <li>The "arg" argument from the load function</li>
12062      * <li>A boolean success indicator</li>
12063      * </ul>
12064      * @param {Object} scope The scope in which to call the callback
12065      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
12066      */
12067     load : function(params, reader, callback, scope, arg){
12068         if(this.fireEvent("beforeload", this, params) !== false){
12069
12070             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
12071
12072             var url = this.url;
12073             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
12074             if(this.nocache){
12075                 url += "&_dc=" + (new Date().getTime());
12076             }
12077             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
12078             var trans = {
12079                 id : transId,
12080                 cb : "stcCallback"+transId,
12081                 scriptId : "stcScript"+transId,
12082                 params : params,
12083                 arg : arg,
12084                 url : url,
12085                 callback : callback,
12086                 scope : scope,
12087                 reader : reader
12088             };
12089             var conn = this;
12090
12091             window[trans.cb] = function(o){
12092                 conn.handleResponse(o, trans);
12093             };
12094
12095             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
12096
12097             if(this.autoAbort !== false){
12098                 this.abort();
12099             }
12100
12101             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
12102
12103             var script = document.createElement("script");
12104             script.setAttribute("src", url);
12105             script.setAttribute("type", "text/javascript");
12106             script.setAttribute("id", trans.scriptId);
12107             this.head.appendChild(script);
12108
12109             this.trans = trans;
12110         }else{
12111             callback.call(scope||this, null, arg, false);
12112         }
12113     },
12114
12115     // private
12116     isLoading : function(){
12117         return this.trans ? true : false;
12118     },
12119
12120     /**
12121      * Abort the current server request.
12122      */
12123     abort : function(){
12124         if(this.isLoading()){
12125             this.destroyTrans(this.trans);
12126         }
12127     },
12128
12129     // private
12130     destroyTrans : function(trans, isLoaded){
12131         this.head.removeChild(document.getElementById(trans.scriptId));
12132         clearTimeout(trans.timeoutId);
12133         if(isLoaded){
12134             window[trans.cb] = undefined;
12135             try{
12136                 delete window[trans.cb];
12137             }catch(e){}
12138         }else{
12139             // if hasn't been loaded, wait for load to remove it to prevent script error
12140             window[trans.cb] = function(){
12141                 window[trans.cb] = undefined;
12142                 try{
12143                     delete window[trans.cb];
12144                 }catch(e){}
12145             };
12146         }
12147     },
12148
12149     // private
12150     handleResponse : function(o, trans){
12151         this.trans = false;
12152         this.destroyTrans(trans, true);
12153         var result;
12154         try {
12155             result = trans.reader.readRecords(o);
12156         }catch(e){
12157             this.fireEvent("loadexception", this, o, trans.arg, e);
12158             trans.callback.call(trans.scope||window, null, trans.arg, false);
12159             return;
12160         }
12161         this.fireEvent("load", this, o, trans.arg);
12162         trans.callback.call(trans.scope||window, result, trans.arg, true);
12163     },
12164
12165     // private
12166     handleFailure : function(trans){
12167         this.trans = false;
12168         this.destroyTrans(trans, false);
12169         this.fireEvent("loadexception", this, null, trans.arg);
12170         trans.callback.call(trans.scope||window, null, trans.arg, false);
12171     }
12172 });/*
12173  * Based on:
12174  * Ext JS Library 1.1.1
12175  * Copyright(c) 2006-2007, Ext JS, LLC.
12176  *
12177  * Originally Released Under LGPL - original licence link has changed is not relivant.
12178  *
12179  * Fork - LGPL
12180  * <script type="text/javascript">
12181  */
12182
12183 /**
12184  * @class Roo.data.JsonReader
12185  * @extends Roo.data.DataReader
12186  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
12187  * based on mappings in a provided Roo.data.Record constructor.
12188  * 
12189  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
12190  * in the reply previously. 
12191  * 
12192  * <p>
12193  * Example code:
12194  * <pre><code>
12195 var RecordDef = Roo.data.Record.create([
12196     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
12197     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
12198 ]);
12199 var myReader = new Roo.data.JsonReader({
12200     totalProperty: "results",    // The property which contains the total dataset size (optional)
12201     root: "rows",                // The property which contains an Array of row objects
12202     id: "id"                     // The property within each row object that provides an ID for the record (optional)
12203 }, RecordDef);
12204 </code></pre>
12205  * <p>
12206  * This would consume a JSON file like this:
12207  * <pre><code>
12208 { 'results': 2, 'rows': [
12209     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
12210     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
12211 }
12212 </code></pre>
12213  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
12214  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
12215  * paged from the remote server.
12216  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
12217  * @cfg {String} root name of the property which contains the Array of row objects.
12218  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
12219  * @cfg {Array} fields Array of field definition objects
12220  * @constructor
12221  * Create a new JsonReader
12222  * @param {Object} meta Metadata configuration options
12223  * @param {Object} recordType Either an Array of field definition objects,
12224  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
12225  */
12226 Roo.data.JsonReader = function(meta, recordType){
12227     
12228     meta = meta || {};
12229     // set some defaults:
12230     Roo.applyIf(meta, {
12231         totalProperty: 'total',
12232         successProperty : 'success',
12233         root : 'data',
12234         id : 'id'
12235     });
12236     
12237     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
12238 };
12239 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
12240     
12241     /**
12242      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
12243      * Used by Store query builder to append _requestMeta to params.
12244      * 
12245      */
12246     metaFromRemote : false,
12247     /**
12248      * This method is only used by a DataProxy which has retrieved data from a remote server.
12249      * @param {Object} response The XHR object which contains the JSON data in its responseText.
12250      * @return {Object} data A data block which is used by an Roo.data.Store object as
12251      * a cache of Roo.data.Records.
12252      */
12253     read : function(response){
12254         var json = response.responseText;
12255        
12256         var o = /* eval:var:o */ eval("("+json+")");
12257         if(!o) {
12258             throw {message: "JsonReader.read: Json object not found"};
12259         }
12260         
12261         if(o.metaData){
12262             
12263             delete this.ef;
12264             this.metaFromRemote = true;
12265             this.meta = o.metaData;
12266             this.recordType = Roo.data.Record.create(o.metaData.fields);
12267             this.onMetaChange(this.meta, this.recordType, o);
12268         }
12269         return this.readRecords(o);
12270     },
12271
12272     // private function a store will implement
12273     onMetaChange : function(meta, recordType, o){
12274
12275     },
12276
12277     /**
12278          * @ignore
12279          */
12280     simpleAccess: function(obj, subsc) {
12281         return obj[subsc];
12282     },
12283
12284         /**
12285          * @ignore
12286          */
12287     getJsonAccessor: function(){
12288         var re = /[\[\.]/;
12289         return function(expr) {
12290             try {
12291                 return(re.test(expr))
12292                     ? new Function("obj", "return obj." + expr)
12293                     : function(obj){
12294                         return obj[expr];
12295                     };
12296             } catch(e){}
12297             return Roo.emptyFn;
12298         };
12299     }(),
12300
12301     /**
12302      * Create a data block containing Roo.data.Records from an XML document.
12303      * @param {Object} o An object which contains an Array of row objects in the property specified
12304      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
12305      * which contains the total size of the dataset.
12306      * @return {Object} data A data block which is used by an Roo.data.Store object as
12307      * a cache of Roo.data.Records.
12308      */
12309     readRecords : function(o){
12310         /**
12311          * After any data loads, the raw JSON data is available for further custom processing.
12312          * @type Object
12313          */
12314         this.o = o;
12315         var s = this.meta, Record = this.recordType,
12316             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
12317
12318 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
12319         if (!this.ef) {
12320             if(s.totalProperty) {
12321                     this.getTotal = this.getJsonAccessor(s.totalProperty);
12322                 }
12323                 if(s.successProperty) {
12324                     this.getSuccess = this.getJsonAccessor(s.successProperty);
12325                 }
12326                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
12327                 if (s.id) {
12328                         var g = this.getJsonAccessor(s.id);
12329                         this.getId = function(rec) {
12330                                 var r = g(rec);  
12331                                 return (r === undefined || r === "") ? null : r;
12332                         };
12333                 } else {
12334                         this.getId = function(){return null;};
12335                 }
12336             this.ef = [];
12337             for(var jj = 0; jj < fl; jj++){
12338                 f = fi[jj];
12339                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
12340                 this.ef[jj] = this.getJsonAccessor(map);
12341             }
12342         }
12343
12344         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
12345         if(s.totalProperty){
12346             var vt = parseInt(this.getTotal(o), 10);
12347             if(!isNaN(vt)){
12348                 totalRecords = vt;
12349             }
12350         }
12351         if(s.successProperty){
12352             var vs = this.getSuccess(o);
12353             if(vs === false || vs === 'false'){
12354                 success = false;
12355             }
12356         }
12357         var records = [];
12358         for(var i = 0; i < c; i++){
12359                 var n = root[i];
12360             var values = {};
12361             var id = this.getId(n);
12362             for(var j = 0; j < fl; j++){
12363                 f = fi[j];
12364             var v = this.ef[j](n);
12365             if (!f.convert) {
12366                 Roo.log('missing convert for ' + f.name);
12367                 Roo.log(f);
12368                 continue;
12369             }
12370             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
12371             }
12372             var record = new Record(values, id);
12373             record.json = n;
12374             records[i] = record;
12375         }
12376         return {
12377             raw : o,
12378             success : success,
12379             records : records,
12380             totalRecords : totalRecords
12381         };
12382     }
12383 });/*
12384  * Based on:
12385  * Ext JS Library 1.1.1
12386  * Copyright(c) 2006-2007, Ext JS, LLC.
12387  *
12388  * Originally Released Under LGPL - original licence link has changed is not relivant.
12389  *
12390  * Fork - LGPL
12391  * <script type="text/javascript">
12392  */
12393
12394 /**
12395  * @class Roo.data.ArrayReader
12396  * @extends Roo.data.DataReader
12397  * Data reader class to create an Array of Roo.data.Record objects from an Array.
12398  * Each element of that Array represents a row of data fields. The
12399  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
12400  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
12401  * <p>
12402  * Example code:.
12403  * <pre><code>
12404 var RecordDef = Roo.data.Record.create([
12405     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
12406     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
12407 ]);
12408 var myReader = new Roo.data.ArrayReader({
12409     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
12410 }, RecordDef);
12411 </code></pre>
12412  * <p>
12413  * This would consume an Array like this:
12414  * <pre><code>
12415 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
12416   </code></pre>
12417  * @cfg {String} id (optional) The subscript within row Array that provides an ID for the Record
12418  * @constructor
12419  * Create a new JsonReader
12420  * @param {Object} meta Metadata configuration options.
12421  * @param {Object} recordType Either an Array of field definition objects
12422  * as specified to {@link Roo.data.Record#create},
12423  * or an {@link Roo.data.Record} object
12424  * created using {@link Roo.data.Record#create}.
12425  */
12426 Roo.data.ArrayReader = function(meta, recordType){
12427     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType);
12428 };
12429
12430 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
12431     /**
12432      * Create a data block containing Roo.data.Records from an XML document.
12433      * @param {Object} o An Array of row objects which represents the dataset.
12434      * @return {Object} data A data block which is used by an Roo.data.Store object as
12435      * a cache of Roo.data.Records.
12436      */
12437     readRecords : function(o){
12438         var sid = this.meta ? this.meta.id : null;
12439         var recordType = this.recordType, fields = recordType.prototype.fields;
12440         var records = [];
12441         var root = o;
12442             for(var i = 0; i < root.length; i++){
12443                     var n = root[i];
12444                 var values = {};
12445                 var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
12446                 for(var j = 0, jlen = fields.length; j < jlen; j++){
12447                 var f = fields.items[j];
12448                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
12449                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
12450                 v = f.convert(v);
12451                 values[f.name] = v;
12452             }
12453                 var record = new recordType(values, id);
12454                 record.json = n;
12455                 records[records.length] = record;
12456             }
12457             return {
12458                 records : records,
12459                 totalRecords : records.length
12460             };
12461     }
12462 });/*
12463  * - LGPL
12464  * * 
12465  */
12466
12467 /**
12468  * @class Roo.bootstrap.ComboBox
12469  * @extends Roo.bootstrap.TriggerField
12470  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
12471  * @cfg {Boolean} append (true|false) default false
12472  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
12473  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
12474  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
12475  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
12476  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
12477  * @cfg {Boolean} animate default true
12478  * @cfg {Boolean} emptyResultText only for touch device
12479  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
12480  * @cfg {String} emptyTitle default ''
12481  * @constructor
12482  * Create a new ComboBox.
12483  * @param {Object} config Configuration options
12484  */
12485 Roo.bootstrap.ComboBox = function(config){
12486     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
12487     this.addEvents({
12488         /**
12489          * @event expand
12490          * Fires when the dropdown list is expanded
12491         * @param {Roo.bootstrap.ComboBox} combo This combo box
12492         */
12493         'expand' : true,
12494         /**
12495          * @event collapse
12496          * Fires when the dropdown list is collapsed
12497         * @param {Roo.bootstrap.ComboBox} combo This combo box
12498         */
12499         'collapse' : true,
12500         /**
12501          * @event beforeselect
12502          * Fires before a list item is selected. Return false to cancel the selection.
12503         * @param {Roo.bootstrap.ComboBox} combo This combo box
12504         * @param {Roo.data.Record} record The data record returned from the underlying store
12505         * @param {Number} index The index of the selected item in the dropdown list
12506         */
12507         'beforeselect' : true,
12508         /**
12509          * @event select
12510          * Fires when a list item is selected
12511         * @param {Roo.bootstrap.ComboBox} combo This combo box
12512         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
12513         * @param {Number} index The index of the selected item in the dropdown list
12514         */
12515         'select' : true,
12516         /**
12517          * @event beforequery
12518          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
12519          * The event object passed has these properties:
12520         * @param {Roo.bootstrap.ComboBox} combo This combo box
12521         * @param {String} query The query
12522         * @param {Boolean} forceAll true to force "all" query
12523         * @param {Boolean} cancel true to cancel the query
12524         * @param {Object} e The query event object
12525         */
12526         'beforequery': true,
12527          /**
12528          * @event add
12529          * Fires when the 'add' icon is pressed (add a listener to enable add button)
12530         * @param {Roo.bootstrap.ComboBox} combo This combo box
12531         */
12532         'add' : true,
12533         /**
12534          * @event edit
12535          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
12536         * @param {Roo.bootstrap.ComboBox} combo This combo box
12537         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
12538         */
12539         'edit' : true,
12540         /**
12541          * @event remove
12542          * Fires when the remove value from the combobox array
12543         * @param {Roo.bootstrap.ComboBox} combo This combo box
12544         */
12545         'remove' : true,
12546         /**
12547          * @event afterremove
12548          * Fires when the remove value from the combobox array
12549         * @param {Roo.bootstrap.ComboBox} combo This combo box
12550         */
12551         'afterremove' : true,
12552         /**
12553          * @event specialfilter
12554          * Fires when specialfilter
12555             * @param {Roo.bootstrap.ComboBox} combo This combo box
12556             */
12557         'specialfilter' : true,
12558         /**
12559          * @event tick
12560          * Fires when tick the element
12561             * @param {Roo.bootstrap.ComboBox} combo This combo box
12562             */
12563         'tick' : true,
12564         /**
12565          * @event touchviewdisplay
12566          * Fires when touch view require special display (default is using displayField)
12567             * @param {Roo.bootstrap.ComboBox} combo This combo box
12568             * @param {Object} cfg set html .
12569             */
12570         'touchviewdisplay' : true
12571         
12572     });
12573     
12574     this.item = [];
12575     this.tickItems = [];
12576     
12577     this.selectedIndex = -1;
12578     if(this.mode == 'local'){
12579         if(config.queryDelay === undefined){
12580             this.queryDelay = 10;
12581         }
12582         if(config.minChars === undefined){
12583             this.minChars = 0;
12584         }
12585     }
12586 };
12587
12588 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
12589      
12590     /**
12591      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
12592      * rendering into an Roo.Editor, defaults to false)
12593      */
12594     /**
12595      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
12596      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
12597      */
12598     /**
12599      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
12600      */
12601     /**
12602      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
12603      * the dropdown list (defaults to undefined, with no header element)
12604      */
12605
12606      /**
12607      * @cfg {String/Roo.Template} tpl The template to use to render the output
12608      */
12609      
12610      /**
12611      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
12612      */
12613     listWidth: undefined,
12614     /**
12615      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
12616      * mode = 'remote' or 'text' if mode = 'local')
12617      */
12618     displayField: undefined,
12619     
12620     /**
12621      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
12622      * mode = 'remote' or 'value' if mode = 'local'). 
12623      * Note: use of a valueField requires the user make a selection
12624      * in order for a value to be mapped.
12625      */
12626     valueField: undefined,
12627     /**
12628      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
12629      */
12630     modalTitle : '',
12631     
12632     /**
12633      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
12634      * field's data value (defaults to the underlying DOM element's name)
12635      */
12636     hiddenName: undefined,
12637     /**
12638      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
12639      */
12640     listClass: '',
12641     /**
12642      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
12643      */
12644     selectedClass: 'active',
12645     
12646     /**
12647      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
12648      */
12649     shadow:'sides',
12650     /**
12651      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
12652      * anchor positions (defaults to 'tl-bl')
12653      */
12654     listAlign: 'tl-bl?',
12655     /**
12656      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
12657      */
12658     maxHeight: 300,
12659     /**
12660      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
12661      * query specified by the allQuery config option (defaults to 'query')
12662      */
12663     triggerAction: 'query',
12664     /**
12665      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
12666      * (defaults to 4, does not apply if editable = false)
12667      */
12668     minChars : 4,
12669     /**
12670      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
12671      * delay (typeAheadDelay) if it matches a known value (defaults to false)
12672      */
12673     typeAhead: false,
12674     /**
12675      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
12676      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
12677      */
12678     queryDelay: 500,
12679     /**
12680      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
12681      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
12682      */
12683     pageSize: 0,
12684     /**
12685      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
12686      * when editable = true (defaults to false)
12687      */
12688     selectOnFocus:false,
12689     /**
12690      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
12691      */
12692     queryParam: 'query',
12693     /**
12694      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
12695      * when mode = 'remote' (defaults to 'Loading...')
12696      */
12697     loadingText: 'Loading...',
12698     /**
12699      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
12700      */
12701     resizable: false,
12702     /**
12703      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
12704      */
12705     handleHeight : 8,
12706     /**
12707      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
12708      * traditional select (defaults to true)
12709      */
12710     editable: true,
12711     /**
12712      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
12713      */
12714     allQuery: '',
12715     /**
12716      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
12717      */
12718     mode: 'remote',
12719     /**
12720      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
12721      * listWidth has a higher value)
12722      */
12723     minListWidth : 70,
12724     /**
12725      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
12726      * allow the user to set arbitrary text into the field (defaults to false)
12727      */
12728     forceSelection:false,
12729     /**
12730      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
12731      * if typeAhead = true (defaults to 250)
12732      */
12733     typeAheadDelay : 250,
12734     /**
12735      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
12736      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
12737      */
12738     valueNotFoundText : undefined,
12739     /**
12740      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
12741      */
12742     blockFocus : false,
12743     
12744     /**
12745      * @cfg {Boolean} disableClear Disable showing of clear button.
12746      */
12747     disableClear : false,
12748     /**
12749      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
12750      */
12751     alwaysQuery : false,
12752     
12753     /**
12754      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
12755      */
12756     multiple : false,
12757     
12758     /**
12759      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
12760      */
12761     invalidClass : "has-warning",
12762     
12763     /**
12764      * @cfg {String} validClass The CSS class to use when marking a field valid (defaults to "x-form-invalid")
12765      */
12766     validClass : "has-success",
12767     
12768     /**
12769      * @cfg {Boolean} specialFilter (true|false) special filter default false
12770      */
12771     specialFilter : false,
12772     
12773     /**
12774      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
12775      */
12776     mobileTouchView : true,
12777     
12778     /**
12779      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
12780      */
12781     useNativeIOS : false,
12782     
12783     ios_options : false,
12784     
12785     //private
12786     addicon : false,
12787     editicon: false,
12788     
12789     page: 0,
12790     hasQuery: false,
12791     append: false,
12792     loadNext: false,
12793     autoFocus : true,
12794     tickable : false,
12795     btnPosition : 'right',
12796     triggerList : true,
12797     showToggleBtn : true,
12798     animate : true,
12799     emptyResultText: 'Empty',
12800     triggerText : 'Select',
12801     emptyTitle : '',
12802     
12803     // element that contains real text value.. (when hidden is used..)
12804     
12805     getAutoCreate : function()
12806     {   
12807         var cfg = false;
12808         //render
12809         /*
12810          * Render classic select for iso
12811          */
12812         
12813         if(Roo.isIOS && this.useNativeIOS){
12814             cfg = this.getAutoCreateNativeIOS();
12815             return cfg;
12816         }
12817         
12818         /*
12819          * Touch Devices
12820          */
12821         
12822         if(Roo.isTouch && this.mobileTouchView){
12823             cfg = this.getAutoCreateTouchView();
12824             return cfg;;
12825         }
12826         
12827         /*
12828          *  Normal ComboBox
12829          */
12830         if(!this.tickable){
12831             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
12832             return cfg;
12833         }
12834         
12835         /*
12836          *  ComboBox with tickable selections
12837          */
12838              
12839         var align = this.labelAlign || this.parentLabelAlign();
12840         
12841         cfg = {
12842             cls : 'form-group roo-combobox-tickable' //input-group
12843         };
12844         
12845         var btn_text_select = '';
12846         var btn_text_done = '';
12847         var btn_text_cancel = '';
12848         
12849         if (this.btn_text_show) {
12850             btn_text_select = 'Select';
12851             btn_text_done = 'Done';
12852             btn_text_cancel = 'Cancel'; 
12853         }
12854         
12855         var buttons = {
12856             tag : 'div',
12857             cls : 'tickable-buttons',
12858             cn : [
12859                 {
12860                     tag : 'button',
12861                     type : 'button',
12862                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
12863                     //html : this.triggerText
12864                     html: btn_text_select
12865                 },
12866                 {
12867                     tag : 'button',
12868                     type : 'button',
12869                     name : 'ok',
12870                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
12871                     //html : 'Done'
12872                     html: btn_text_done
12873                 },
12874                 {
12875                     tag : 'button',
12876                     type : 'button',
12877                     name : 'cancel',
12878                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
12879                     //html : 'Cancel'
12880                     html: btn_text_cancel
12881                 }
12882             ]
12883         };
12884         
12885         if(this.editable){
12886             buttons.cn.unshift({
12887                 tag: 'input',
12888                 cls: 'roo-select2-search-field-input'
12889             });
12890         }
12891         
12892         var _this = this;
12893         
12894         Roo.each(buttons.cn, function(c){
12895             if (_this.size) {
12896                 c.cls += ' btn-' + _this.size;
12897             }
12898
12899             if (_this.disabled) {
12900                 c.disabled = true;
12901             }
12902         });
12903         
12904         var box = {
12905             tag: 'div',
12906             cn: [
12907                 {
12908                     tag: 'input',
12909                     type : 'hidden',
12910                     cls: 'form-hidden-field'
12911                 },
12912                 {
12913                     tag: 'ul',
12914                     cls: 'roo-select2-choices',
12915                     cn:[
12916                         {
12917                             tag: 'li',
12918                             cls: 'roo-select2-search-field',
12919                             cn: [
12920                                 buttons
12921                             ]
12922                         }
12923                     ]
12924                 }
12925             ]
12926         };
12927         
12928         var combobox = {
12929             cls: 'roo-select2-container input-group roo-select2-container-multi',
12930             cn: [
12931                 box
12932 //                {
12933 //                    tag: 'ul',
12934 //                    cls: 'typeahead typeahead-long dropdown-menu',
12935 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
12936 //                }
12937             ]
12938         };
12939         
12940         if(this.hasFeedback && !this.allowBlank){
12941             
12942             var feedback = {
12943                 tag: 'span',
12944                 cls: 'glyphicon form-control-feedback'
12945             };
12946
12947             combobox.cn.push(feedback);
12948         }
12949         
12950         
12951         if (align ==='left' && this.fieldLabel.length) {
12952             
12953             cfg.cls += ' roo-form-group-label-left';
12954             
12955             cfg.cn = [
12956                 {
12957                     tag : 'i',
12958                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12959                     tooltip : 'This field is required'
12960                 },
12961                 {
12962                     tag: 'label',
12963                     'for' :  id,
12964                     cls : 'control-label',
12965                     html : this.fieldLabel
12966
12967                 },
12968                 {
12969                     cls : "", 
12970                     cn: [
12971                         combobox
12972                     ]
12973                 }
12974
12975             ];
12976             
12977             var labelCfg = cfg.cn[1];
12978             var contentCfg = cfg.cn[2];
12979             
12980
12981             if(this.indicatorpos == 'right'){
12982                 
12983                 cfg.cn = [
12984                     {
12985                         tag: 'label',
12986                         'for' :  id,
12987                         cls : 'control-label',
12988                         cn : [
12989                             {
12990                                 tag : 'span',
12991                                 html : this.fieldLabel
12992                             },
12993                             {
12994                                 tag : 'i',
12995                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12996                                 tooltip : 'This field is required'
12997                             }
12998                         ]
12999                     },
13000                     {
13001                         cls : "",
13002                         cn: [
13003                             combobox
13004                         ]
13005                     }
13006
13007                 ];
13008                 
13009                 
13010                 
13011                 labelCfg = cfg.cn[0];
13012                 contentCfg = cfg.cn[1];
13013             
13014             }
13015             
13016             if(this.labelWidth > 12){
13017                 labelCfg.style = "width: " + this.labelWidth + 'px';
13018             }
13019             
13020             if(this.labelWidth < 13 && this.labelmd == 0){
13021                 this.labelmd = this.labelWidth;
13022             }
13023             
13024             if(this.labellg > 0){
13025                 labelCfg.cls += ' col-lg-' + this.labellg;
13026                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13027             }
13028             
13029             if(this.labelmd > 0){
13030                 labelCfg.cls += ' col-md-' + this.labelmd;
13031                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13032             }
13033             
13034             if(this.labelsm > 0){
13035                 labelCfg.cls += ' col-sm-' + this.labelsm;
13036                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13037             }
13038             
13039             if(this.labelxs > 0){
13040                 labelCfg.cls += ' col-xs-' + this.labelxs;
13041                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13042             }
13043                 
13044                 
13045         } else if ( this.fieldLabel.length) {
13046 //                Roo.log(" label");
13047                  cfg.cn = [
13048                     {
13049                         tag : 'i',
13050                         cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
13051                         tooltip : 'This field is required'
13052                     },
13053                     {
13054                         tag: 'label',
13055                         //cls : 'input-group-addon',
13056                         html : this.fieldLabel
13057                     },
13058                     combobox
13059                 ];
13060                 
13061                 if(this.indicatorpos == 'right'){
13062                     cfg.cn = [
13063                         {
13064                             tag: 'label',
13065                             //cls : 'input-group-addon',
13066                             html : this.fieldLabel
13067                         },
13068                         {
13069                             tag : 'i',
13070                             cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
13071                             tooltip : 'This field is required'
13072                         },
13073                         combobox
13074                     ];
13075                     
13076                 }
13077
13078         } else {
13079             
13080 //                Roo.log(" no label && no align");
13081                 cfg = combobox
13082                      
13083                 
13084         }
13085          
13086         var settings=this;
13087         ['xs','sm','md','lg'].map(function(size){
13088             if (settings[size]) {
13089                 cfg.cls += ' col-' + size + '-' + settings[size];
13090             }
13091         });
13092         
13093         return cfg;
13094         
13095     },
13096     
13097     _initEventsCalled : false,
13098     
13099     // private
13100     initEvents: function()
13101     {   
13102         if (this._initEventsCalled) { // as we call render... prevent looping...
13103             return;
13104         }
13105         this._initEventsCalled = true;
13106         
13107         if (!this.store) {
13108             throw "can not find store for combo";
13109         }
13110         
13111         this.indicator = this.indicatorEl();
13112         
13113         this.store = Roo.factory(this.store, Roo.data);
13114         this.store.parent = this;
13115         
13116         // if we are building from html. then this element is so complex, that we can not really
13117         // use the rendered HTML.
13118         // so we have to trash and replace the previous code.
13119         if (Roo.XComponent.build_from_html) {
13120             // remove this element....
13121             var e = this.el.dom, k=0;
13122             while (e ) { e = e.previousSibling;  ++k;}
13123
13124             this.el.remove();
13125             
13126             this.el=false;
13127             this.rendered = false;
13128             
13129             this.render(this.parent().getChildContainer(true), k);
13130         }
13131         
13132         if(Roo.isIOS && this.useNativeIOS){
13133             this.initIOSView();
13134             return;
13135         }
13136         
13137         /*
13138          * Touch Devices
13139          */
13140         
13141         if(Roo.isTouch && this.mobileTouchView){
13142             this.initTouchView();
13143             return;
13144         }
13145         
13146         if(this.tickable){
13147             this.initTickableEvents();
13148             return;
13149         }
13150         
13151         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
13152         
13153         if(this.hiddenName){
13154             
13155             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13156             
13157             this.hiddenField.dom.value =
13158                 this.hiddenValue !== undefined ? this.hiddenValue :
13159                 this.value !== undefined ? this.value : '';
13160
13161             // prevent input submission
13162             this.el.dom.removeAttribute('name');
13163             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13164              
13165              
13166         }
13167         //if(Roo.isGecko){
13168         //    this.el.dom.setAttribute('autocomplete', 'off');
13169         //}
13170         
13171         var cls = 'x-combo-list';
13172         
13173         //this.list = new Roo.Layer({
13174         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
13175         //});
13176         
13177         var _this = this;
13178         
13179         (function(){
13180             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13181             _this.list.setWidth(lw);
13182         }).defer(100);
13183         
13184         this.list.on('mouseover', this.onViewOver, this);
13185         this.list.on('mousemove', this.onViewMove, this);
13186         this.list.on('scroll', this.onViewScroll, this);
13187         
13188         /*
13189         this.list.swallowEvent('mousewheel');
13190         this.assetHeight = 0;
13191
13192         if(this.title){
13193             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
13194             this.assetHeight += this.header.getHeight();
13195         }
13196
13197         this.innerList = this.list.createChild({cls:cls+'-inner'});
13198         this.innerList.on('mouseover', this.onViewOver, this);
13199         this.innerList.on('mousemove', this.onViewMove, this);
13200         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13201         
13202         if(this.allowBlank && !this.pageSize && !this.disableClear){
13203             this.footer = this.list.createChild({cls:cls+'-ft'});
13204             this.pageTb = new Roo.Toolbar(this.footer);
13205            
13206         }
13207         if(this.pageSize){
13208             this.footer = this.list.createChild({cls:cls+'-ft'});
13209             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
13210                     {pageSize: this.pageSize});
13211             
13212         }
13213         
13214         if (this.pageTb && this.allowBlank && !this.disableClear) {
13215             var _this = this;
13216             this.pageTb.add(new Roo.Toolbar.Fill(), {
13217                 cls: 'x-btn-icon x-btn-clear',
13218                 text: '&#160;',
13219                 handler: function()
13220                 {
13221                     _this.collapse();
13222                     _this.clearValue();
13223                     _this.onSelect(false, -1);
13224                 }
13225             });
13226         }
13227         if (this.footer) {
13228             this.assetHeight += this.footer.getHeight();
13229         }
13230         */
13231             
13232         if(!this.tpl){
13233             this.tpl = '<li><a href="#">{' + this.displayField + '}</a></li>';
13234         }
13235
13236         this.view = new Roo.View(this.list, this.tpl, {
13237             singleSelect:true, store: this.store, selectedClass: this.selectedClass
13238         });
13239         //this.view.wrapEl.setDisplayed(false);
13240         this.view.on('click', this.onViewClick, this);
13241         
13242         
13243         this.store.on('beforeload', this.onBeforeLoad, this);
13244         this.store.on('load', this.onLoad, this);
13245         this.store.on('loadexception', this.onLoadException, this);
13246         /*
13247         if(this.resizable){
13248             this.resizer = new Roo.Resizable(this.list,  {
13249                pinned:true, handles:'se'
13250             });
13251             this.resizer.on('resize', function(r, w, h){
13252                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
13253                 this.listWidth = w;
13254                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
13255                 this.restrictHeight();
13256             }, this);
13257             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
13258         }
13259         */
13260         if(!this.editable){
13261             this.editable = true;
13262             this.setEditable(false);
13263         }
13264         
13265         /*
13266         
13267         if (typeof(this.events.add.listeners) != 'undefined') {
13268             
13269             this.addicon = this.wrap.createChild(
13270                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
13271        
13272             this.addicon.on('click', function(e) {
13273                 this.fireEvent('add', this);
13274             }, this);
13275         }
13276         if (typeof(this.events.edit.listeners) != 'undefined') {
13277             
13278             this.editicon = this.wrap.createChild(
13279                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
13280             if (this.addicon) {
13281                 this.editicon.setStyle('margin-left', '40px');
13282             }
13283             this.editicon.on('click', function(e) {
13284                 
13285                 // we fire even  if inothing is selected..
13286                 this.fireEvent('edit', this, this.lastData );
13287                 
13288             }, this);
13289         }
13290         */
13291         
13292         this.keyNav = new Roo.KeyNav(this.inputEl(), {
13293             "up" : function(e){
13294                 this.inKeyMode = true;
13295                 this.selectPrev();
13296             },
13297
13298             "down" : function(e){
13299                 if(!this.isExpanded()){
13300                     this.onTriggerClick();
13301                 }else{
13302                     this.inKeyMode = true;
13303                     this.selectNext();
13304                 }
13305             },
13306
13307             "enter" : function(e){
13308 //                this.onViewClick();
13309                 //return true;
13310                 this.collapse();
13311                 
13312                 if(this.fireEvent("specialkey", this, e)){
13313                     this.onViewClick(false);
13314                 }
13315                 
13316                 return true;
13317             },
13318
13319             "esc" : function(e){
13320                 this.collapse();
13321             },
13322
13323             "tab" : function(e){
13324                 this.collapse();
13325                 
13326                 if(this.fireEvent("specialkey", this, e)){
13327                     this.onViewClick(false);
13328                 }
13329                 
13330                 return true;
13331             },
13332
13333             scope : this,
13334
13335             doRelay : function(foo, bar, hname){
13336                 if(hname == 'down' || this.scope.isExpanded()){
13337                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13338                 }
13339                 return true;
13340             },
13341
13342             forceKeyDown: true
13343         });
13344         
13345         
13346         this.queryDelay = Math.max(this.queryDelay || 10,
13347                 this.mode == 'local' ? 10 : 250);
13348         
13349         
13350         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13351         
13352         if(this.typeAhead){
13353             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13354         }
13355         if(this.editable !== false){
13356             this.inputEl().on("keyup", this.onKeyUp, this);
13357         }
13358         if(this.forceSelection){
13359             this.inputEl().on('blur', this.doForce, this);
13360         }
13361         
13362         if(this.multiple){
13363             this.choices = this.el.select('ul.roo-select2-choices', true).first();
13364             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13365         }
13366     },
13367     
13368     initTickableEvents: function()
13369     {   
13370         this.createList();
13371         
13372         if(this.hiddenName){
13373             
13374             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
13375             
13376             this.hiddenField.dom.value =
13377                 this.hiddenValue !== undefined ? this.hiddenValue :
13378                 this.value !== undefined ? this.value : '';
13379
13380             // prevent input submission
13381             this.el.dom.removeAttribute('name');
13382             this.hiddenField.dom.setAttribute('name', this.hiddenName);
13383              
13384              
13385         }
13386         
13387 //        this.list = this.el.select('ul.dropdown-menu',true).first();
13388         
13389         this.choices = this.el.select('ul.roo-select2-choices', true).first();
13390         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
13391         if(this.triggerList){
13392             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
13393         }
13394          
13395         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
13396         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
13397         
13398         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
13399         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
13400         
13401         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
13402         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
13403         
13404         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
13405         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
13406         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
13407         
13408         this.okBtn.hide();
13409         this.cancelBtn.hide();
13410         
13411         var _this = this;
13412         
13413         (function(){
13414             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
13415             _this.list.setWidth(lw);
13416         }).defer(100);
13417         
13418         this.list.on('mouseover', this.onViewOver, this);
13419         this.list.on('mousemove', this.onViewMove, this);
13420         
13421         this.list.on('scroll', this.onViewScroll, this);
13422         
13423         if(!this.tpl){
13424             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>';
13425         }
13426
13427         this.view = new Roo.View(this.list, this.tpl, {
13428             singleSelect:true, tickable:true, parent:this, store: this.store, selectedClass: this.selectedClass
13429         });
13430         
13431         //this.view.wrapEl.setDisplayed(false);
13432         this.view.on('click', this.onViewClick, this);
13433         
13434         
13435         
13436         this.store.on('beforeload', this.onBeforeLoad, this);
13437         this.store.on('load', this.onLoad, this);
13438         this.store.on('loadexception', this.onLoadException, this);
13439         
13440         if(this.editable){
13441             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
13442                 "up" : function(e){
13443                     this.inKeyMode = true;
13444                     this.selectPrev();
13445                 },
13446
13447                 "down" : function(e){
13448                     this.inKeyMode = true;
13449                     this.selectNext();
13450                 },
13451
13452                 "enter" : function(e){
13453                     if(this.fireEvent("specialkey", this, e)){
13454                         this.onViewClick(false);
13455                     }
13456                     
13457                     return true;
13458                 },
13459
13460                 "esc" : function(e){
13461                     this.onTickableFooterButtonClick(e, false, false);
13462                 },
13463
13464                 "tab" : function(e){
13465                     this.fireEvent("specialkey", this, e);
13466                     
13467                     this.onTickableFooterButtonClick(e, false, false);
13468                     
13469                     return true;
13470                 },
13471
13472                 scope : this,
13473
13474                 doRelay : function(e, fn, key){
13475                     if(this.scope.isExpanded()){
13476                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
13477                     }
13478                     return true;
13479                 },
13480
13481                 forceKeyDown: true
13482             });
13483         }
13484         
13485         this.queryDelay = Math.max(this.queryDelay || 10,
13486                 this.mode == 'local' ? 10 : 250);
13487         
13488         
13489         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
13490         
13491         if(this.typeAhead){
13492             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
13493         }
13494         
13495         if(this.editable !== false){
13496             this.tickableInputEl().on("keyup", this.onKeyUp, this);
13497         }
13498         
13499         this.indicator = this.indicatorEl();
13500         
13501         if(this.indicator){
13502             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
13503             this.indicator.hide();
13504         }
13505         
13506     },
13507
13508     onDestroy : function(){
13509         if(this.view){
13510             this.view.setStore(null);
13511             this.view.el.removeAllListeners();
13512             this.view.el.remove();
13513             this.view.purgeListeners();
13514         }
13515         if(this.list){
13516             this.list.dom.innerHTML  = '';
13517         }
13518         
13519         if(this.store){
13520             this.store.un('beforeload', this.onBeforeLoad, this);
13521             this.store.un('load', this.onLoad, this);
13522             this.store.un('loadexception', this.onLoadException, this);
13523         }
13524         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
13525     },
13526
13527     // private
13528     fireKey : function(e){
13529         if(e.isNavKeyPress() && !this.list.isVisible()){
13530             this.fireEvent("specialkey", this, e);
13531         }
13532     },
13533
13534     // private
13535     onResize: function(w, h){
13536 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
13537 //        
13538 //        if(typeof w != 'number'){
13539 //            // we do not handle it!?!?
13540 //            return;
13541 //        }
13542 //        var tw = this.trigger.getWidth();
13543 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
13544 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
13545 //        var x = w - tw;
13546 //        this.inputEl().setWidth( this.adjustWidth('input', x));
13547 //            
13548 //        //this.trigger.setStyle('left', x+'px');
13549 //        
13550 //        if(this.list && this.listWidth === undefined){
13551 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
13552 //            this.list.setWidth(lw);
13553 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
13554 //        }
13555         
13556     
13557         
13558     },
13559
13560     /**
13561      * Allow or prevent the user from directly editing the field text.  If false is passed,
13562      * the user will only be able to select from the items defined in the dropdown list.  This method
13563      * is the runtime equivalent of setting the 'editable' config option at config time.
13564      * @param {Boolean} value True to allow the user to directly edit the field text
13565      */
13566     setEditable : function(value){
13567         if(value == this.editable){
13568             return;
13569         }
13570         this.editable = value;
13571         if(!value){
13572             this.inputEl().dom.setAttribute('readOnly', true);
13573             this.inputEl().on('mousedown', this.onTriggerClick,  this);
13574             this.inputEl().addClass('x-combo-noedit');
13575         }else{
13576             this.inputEl().dom.setAttribute('readOnly', false);
13577             this.inputEl().un('mousedown', this.onTriggerClick,  this);
13578             this.inputEl().removeClass('x-combo-noedit');
13579         }
13580     },
13581
13582     // private
13583     
13584     onBeforeLoad : function(combo,opts){
13585         if(!this.hasFocus){
13586             return;
13587         }
13588          if (!opts.add) {
13589             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
13590          }
13591         this.restrictHeight();
13592         this.selectedIndex = -1;
13593     },
13594
13595     // private
13596     onLoad : function(){
13597         
13598         this.hasQuery = false;
13599         
13600         if(!this.hasFocus){
13601             return;
13602         }
13603         
13604         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13605             this.loading.hide();
13606         }
13607         
13608         if(this.store.getCount() > 0){
13609             
13610             this.expand();
13611             this.restrictHeight();
13612             if(this.lastQuery == this.allQuery){
13613                 if(this.editable && !this.tickable){
13614                     this.inputEl().dom.select();
13615                 }
13616                 
13617                 if(
13618                     !this.selectByValue(this.value, true) &&
13619                     this.autoFocus && 
13620                     (
13621                         !this.store.lastOptions ||
13622                         typeof(this.store.lastOptions.add) == 'undefined' || 
13623                         this.store.lastOptions.add != true
13624                     )
13625                 ){
13626                     this.select(0, true);
13627                 }
13628             }else{
13629                 if(this.autoFocus){
13630                     this.selectNext();
13631                 }
13632                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
13633                     this.taTask.delay(this.typeAheadDelay);
13634                 }
13635             }
13636         }else{
13637             this.onEmptyResults();
13638         }
13639         
13640         //this.el.focus();
13641     },
13642     // private
13643     onLoadException : function()
13644     {
13645         this.hasQuery = false;
13646         
13647         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
13648             this.loading.hide();
13649         }
13650         
13651         if(this.tickable && this.editable){
13652             return;
13653         }
13654         
13655         this.collapse();
13656         // only causes errors at present
13657         //Roo.log(this.store.reader.jsonData);
13658         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
13659             // fixme
13660             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
13661         //}
13662         
13663         
13664     },
13665     // private
13666     onTypeAhead : function(){
13667         if(this.store.getCount() > 0){
13668             var r = this.store.getAt(0);
13669             var newValue = r.data[this.displayField];
13670             var len = newValue.length;
13671             var selStart = this.getRawValue().length;
13672             
13673             if(selStart != len){
13674                 this.setRawValue(newValue);
13675                 this.selectText(selStart, newValue.length);
13676             }
13677         }
13678     },
13679
13680     // private
13681     onSelect : function(record, index){
13682         
13683         if(this.fireEvent('beforeselect', this, record, index) !== false){
13684         
13685             this.setFromData(index > -1 ? record.data : false);
13686             
13687             this.collapse();
13688             this.fireEvent('select', this, record, index);
13689         }
13690     },
13691
13692     /**
13693      * Returns the currently selected field value or empty string if no value is set.
13694      * @return {String} value The selected value
13695      */
13696     getValue : function()
13697     {
13698         if(Roo.isIOS && this.useNativeIOS){
13699             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
13700         }
13701         
13702         if(this.multiple){
13703             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
13704         }
13705         
13706         if(this.valueField){
13707             return typeof this.value != 'undefined' ? this.value : '';
13708         }else{
13709             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
13710         }
13711     },
13712     
13713     getRawValue : function()
13714     {
13715         if(Roo.isIOS && this.useNativeIOS){
13716             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
13717         }
13718         
13719         var v = this.inputEl().getValue();
13720         
13721         return v;
13722     },
13723
13724     /**
13725      * Clears any text/value currently set in the field
13726      */
13727     clearValue : function(){
13728         
13729         if(this.hiddenField){
13730             this.hiddenField.dom.value = '';
13731         }
13732         this.value = '';
13733         this.setRawValue('');
13734         this.lastSelectionText = '';
13735         this.lastData = false;
13736         
13737         var close = this.closeTriggerEl();
13738         
13739         if(close){
13740             close.hide();
13741         }
13742         
13743         this.validate();
13744         
13745     },
13746
13747     /**
13748      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
13749      * will be displayed in the field.  If the value does not match the data value of an existing item,
13750      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
13751      * Otherwise the field will be blank (although the value will still be set).
13752      * @param {String} value The value to match
13753      */
13754     setValue : function(v)
13755     {
13756         if(Roo.isIOS && this.useNativeIOS){
13757             this.setIOSValue(v);
13758             return;
13759         }
13760         
13761         if(this.multiple){
13762             this.syncValue();
13763             return;
13764         }
13765         
13766         var text = v;
13767         if(this.valueField){
13768             var r = this.findRecord(this.valueField, v);
13769             if(r){
13770                 text = r.data[this.displayField];
13771             }else if(this.valueNotFoundText !== undefined){
13772                 text = this.valueNotFoundText;
13773             }
13774         }
13775         this.lastSelectionText = text;
13776         if(this.hiddenField){
13777             this.hiddenField.dom.value = v;
13778         }
13779         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
13780         this.value = v;
13781         
13782         var close = this.closeTriggerEl();
13783         
13784         if(close){
13785             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
13786         }
13787         
13788         this.validate();
13789     },
13790     /**
13791      * @property {Object} the last set data for the element
13792      */
13793     
13794     lastData : false,
13795     /**
13796      * Sets the value of the field based on a object which is related to the record format for the store.
13797      * @param {Object} value the value to set as. or false on reset?
13798      */
13799     setFromData : function(o){
13800         
13801         if(this.multiple){
13802             this.addItem(o);
13803             return;
13804         }
13805             
13806         var dv = ''; // display value
13807         var vv = ''; // value value..
13808         this.lastData = o;
13809         if (this.displayField) {
13810             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
13811         } else {
13812             // this is an error condition!!!
13813             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
13814         }
13815         
13816         if(this.valueField){
13817             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
13818         }
13819         
13820         var close = this.closeTriggerEl();
13821         
13822         if(close){
13823             if(dv.length || vv * 1 > 0){
13824                 close.show() ;
13825                 this.blockFocus=true;
13826             } else {
13827                 close.hide();
13828             }             
13829         }
13830         
13831         if(this.hiddenField){
13832             this.hiddenField.dom.value = vv;
13833             
13834             this.lastSelectionText = dv;
13835             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13836             this.value = vv;
13837             return;
13838         }
13839         // no hidden field.. - we store the value in 'value', but still display
13840         // display field!!!!
13841         this.lastSelectionText = dv;
13842         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
13843         this.value = vv;
13844         
13845         
13846         
13847     },
13848     // private
13849     reset : function(){
13850         // overridden so that last data is reset..
13851         
13852         if(this.multiple){
13853             this.clearItem();
13854             return;
13855         }
13856         
13857         this.setValue(this.originalValue);
13858         //this.clearInvalid();
13859         this.lastData = false;
13860         if (this.view) {
13861             this.view.clearSelections();
13862         }
13863         
13864         this.validate();
13865     },
13866     // private
13867     findRecord : function(prop, value){
13868         var record;
13869         if(this.store.getCount() > 0){
13870             this.store.each(function(r){
13871                 if(r.data[prop] == value){
13872                     record = r;
13873                     return false;
13874                 }
13875                 return true;
13876             });
13877         }
13878         return record;
13879     },
13880     
13881     getName: function()
13882     {
13883         // returns hidden if it's set..
13884         if (!this.rendered) {return ''};
13885         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
13886         
13887     },
13888     // private
13889     onViewMove : function(e, t){
13890         this.inKeyMode = false;
13891     },
13892
13893     // private
13894     onViewOver : function(e, t){
13895         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
13896             return;
13897         }
13898         var item = this.view.findItemFromChild(t);
13899         
13900         if(item){
13901             var index = this.view.indexOf(item);
13902             this.select(index, false);
13903         }
13904     },
13905
13906     // private
13907     onViewClick : function(view, doFocus, el, e)
13908     {
13909         var index = this.view.getSelectedIndexes()[0];
13910         
13911         var r = this.store.getAt(index);
13912         
13913         if(this.tickable){
13914             
13915             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
13916                 return;
13917             }
13918             
13919             var rm = false;
13920             var _this = this;
13921             
13922             Roo.each(this.tickItems, function(v,k){
13923                 
13924                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
13925                     Roo.log(v);
13926                     _this.tickItems.splice(k, 1);
13927                     
13928                     if(typeof(e) == 'undefined' && view == false){
13929                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
13930                     }
13931                     
13932                     rm = true;
13933                     return;
13934                 }
13935             });
13936             
13937             if(rm){
13938                 return;
13939             }
13940             
13941             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
13942                 this.tickItems.push(r.data);
13943             }
13944             
13945             if(typeof(e) == 'undefined' && view == false){
13946                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
13947             }
13948                     
13949             return;
13950         }
13951         
13952         if(r){
13953             this.onSelect(r, index);
13954         }
13955         if(doFocus !== false && !this.blockFocus){
13956             this.inputEl().focus();
13957         }
13958     },
13959
13960     // private
13961     restrictHeight : function(){
13962         //this.innerList.dom.style.height = '';
13963         //var inner = this.innerList.dom;
13964         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
13965         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
13966         //this.list.beginUpdate();
13967         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
13968         this.list.alignTo(this.inputEl(), this.listAlign);
13969         this.list.alignTo(this.inputEl(), this.listAlign);
13970         //this.list.endUpdate();
13971     },
13972
13973     // private
13974     onEmptyResults : function(){
13975         
13976         if(this.tickable && this.editable){
13977             this.restrictHeight();
13978             return;
13979         }
13980         
13981         this.collapse();
13982     },
13983
13984     /**
13985      * Returns true if the dropdown list is expanded, else false.
13986      */
13987     isExpanded : function(){
13988         return this.list.isVisible();
13989     },
13990
13991     /**
13992      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
13993      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
13994      * @param {String} value The data value of the item to select
13995      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
13996      * selected item if it is not currently in view (defaults to true)
13997      * @return {Boolean} True if the value matched an item in the list, else false
13998      */
13999     selectByValue : function(v, scrollIntoView){
14000         if(v !== undefined && v !== null){
14001             var r = this.findRecord(this.valueField || this.displayField, v);
14002             if(r){
14003                 this.select(this.store.indexOf(r), scrollIntoView);
14004                 return true;
14005             }
14006         }
14007         return false;
14008     },
14009
14010     /**
14011      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
14012      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
14013      * @param {Number} index The zero-based index of the list item to select
14014      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
14015      * selected item if it is not currently in view (defaults to true)
14016      */
14017     select : function(index, scrollIntoView){
14018         this.selectedIndex = index;
14019         this.view.select(index);
14020         if(scrollIntoView !== false){
14021             var el = this.view.getNode(index);
14022             /*
14023              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
14024              */
14025             if(el){
14026                 this.list.scrollChildIntoView(el, false);
14027             }
14028         }
14029     },
14030
14031     // private
14032     selectNext : function(){
14033         var ct = this.store.getCount();
14034         if(ct > 0){
14035             if(this.selectedIndex == -1){
14036                 this.select(0);
14037             }else if(this.selectedIndex < ct-1){
14038                 this.select(this.selectedIndex+1);
14039             }
14040         }
14041     },
14042
14043     // private
14044     selectPrev : function(){
14045         var ct = this.store.getCount();
14046         if(ct > 0){
14047             if(this.selectedIndex == -1){
14048                 this.select(0);
14049             }else if(this.selectedIndex != 0){
14050                 this.select(this.selectedIndex-1);
14051             }
14052         }
14053     },
14054
14055     // private
14056     onKeyUp : function(e){
14057         if(this.editable !== false && !e.isSpecialKey()){
14058             this.lastKey = e.getKey();
14059             this.dqTask.delay(this.queryDelay);
14060         }
14061     },
14062
14063     // private
14064     validateBlur : function(){
14065         return !this.list || !this.list.isVisible();   
14066     },
14067
14068     // private
14069     initQuery : function(){
14070         
14071         var v = this.getRawValue();
14072         
14073         if(this.tickable && this.editable){
14074             v = this.tickableInputEl().getValue();
14075         }
14076         
14077         this.doQuery(v);
14078     },
14079
14080     // private
14081     doForce : function(){
14082         if(this.inputEl().dom.value.length > 0){
14083             this.inputEl().dom.value =
14084                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
14085              
14086         }
14087     },
14088
14089     /**
14090      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
14091      * query allowing the query action to be canceled if needed.
14092      * @param {String} query The SQL query to execute
14093      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
14094      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
14095      * saved in the current store (defaults to false)
14096      */
14097     doQuery : function(q, forceAll){
14098         
14099         if(q === undefined || q === null){
14100             q = '';
14101         }
14102         var qe = {
14103             query: q,
14104             forceAll: forceAll,
14105             combo: this,
14106             cancel:false
14107         };
14108         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
14109             return false;
14110         }
14111         q = qe.query;
14112         
14113         forceAll = qe.forceAll;
14114         if(forceAll === true || (q.length >= this.minChars)){
14115             
14116             this.hasQuery = true;
14117             
14118             if(this.lastQuery != q || this.alwaysQuery){
14119                 this.lastQuery = q;
14120                 if(this.mode == 'local'){
14121                     this.selectedIndex = -1;
14122                     if(forceAll){
14123                         this.store.clearFilter();
14124                     }else{
14125                         
14126                         if(this.specialFilter){
14127                             this.fireEvent('specialfilter', this);
14128                             this.onLoad();
14129                             return;
14130                         }
14131                         
14132                         this.store.filter(this.displayField, q);
14133                     }
14134                     
14135                     this.store.fireEvent("datachanged", this.store);
14136                     
14137                     this.onLoad();
14138                     
14139                     
14140                 }else{
14141                     
14142                     this.store.baseParams[this.queryParam] = q;
14143                     
14144                     var options = {params : this.getParams(q)};
14145                     
14146                     if(this.loadNext){
14147                         options.add = true;
14148                         options.params.start = this.page * this.pageSize;
14149                     }
14150                     
14151                     this.store.load(options);
14152                     
14153                     /*
14154                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
14155                      *  we should expand the list on onLoad
14156                      *  so command out it
14157                      */
14158 //                    this.expand();
14159                 }
14160             }else{
14161                 this.selectedIndex = -1;
14162                 this.onLoad();   
14163             }
14164         }
14165         
14166         this.loadNext = false;
14167     },
14168     
14169     // private
14170     getParams : function(q){
14171         var p = {};
14172         //p[this.queryParam] = q;
14173         
14174         if(this.pageSize){
14175             p.start = 0;
14176             p.limit = this.pageSize;
14177         }
14178         return p;
14179     },
14180
14181     /**
14182      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
14183      */
14184     collapse : function(){
14185         if(!this.isExpanded()){
14186             return;
14187         }
14188         
14189         this.list.hide();
14190         
14191         this.hasFocus = false;
14192         
14193         if(this.tickable){
14194             this.okBtn.hide();
14195             this.cancelBtn.hide();
14196             this.trigger.show();
14197             
14198             if(this.editable){
14199                 this.tickableInputEl().dom.value = '';
14200                 this.tickableInputEl().blur();
14201             }
14202             
14203         }
14204         
14205         Roo.get(document).un('mousedown', this.collapseIf, this);
14206         Roo.get(document).un('mousewheel', this.collapseIf, this);
14207         if (!this.editable) {
14208             Roo.get(document).un('keydown', this.listKeyPress, this);
14209         }
14210         this.fireEvent('collapse', this);
14211         
14212         this.validate();
14213     },
14214
14215     // private
14216     collapseIf : function(e){
14217         var in_combo  = e.within(this.el);
14218         var in_list =  e.within(this.list);
14219         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
14220         
14221         if (in_combo || in_list || is_list) {
14222             //e.stopPropagation();
14223             return;
14224         }
14225         
14226         if(this.tickable){
14227             this.onTickableFooterButtonClick(e, false, false);
14228         }
14229
14230         this.collapse();
14231         
14232     },
14233
14234     /**
14235      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
14236      */
14237     expand : function(){
14238        
14239         if(this.isExpanded() || !this.hasFocus){
14240             return;
14241         }
14242         
14243         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
14244         this.list.setWidth(lw);
14245         
14246         Roo.log('expand');
14247         
14248         this.list.show();
14249         
14250         this.restrictHeight();
14251         
14252         if(this.tickable){
14253             
14254             this.tickItems = Roo.apply([], this.item);
14255             
14256             this.okBtn.show();
14257             this.cancelBtn.show();
14258             this.trigger.hide();
14259             
14260             if(this.editable){
14261                 this.tickableInputEl().focus();
14262             }
14263             
14264         }
14265         
14266         Roo.get(document).on('mousedown', this.collapseIf, this);
14267         Roo.get(document).on('mousewheel', this.collapseIf, this);
14268         if (!this.editable) {
14269             Roo.get(document).on('keydown', this.listKeyPress, this);
14270         }
14271         
14272         this.fireEvent('expand', this);
14273     },
14274
14275     // private
14276     // Implements the default empty TriggerField.onTriggerClick function
14277     onTriggerClick : function(e)
14278     {
14279         Roo.log('trigger click');
14280         
14281         if(this.disabled || !this.triggerList){
14282             return;
14283         }
14284         
14285         this.page = 0;
14286         this.loadNext = false;
14287         
14288         if(this.isExpanded()){
14289             this.collapse();
14290             if (!this.blockFocus) {
14291                 this.inputEl().focus();
14292             }
14293             
14294         }else {
14295             this.hasFocus = true;
14296             if(this.triggerAction == 'all') {
14297                 this.doQuery(this.allQuery, true);
14298             } else {
14299                 this.doQuery(this.getRawValue());
14300             }
14301             if (!this.blockFocus) {
14302                 this.inputEl().focus();
14303             }
14304         }
14305     },
14306     
14307     onTickableTriggerClick : function(e)
14308     {
14309         if(this.disabled){
14310             return;
14311         }
14312         
14313         this.page = 0;
14314         this.loadNext = false;
14315         this.hasFocus = true;
14316         
14317         if(this.triggerAction == 'all') {
14318             this.doQuery(this.allQuery, true);
14319         } else {
14320             this.doQuery(this.getRawValue());
14321         }
14322     },
14323     
14324     onSearchFieldClick : function(e)
14325     {
14326         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
14327             this.onTickableFooterButtonClick(e, false, false);
14328             return;
14329         }
14330         
14331         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
14332             return;
14333         }
14334         
14335         this.page = 0;
14336         this.loadNext = false;
14337         this.hasFocus = true;
14338         
14339         if(this.triggerAction == 'all') {
14340             this.doQuery(this.allQuery, true);
14341         } else {
14342             this.doQuery(this.getRawValue());
14343         }
14344     },
14345     
14346     listKeyPress : function(e)
14347     {
14348         //Roo.log('listkeypress');
14349         // scroll to first matching element based on key pres..
14350         if (e.isSpecialKey()) {
14351             return false;
14352         }
14353         var k = String.fromCharCode(e.getKey()).toUpperCase();
14354         //Roo.log(k);
14355         var match  = false;
14356         var csel = this.view.getSelectedNodes();
14357         var cselitem = false;
14358         if (csel.length) {
14359             var ix = this.view.indexOf(csel[0]);
14360             cselitem  = this.store.getAt(ix);
14361             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
14362                 cselitem = false;
14363             }
14364             
14365         }
14366         
14367         this.store.each(function(v) { 
14368             if (cselitem) {
14369                 // start at existing selection.
14370                 if (cselitem.id == v.id) {
14371                     cselitem = false;
14372                 }
14373                 return true;
14374             }
14375                 
14376             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
14377                 match = this.store.indexOf(v);
14378                 return false;
14379             }
14380             return true;
14381         }, this);
14382         
14383         if (match === false) {
14384             return true; // no more action?
14385         }
14386         // scroll to?
14387         this.view.select(match);
14388         var sn = Roo.get(this.view.getSelectedNodes()[0]);
14389         sn.scrollIntoView(sn.dom.parentNode, false);
14390     },
14391     
14392     onViewScroll : function(e, t){
14393         
14394         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){
14395             return;
14396         }
14397         
14398         this.hasQuery = true;
14399         
14400         this.loading = this.list.select('.loading', true).first();
14401         
14402         if(this.loading === null){
14403             this.list.createChild({
14404                 tag: 'div',
14405                 cls: 'loading roo-select2-more-results roo-select2-active',
14406                 html: 'Loading more results...'
14407             });
14408             
14409             this.loading = this.list.select('.loading', true).first();
14410             
14411             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
14412             
14413             this.loading.hide();
14414         }
14415         
14416         this.loading.show();
14417         
14418         var _combo = this;
14419         
14420         this.page++;
14421         this.loadNext = true;
14422         
14423         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
14424         
14425         return;
14426     },
14427     
14428     addItem : function(o)
14429     {   
14430         var dv = ''; // display value
14431         
14432         if (this.displayField) {
14433             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
14434         } else {
14435             // this is an error condition!!!
14436             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
14437         }
14438         
14439         if(!dv.length){
14440             return;
14441         }
14442         
14443         var choice = this.choices.createChild({
14444             tag: 'li',
14445             cls: 'roo-select2-search-choice',
14446             cn: [
14447                 {
14448                     tag: 'div',
14449                     html: dv
14450                 },
14451                 {
14452                     tag: 'a',
14453                     href: '#',
14454                     cls: 'roo-select2-search-choice-close fa fa-times',
14455                     tabindex: '-1'
14456                 }
14457             ]
14458             
14459         }, this.searchField);
14460         
14461         var close = choice.select('a.roo-select2-search-choice-close', true).first();
14462         
14463         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
14464         
14465         this.item.push(o);
14466         
14467         this.lastData = o;
14468         
14469         this.syncValue();
14470         
14471         this.inputEl().dom.value = '';
14472         
14473         this.validate();
14474     },
14475     
14476     onRemoveItem : function(e, _self, o)
14477     {
14478         e.preventDefault();
14479         
14480         this.lastItem = Roo.apply([], this.item);
14481         
14482         var index = this.item.indexOf(o.data) * 1;
14483         
14484         if( index < 0){
14485             Roo.log('not this item?!');
14486             return;
14487         }
14488         
14489         this.item.splice(index, 1);
14490         o.item.remove();
14491         
14492         this.syncValue();
14493         
14494         this.fireEvent('remove', this, e);
14495         
14496         this.validate();
14497         
14498     },
14499     
14500     syncValue : function()
14501     {
14502         if(!this.item.length){
14503             this.clearValue();
14504             return;
14505         }
14506             
14507         var value = [];
14508         var _this = this;
14509         Roo.each(this.item, function(i){
14510             if(_this.valueField){
14511                 value.push(i[_this.valueField]);
14512                 return;
14513             }
14514
14515             value.push(i);
14516         });
14517
14518         this.value = value.join(',');
14519
14520         if(this.hiddenField){
14521             this.hiddenField.dom.value = this.value;
14522         }
14523         
14524         this.store.fireEvent("datachanged", this.store);
14525         
14526         this.validate();
14527     },
14528     
14529     clearItem : function()
14530     {
14531         if(!this.multiple){
14532             return;
14533         }
14534         
14535         this.item = [];
14536         
14537         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
14538            c.remove();
14539         });
14540         
14541         this.syncValue();
14542         
14543         this.validate();
14544         
14545         if(this.tickable && !Roo.isTouch){
14546             this.view.refresh();
14547         }
14548     },
14549     
14550     inputEl: function ()
14551     {
14552         if(Roo.isIOS && this.useNativeIOS){
14553             return this.el.select('select.roo-ios-select', true).first();
14554         }
14555         
14556         if(Roo.isTouch && this.mobileTouchView){
14557             return this.el.select('input.form-control',true).first();
14558         }
14559         
14560         if(this.tickable){
14561             return this.searchField;
14562         }
14563         
14564         return this.el.select('input.form-control',true).first();
14565     },
14566     
14567     onTickableFooterButtonClick : function(e, btn, el)
14568     {
14569         e.preventDefault();
14570         
14571         this.lastItem = Roo.apply([], this.item);
14572         
14573         if(btn && btn.name == 'cancel'){
14574             this.tickItems = Roo.apply([], this.item);
14575             this.collapse();
14576             return;
14577         }
14578         
14579         this.clearItem();
14580         
14581         var _this = this;
14582         
14583         Roo.each(this.tickItems, function(o){
14584             _this.addItem(o);
14585         });
14586         
14587         this.collapse();
14588         
14589     },
14590     
14591     validate : function()
14592     {
14593         var v = this.getRawValue();
14594         
14595         if(this.multiple){
14596             v = this.getValue();
14597         }
14598         
14599         if(this.disabled || this.allowBlank || v.length){
14600             this.markValid();
14601             return true;
14602         }
14603         
14604         this.markInvalid();
14605         return false;
14606     },
14607     
14608     tickableInputEl : function()
14609     {
14610         if(!this.tickable || !this.editable){
14611             return this.inputEl();
14612         }
14613         
14614         return this.inputEl().select('.roo-select2-search-field-input', true).first();
14615     },
14616     
14617     
14618     getAutoCreateTouchView : function()
14619     {
14620         var id = Roo.id();
14621         
14622         var cfg = {
14623             cls: 'form-group' //input-group
14624         };
14625         
14626         var input =  {
14627             tag: 'input',
14628             id : id,
14629             type : this.inputType,
14630             cls : 'form-control x-combo-noedit',
14631             autocomplete: 'new-password',
14632             placeholder : this.placeholder || '',
14633             readonly : true
14634         };
14635         
14636         if (this.name) {
14637             input.name = this.name;
14638         }
14639         
14640         if (this.size) {
14641             input.cls += ' input-' + this.size;
14642         }
14643         
14644         if (this.disabled) {
14645             input.disabled = true;
14646         }
14647         
14648         var inputblock = {
14649             cls : '',
14650             cn : [
14651                 input
14652             ]
14653         };
14654         
14655         if(this.before){
14656             inputblock.cls += ' input-group';
14657             
14658             inputblock.cn.unshift({
14659                 tag :'span',
14660                 cls : 'input-group-addon',
14661                 html : this.before
14662             });
14663         }
14664         
14665         if(this.removable && !this.multiple){
14666             inputblock.cls += ' roo-removable';
14667             
14668             inputblock.cn.push({
14669                 tag: 'button',
14670                 html : 'x',
14671                 cls : 'roo-combo-removable-btn close'
14672             });
14673         }
14674
14675         if(this.hasFeedback && !this.allowBlank){
14676             
14677             inputblock.cls += ' has-feedback';
14678             
14679             inputblock.cn.push({
14680                 tag: 'span',
14681                 cls: 'glyphicon form-control-feedback'
14682             });
14683             
14684         }
14685         
14686         if (this.after) {
14687             
14688             inputblock.cls += (this.before) ? '' : ' input-group';
14689             
14690             inputblock.cn.push({
14691                 tag :'span',
14692                 cls : 'input-group-addon',
14693                 html : this.after
14694             });
14695         }
14696
14697         var box = {
14698             tag: 'div',
14699             cn: [
14700                 {
14701                     tag: 'input',
14702                     type : 'hidden',
14703                     cls: 'form-hidden-field'
14704                 },
14705                 inputblock
14706             ]
14707             
14708         };
14709         
14710         if(this.multiple){
14711             box = {
14712                 tag: 'div',
14713                 cn: [
14714                     {
14715                         tag: 'input',
14716                         type : 'hidden',
14717                         cls: 'form-hidden-field'
14718                     },
14719                     {
14720                         tag: 'ul',
14721                         cls: 'roo-select2-choices',
14722                         cn:[
14723                             {
14724                                 tag: 'li',
14725                                 cls: 'roo-select2-search-field',
14726                                 cn: [
14727
14728                                     inputblock
14729                                 ]
14730                             }
14731                         ]
14732                     }
14733                 ]
14734             }
14735         };
14736         
14737         var combobox = {
14738             cls: 'roo-select2-container input-group roo-touchview-combobox ',
14739             cn: [
14740                 box
14741             ]
14742         };
14743         
14744         if(!this.multiple && this.showToggleBtn){
14745             
14746             var caret = {
14747                         tag: 'span',
14748                         cls: 'caret'
14749             };
14750             
14751             if (this.caret != false) {
14752                 caret = {
14753                      tag: 'i',
14754                      cls: 'fa fa-' + this.caret
14755                 };
14756                 
14757             }
14758             
14759             combobox.cn.push({
14760                 tag :'span',
14761                 cls : 'input-group-addon btn dropdown-toggle',
14762                 cn : [
14763                     caret,
14764                     {
14765                         tag: 'span',
14766                         cls: 'combobox-clear',
14767                         cn  : [
14768                             {
14769                                 tag : 'i',
14770                                 cls: 'icon-remove'
14771                             }
14772                         ]
14773                     }
14774                 ]
14775
14776             })
14777         }
14778         
14779         if(this.multiple){
14780             combobox.cls += ' roo-select2-container-multi';
14781         }
14782         
14783         var align = this.labelAlign || this.parentLabelAlign();
14784         
14785         if (align ==='left' && this.fieldLabel.length) {
14786
14787             cfg.cn = [
14788                 {
14789                    tag : 'i',
14790                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14791                    tooltip : 'This field is required'
14792                 },
14793                 {
14794                     tag: 'label',
14795                     cls : 'control-label',
14796                     html : this.fieldLabel
14797
14798                 },
14799                 {
14800                     cls : '', 
14801                     cn: [
14802                         combobox
14803                     ]
14804                 }
14805             ];
14806             
14807             var labelCfg = cfg.cn[1];
14808             var contentCfg = cfg.cn[2];
14809             
14810
14811             if(this.indicatorpos == 'right'){
14812                 cfg.cn = [
14813                     {
14814                         tag: 'label',
14815                         'for' :  id,
14816                         cls : 'control-label',
14817                         cn : [
14818                             {
14819                                 tag : 'span',
14820                                 html : this.fieldLabel
14821                             },
14822                             {
14823                                 tag : 'i',
14824                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14825                                 tooltip : 'This field is required'
14826                             }
14827                         ]
14828                     },
14829                     {
14830                         cls : "",
14831                         cn: [
14832                             combobox
14833                         ]
14834                     }
14835
14836                 ];
14837                 
14838                 labelCfg = cfg.cn[0];
14839                 contentCfg = cfg.cn[1];
14840             }
14841             
14842            
14843             
14844             if(this.labelWidth > 12){
14845                 labelCfg.style = "width: " + this.labelWidth + 'px';
14846             }
14847             
14848             if(this.labelWidth < 13 && this.labelmd == 0){
14849                 this.labelmd = this.labelWidth;
14850             }
14851             
14852             if(this.labellg > 0){
14853                 labelCfg.cls += ' col-lg-' + this.labellg;
14854                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
14855             }
14856             
14857             if(this.labelmd > 0){
14858                 labelCfg.cls += ' col-md-' + this.labelmd;
14859                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
14860             }
14861             
14862             if(this.labelsm > 0){
14863                 labelCfg.cls += ' col-sm-' + this.labelsm;
14864                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
14865             }
14866             
14867             if(this.labelxs > 0){
14868                 labelCfg.cls += ' col-xs-' + this.labelxs;
14869                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
14870             }
14871                 
14872                 
14873         } else if ( this.fieldLabel.length) {
14874             cfg.cn = [
14875                 {
14876                    tag : 'i',
14877                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
14878                    tooltip : 'This field is required'
14879                 },
14880                 {
14881                     tag: 'label',
14882                     cls : 'control-label',
14883                     html : this.fieldLabel
14884
14885                 },
14886                 {
14887                     cls : '', 
14888                     cn: [
14889                         combobox
14890                     ]
14891                 }
14892             ];
14893             
14894             if(this.indicatorpos == 'right'){
14895                 cfg.cn = [
14896                     {
14897                         tag: 'label',
14898                         cls : 'control-label',
14899                         html : this.fieldLabel,
14900                         cn : [
14901                             {
14902                                tag : 'i',
14903                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
14904                                tooltip : 'This field is required'
14905                             }
14906                         ]
14907                     },
14908                     {
14909                         cls : '', 
14910                         cn: [
14911                             combobox
14912                         ]
14913                     }
14914                 ];
14915             }
14916         } else {
14917             cfg.cn = combobox;    
14918         }
14919         
14920         
14921         var settings = this;
14922         
14923         ['xs','sm','md','lg'].map(function(size){
14924             if (settings[size]) {
14925                 cfg.cls += ' col-' + size + '-' + settings[size];
14926             }
14927         });
14928         
14929         return cfg;
14930     },
14931     
14932     initTouchView : function()
14933     {
14934         this.renderTouchView();
14935         
14936         this.touchViewEl.on('scroll', function(){
14937             this.el.dom.scrollTop = 0;
14938         }, this);
14939         
14940         this.originalValue = this.getValue();
14941         
14942         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
14943         
14944         this.inputEl().on("click", this.showTouchView, this);
14945         if (this.triggerEl) {
14946             this.triggerEl.on("click", this.showTouchView, this);
14947         }
14948         
14949         
14950         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
14951         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
14952         
14953         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
14954         
14955         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
14956         this.store.on('load', this.onTouchViewLoad, this);
14957         this.store.on('loadexception', this.onTouchViewLoadException, this);
14958         
14959         if(this.hiddenName){
14960             
14961             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
14962             
14963             this.hiddenField.dom.value =
14964                 this.hiddenValue !== undefined ? this.hiddenValue :
14965                 this.value !== undefined ? this.value : '';
14966         
14967             this.el.dom.removeAttribute('name');
14968             this.hiddenField.dom.setAttribute('name', this.hiddenName);
14969         }
14970         
14971         if(this.multiple){
14972             this.choices = this.el.select('ul.roo-select2-choices', true).first();
14973             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
14974         }
14975         
14976         if(this.removable && !this.multiple){
14977             var close = this.closeTriggerEl();
14978             if(close){
14979                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
14980                 close.on('click', this.removeBtnClick, this, close);
14981             }
14982         }
14983         /*
14984          * fix the bug in Safari iOS8
14985          */
14986         this.inputEl().on("focus", function(e){
14987             document.activeElement.blur();
14988         }, this);
14989         
14990         return;
14991         
14992         
14993     },
14994     
14995     renderTouchView : function()
14996     {
14997         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
14998         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
14999         
15000         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
15001         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15002         
15003         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
15004         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15005         this.touchViewBodyEl.setStyle('overflow', 'auto');
15006         
15007         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
15008         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15009         
15010         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
15011         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
15012         
15013     },
15014     
15015     showTouchView : function()
15016     {
15017         if(this.disabled){
15018             return;
15019         }
15020         
15021         this.touchViewHeaderEl.hide();
15022
15023         if(this.modalTitle.length){
15024             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
15025             this.touchViewHeaderEl.show();
15026         }
15027
15028         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
15029         this.touchViewEl.show();
15030
15031         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
15032         
15033         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
15034         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
15035
15036         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15037
15038         if(this.modalTitle.length){
15039             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15040         }
15041         
15042         this.touchViewBodyEl.setHeight(bodyHeight);
15043
15044         if(this.animate){
15045             var _this = this;
15046             (function(){ _this.touchViewEl.addClass('in'); }).defer(50);
15047         }else{
15048             this.touchViewEl.addClass('in');
15049         }
15050
15051         this.doTouchViewQuery();
15052         
15053     },
15054     
15055     hideTouchView : function()
15056     {
15057         this.touchViewEl.removeClass('in');
15058
15059         if(this.animate){
15060             var _this = this;
15061             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
15062         }else{
15063             this.touchViewEl.setStyle('display', 'none');
15064         }
15065         
15066     },
15067     
15068     setTouchViewValue : function()
15069     {
15070         if(this.multiple){
15071             this.clearItem();
15072         
15073             var _this = this;
15074
15075             Roo.each(this.tickItems, function(o){
15076                 this.addItem(o);
15077             }, this);
15078         }
15079         
15080         this.hideTouchView();
15081     },
15082     
15083     doTouchViewQuery : function()
15084     {
15085         var qe = {
15086             query: '',
15087             forceAll: true,
15088             combo: this,
15089             cancel:false
15090         };
15091         
15092         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
15093             return false;
15094         }
15095         
15096         if(!this.alwaysQuery || this.mode == 'local'){
15097             this.onTouchViewLoad();
15098             return;
15099         }
15100         
15101         this.store.load();
15102     },
15103     
15104     onTouchViewBeforeLoad : function(combo,opts)
15105     {
15106         return;
15107     },
15108
15109     // private
15110     onTouchViewLoad : function()
15111     {
15112         if(this.store.getCount() < 1){
15113             this.onTouchViewEmptyResults();
15114             return;
15115         }
15116         
15117         this.clearTouchView();
15118         
15119         var rawValue = this.getRawValue();
15120         
15121         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
15122         
15123         this.tickItems = [];
15124         
15125         this.store.data.each(function(d, rowIndex){
15126             var row = this.touchViewListGroup.createChild(template);
15127             
15128             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
15129                 row.addClass(d.data.cls);
15130             }
15131             
15132             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15133                 var cfg = {
15134                     data : d.data,
15135                     html : d.data[this.displayField]
15136                 };
15137                 
15138                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
15139                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
15140                 }
15141             }
15142             row.removeClass('selected');
15143             if(!this.multiple && this.valueField &&
15144                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
15145             {
15146                 // radio buttons..
15147                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15148                 row.addClass('selected');
15149             }
15150             
15151             if(this.multiple && this.valueField &&
15152                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
15153             {
15154                 
15155                 // checkboxes...
15156                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15157                 this.tickItems.push(d.data);
15158             }
15159             
15160             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
15161             
15162         }, this);
15163         
15164         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
15165         
15166         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
15167
15168         if(this.modalTitle.length){
15169             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
15170         }
15171
15172         var listHeight = this.touchViewListGroup.getHeight();
15173         
15174         var _this = this;
15175         
15176         if(firstChecked && listHeight > bodyHeight){
15177             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
15178         }
15179         
15180     },
15181     
15182     onTouchViewLoadException : function()
15183     {
15184         this.hideTouchView();
15185     },
15186     
15187     onTouchViewEmptyResults : function()
15188     {
15189         this.clearTouchView();
15190         
15191         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
15192         
15193         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
15194         
15195     },
15196     
15197     clearTouchView : function()
15198     {
15199         this.touchViewListGroup.dom.innerHTML = '';
15200     },
15201     
15202     onTouchViewClick : function(e, el, o)
15203     {
15204         e.preventDefault();
15205         
15206         var row = o.row;
15207         var rowIndex = o.rowIndex;
15208         
15209         var r = this.store.getAt(rowIndex);
15210         
15211         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
15212             
15213             if(!this.multiple){
15214                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
15215                     c.dom.removeAttribute('checked');
15216                 }, this);
15217
15218                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15219
15220                 this.setFromData(r.data);
15221
15222                 var close = this.closeTriggerEl();
15223
15224                 if(close){
15225                     close.show();
15226                 }
15227
15228                 this.hideTouchView();
15229
15230                 this.fireEvent('select', this, r, rowIndex);
15231
15232                 return;
15233             }
15234
15235             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
15236                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
15237                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
15238                 return;
15239             }
15240
15241             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
15242             this.addItem(r.data);
15243             this.tickItems.push(r.data);
15244         }
15245     },
15246     
15247     getAutoCreateNativeIOS : function()
15248     {
15249         var cfg = {
15250             cls: 'form-group' //input-group,
15251         };
15252         
15253         var combobox =  {
15254             tag: 'select',
15255             cls : 'roo-ios-select'
15256         };
15257         
15258         if (this.name) {
15259             combobox.name = this.name;
15260         }
15261         
15262         if (this.disabled) {
15263             combobox.disabled = true;
15264         }
15265         
15266         var settings = this;
15267         
15268         ['xs','sm','md','lg'].map(function(size){
15269             if (settings[size]) {
15270                 cfg.cls += ' col-' + size + '-' + settings[size];
15271             }
15272         });
15273         
15274         cfg.cn = combobox;
15275         
15276         return cfg;
15277         
15278     },
15279     
15280     initIOSView : function()
15281     {
15282         this.store.on('load', this.onIOSViewLoad, this);
15283         
15284         return;
15285     },
15286     
15287     onIOSViewLoad : function()
15288     {
15289         if(this.store.getCount() < 1){
15290             return;
15291         }
15292         
15293         this.clearIOSView();
15294         
15295         if(this.allowBlank) {
15296             
15297             var default_text = '-- SELECT --';
15298             
15299             if(this.placeholder.length){
15300                 default_text = this.placeholder;
15301             }
15302             
15303             if(this.emptyTitle.length){
15304                 default_text += ' - ' + this.emptyTitle + ' -';
15305             }
15306             
15307             var opt = this.inputEl().createChild({
15308                 tag: 'option',
15309                 value : 0,
15310                 html : default_text
15311             });
15312             
15313             var o = {};
15314             o[this.valueField] = 0;
15315             o[this.displayField] = default_text;
15316             
15317             this.ios_options.push({
15318                 data : o,
15319                 el : opt
15320             });
15321             
15322         }
15323         
15324         this.store.data.each(function(d, rowIndex){
15325             
15326             var html = '';
15327             
15328             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
15329                 html = d.data[this.displayField];
15330             }
15331             
15332             var value = '';
15333             
15334             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
15335                 value = d.data[this.valueField];
15336             }
15337             
15338             var option = {
15339                 tag: 'option',
15340                 value : value,
15341                 html : html
15342             };
15343             
15344             if(this.value == d.data[this.valueField]){
15345                 option['selected'] = true;
15346             }
15347             
15348             var opt = this.inputEl().createChild(option);
15349             
15350             this.ios_options.push({
15351                 data : d.data,
15352                 el : opt
15353             });
15354             
15355         }, this);
15356         
15357         this.inputEl().on('change', function(){
15358            this.fireEvent('select', this);
15359         }, this);
15360         
15361     },
15362     
15363     clearIOSView: function()
15364     {
15365         this.inputEl().dom.innerHTML = '';
15366         
15367         this.ios_options = [];
15368     },
15369     
15370     setIOSValue: function(v)
15371     {
15372         this.value = v;
15373         
15374         if(!this.ios_options){
15375             return;
15376         }
15377         
15378         Roo.each(this.ios_options, function(opts){
15379            
15380            opts.el.dom.removeAttribute('selected');
15381            
15382            if(opts.data[this.valueField] != v){
15383                return;
15384            }
15385            
15386            opts.el.dom.setAttribute('selected', true);
15387            
15388         }, this);
15389     }
15390
15391     /** 
15392     * @cfg {Boolean} grow 
15393     * @hide 
15394     */
15395     /** 
15396     * @cfg {Number} growMin 
15397     * @hide 
15398     */
15399     /** 
15400     * @cfg {Number} growMax 
15401     * @hide 
15402     */
15403     /**
15404      * @hide
15405      * @method autoSize
15406      */
15407 });
15408
15409 Roo.apply(Roo.bootstrap.ComboBox,  {
15410     
15411     header : {
15412         tag: 'div',
15413         cls: 'modal-header',
15414         cn: [
15415             {
15416                 tag: 'h4',
15417                 cls: 'modal-title'
15418             }
15419         ]
15420     },
15421     
15422     body : {
15423         tag: 'div',
15424         cls: 'modal-body',
15425         cn: [
15426             {
15427                 tag: 'ul',
15428                 cls: 'list-group'
15429             }
15430         ]
15431     },
15432     
15433     listItemRadio : {
15434         tag: 'li',
15435         cls: 'list-group-item',
15436         cn: [
15437             {
15438                 tag: 'span',
15439                 cls: 'roo-combobox-list-group-item-value'
15440             },
15441             {
15442                 tag: 'div',
15443                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
15444                 cn: [
15445                     {
15446                         tag: 'input',
15447                         type: 'radio'
15448                     },
15449                     {
15450                         tag: 'label'
15451                     }
15452                 ]
15453             }
15454         ]
15455     },
15456     
15457     listItemCheckbox : {
15458         tag: 'li',
15459         cls: 'list-group-item',
15460         cn: [
15461             {
15462                 tag: 'span',
15463                 cls: 'roo-combobox-list-group-item-value'
15464             },
15465             {
15466                 tag: 'div',
15467                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
15468                 cn: [
15469                     {
15470                         tag: 'input',
15471                         type: 'checkbox'
15472                     },
15473                     {
15474                         tag: 'label'
15475                     }
15476                 ]
15477             }
15478         ]
15479     },
15480     
15481     emptyResult : {
15482         tag: 'div',
15483         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
15484     },
15485     
15486     footer : {
15487         tag: 'div',
15488         cls: 'modal-footer',
15489         cn: [
15490             {
15491                 tag: 'div',
15492                 cls: 'row',
15493                 cn: [
15494                     {
15495                         tag: 'div',
15496                         cls: 'col-xs-6 text-left',
15497                         cn: {
15498                             tag: 'button',
15499                             cls: 'btn btn-danger roo-touch-view-cancel',
15500                             html: 'Cancel'
15501                         }
15502                     },
15503                     {
15504                         tag: 'div',
15505                         cls: 'col-xs-6 text-right',
15506                         cn: {
15507                             tag: 'button',
15508                             cls: 'btn btn-success roo-touch-view-ok',
15509                             html: 'OK'
15510                         }
15511                     }
15512                 ]
15513             }
15514         ]
15515         
15516     }
15517 });
15518
15519 Roo.apply(Roo.bootstrap.ComboBox,  {
15520     
15521     touchViewTemplate : {
15522         tag: 'div',
15523         cls: 'modal fade roo-combobox-touch-view',
15524         cn: [
15525             {
15526                 tag: 'div',
15527                 cls: 'modal-dialog',
15528                 style : 'position:fixed', // we have to fix position....
15529                 cn: [
15530                     {
15531                         tag: 'div',
15532                         cls: 'modal-content',
15533                         cn: [
15534                             Roo.bootstrap.ComboBox.header,
15535                             Roo.bootstrap.ComboBox.body,
15536                             Roo.bootstrap.ComboBox.footer
15537                         ]
15538                     }
15539                 ]
15540             }
15541         ]
15542     }
15543 });/*
15544  * Based on:
15545  * Ext JS Library 1.1.1
15546  * Copyright(c) 2006-2007, Ext JS, LLC.
15547  *
15548  * Originally Released Under LGPL - original licence link has changed is not relivant.
15549  *
15550  * Fork - LGPL
15551  * <script type="text/javascript">
15552  */
15553
15554 /**
15555  * @class Roo.View
15556  * @extends Roo.util.Observable
15557  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
15558  * This class also supports single and multi selection modes. <br>
15559  * Create a data model bound view:
15560  <pre><code>
15561  var store = new Roo.data.Store(...);
15562
15563  var view = new Roo.View({
15564     el : "my-element",
15565     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
15566  
15567     singleSelect: true,
15568     selectedClass: "ydataview-selected",
15569     store: store
15570  });
15571
15572  // listen for node click?
15573  view.on("click", function(vw, index, node, e){
15574  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
15575  });
15576
15577  // load XML data
15578  dataModel.load("foobar.xml");
15579  </code></pre>
15580  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
15581  * <br><br>
15582  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
15583  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
15584  * 
15585  * Note: old style constructor is still suported (container, template, config)
15586  * 
15587  * @constructor
15588  * Create a new View
15589  * @param {Object} config The config object
15590  * 
15591  */
15592 Roo.View = function(config, depreciated_tpl, depreciated_config){
15593     
15594     this.parent = false;
15595     
15596     if (typeof(depreciated_tpl) == 'undefined') {
15597         // new way.. - universal constructor.
15598         Roo.apply(this, config);
15599         this.el  = Roo.get(this.el);
15600     } else {
15601         // old format..
15602         this.el  = Roo.get(config);
15603         this.tpl = depreciated_tpl;
15604         Roo.apply(this, depreciated_config);
15605     }
15606     this.wrapEl  = this.el.wrap().wrap();
15607     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
15608     
15609     
15610     if(typeof(this.tpl) == "string"){
15611         this.tpl = new Roo.Template(this.tpl);
15612     } else {
15613         // support xtype ctors..
15614         this.tpl = new Roo.factory(this.tpl, Roo);
15615     }
15616     
15617     
15618     this.tpl.compile();
15619     
15620     /** @private */
15621     this.addEvents({
15622         /**
15623          * @event beforeclick
15624          * Fires before a click is processed. Returns false to cancel the default action.
15625          * @param {Roo.View} this
15626          * @param {Number} index The index of the target node
15627          * @param {HTMLElement} node The target node
15628          * @param {Roo.EventObject} e The raw event object
15629          */
15630             "beforeclick" : true,
15631         /**
15632          * @event click
15633          * Fires when a template node is clicked.
15634          * @param {Roo.View} this
15635          * @param {Number} index The index of the target node
15636          * @param {HTMLElement} node The target node
15637          * @param {Roo.EventObject} e The raw event object
15638          */
15639             "click" : true,
15640         /**
15641          * @event dblclick
15642          * Fires when a template node is double clicked.
15643          * @param {Roo.View} this
15644          * @param {Number} index The index of the target node
15645          * @param {HTMLElement} node The target node
15646          * @param {Roo.EventObject} e The raw event object
15647          */
15648             "dblclick" : true,
15649         /**
15650          * @event contextmenu
15651          * Fires when a template node is right clicked.
15652          * @param {Roo.View} this
15653          * @param {Number} index The index of the target node
15654          * @param {HTMLElement} node The target node
15655          * @param {Roo.EventObject} e The raw event object
15656          */
15657             "contextmenu" : true,
15658         /**
15659          * @event selectionchange
15660          * Fires when the selected nodes change.
15661          * @param {Roo.View} this
15662          * @param {Array} selections Array of the selected nodes
15663          */
15664             "selectionchange" : true,
15665     
15666         /**
15667          * @event beforeselect
15668          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
15669          * @param {Roo.View} this
15670          * @param {HTMLElement} node The node to be selected
15671          * @param {Array} selections Array of currently selected nodes
15672          */
15673             "beforeselect" : true,
15674         /**
15675          * @event preparedata
15676          * Fires on every row to render, to allow you to change the data.
15677          * @param {Roo.View} this
15678          * @param {Object} data to be rendered (change this)
15679          */
15680           "preparedata" : true
15681           
15682           
15683         });
15684
15685
15686
15687     this.el.on({
15688         "click": this.onClick,
15689         "dblclick": this.onDblClick,
15690         "contextmenu": this.onContextMenu,
15691         scope:this
15692     });
15693
15694     this.selections = [];
15695     this.nodes = [];
15696     this.cmp = new Roo.CompositeElementLite([]);
15697     if(this.store){
15698         this.store = Roo.factory(this.store, Roo.data);
15699         this.setStore(this.store, true);
15700     }
15701     
15702     if ( this.footer && this.footer.xtype) {
15703            
15704          var fctr = this.wrapEl.appendChild(document.createElement("div"));
15705         
15706         this.footer.dataSource = this.store;
15707         this.footer.container = fctr;
15708         this.footer = Roo.factory(this.footer, Roo);
15709         fctr.insertFirst(this.el);
15710         
15711         // this is a bit insane - as the paging toolbar seems to detach the el..
15712 //        dom.parentNode.parentNode.parentNode
15713          // they get detached?
15714     }
15715     
15716     
15717     Roo.View.superclass.constructor.call(this);
15718     
15719     
15720 };
15721
15722 Roo.extend(Roo.View, Roo.util.Observable, {
15723     
15724      /**
15725      * @cfg {Roo.data.Store} store Data store to load data from.
15726      */
15727     store : false,
15728     
15729     /**
15730      * @cfg {String|Roo.Element} el The container element.
15731      */
15732     el : '',
15733     
15734     /**
15735      * @cfg {String|Roo.Template} tpl The template used by this View 
15736      */
15737     tpl : false,
15738     /**
15739      * @cfg {String} dataName the named area of the template to use as the data area
15740      *                          Works with domtemplates roo-name="name"
15741      */
15742     dataName: false,
15743     /**
15744      * @cfg {String} selectedClass The css class to add to selected nodes
15745      */
15746     selectedClass : "x-view-selected",
15747      /**
15748      * @cfg {String} emptyText The empty text to show when nothing is loaded.
15749      */
15750     emptyText : "",
15751     
15752     /**
15753      * @cfg {String} text to display on mask (default Loading)
15754      */
15755     mask : false,
15756     /**
15757      * @cfg {Boolean} multiSelect Allow multiple selection
15758      */
15759     multiSelect : false,
15760     /**
15761      * @cfg {Boolean} singleSelect Allow single selection
15762      */
15763     singleSelect:  false,
15764     
15765     /**
15766      * @cfg {Boolean} toggleSelect - selecting 
15767      */
15768     toggleSelect : false,
15769     
15770     /**
15771      * @cfg {Boolean} tickable - selecting 
15772      */
15773     tickable : false,
15774     
15775     /**
15776      * Returns the element this view is bound to.
15777      * @return {Roo.Element}
15778      */
15779     getEl : function(){
15780         return this.wrapEl;
15781     },
15782     
15783     
15784
15785     /**
15786      * Refreshes the view. - called by datachanged on the store. - do not call directly.
15787      */
15788     refresh : function(){
15789         //Roo.log('refresh');
15790         var t = this.tpl;
15791         
15792         // if we are using something like 'domtemplate', then
15793         // the what gets used is:
15794         // t.applySubtemplate(NAME, data, wrapping data..)
15795         // the outer template then get' applied with
15796         //     the store 'extra data'
15797         // and the body get's added to the
15798         //      roo-name="data" node?
15799         //      <span class='roo-tpl-{name}'></span> ?????
15800         
15801         
15802         
15803         this.clearSelections();
15804         this.el.update("");
15805         var html = [];
15806         var records = this.store.getRange();
15807         if(records.length < 1) {
15808             
15809             // is this valid??  = should it render a template??
15810             
15811             this.el.update(this.emptyText);
15812             return;
15813         }
15814         var el = this.el;
15815         if (this.dataName) {
15816             this.el.update(t.apply(this.store.meta)); //????
15817             el = this.el.child('.roo-tpl-' + this.dataName);
15818         }
15819         
15820         for(var i = 0, len = records.length; i < len; i++){
15821             var data = this.prepareData(records[i].data, i, records[i]);
15822             this.fireEvent("preparedata", this, data, i, records[i]);
15823             
15824             var d = Roo.apply({}, data);
15825             
15826             if(this.tickable){
15827                 Roo.apply(d, {'roo-id' : Roo.id()});
15828                 
15829                 var _this = this;
15830             
15831                 Roo.each(this.parent.item, function(item){
15832                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
15833                         return;
15834                     }
15835                     Roo.apply(d, {'roo-data-checked' : 'checked'});
15836                 });
15837             }
15838             
15839             html[html.length] = Roo.util.Format.trim(
15840                 this.dataName ?
15841                     t.applySubtemplate(this.dataName, d, this.store.meta) :
15842                     t.apply(d)
15843             );
15844         }
15845         
15846         
15847         
15848         el.update(html.join(""));
15849         this.nodes = el.dom.childNodes;
15850         this.updateIndexes(0);
15851     },
15852     
15853
15854     /**
15855      * Function to override to reformat the data that is sent to
15856      * the template for each node.
15857      * DEPRICATED - use the preparedata event handler.
15858      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
15859      * a JSON object for an UpdateManager bound view).
15860      */
15861     prepareData : function(data, index, record)
15862     {
15863         this.fireEvent("preparedata", this, data, index, record);
15864         return data;
15865     },
15866
15867     onUpdate : function(ds, record){
15868         // Roo.log('on update');   
15869         this.clearSelections();
15870         var index = this.store.indexOf(record);
15871         var n = this.nodes[index];
15872         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
15873         n.parentNode.removeChild(n);
15874         this.updateIndexes(index, index);
15875     },
15876
15877     
15878     
15879 // --------- FIXME     
15880     onAdd : function(ds, records, index)
15881     {
15882         //Roo.log(['on Add', ds, records, index] );        
15883         this.clearSelections();
15884         if(this.nodes.length == 0){
15885             this.refresh();
15886             return;
15887         }
15888         var n = this.nodes[index];
15889         for(var i = 0, len = records.length; i < len; i++){
15890             var d = this.prepareData(records[i].data, i, records[i]);
15891             if(n){
15892                 this.tpl.insertBefore(n, d);
15893             }else{
15894                 
15895                 this.tpl.append(this.el, d);
15896             }
15897         }
15898         this.updateIndexes(index);
15899     },
15900
15901     onRemove : function(ds, record, index){
15902        // Roo.log('onRemove');
15903         this.clearSelections();
15904         var el = this.dataName  ?
15905             this.el.child('.roo-tpl-' + this.dataName) :
15906             this.el; 
15907         
15908         el.dom.removeChild(this.nodes[index]);
15909         this.updateIndexes(index);
15910     },
15911
15912     /**
15913      * Refresh an individual node.
15914      * @param {Number} index
15915      */
15916     refreshNode : function(index){
15917         this.onUpdate(this.store, this.store.getAt(index));
15918     },
15919
15920     updateIndexes : function(startIndex, endIndex){
15921         var ns = this.nodes;
15922         startIndex = startIndex || 0;
15923         endIndex = endIndex || ns.length - 1;
15924         for(var i = startIndex; i <= endIndex; i++){
15925             ns[i].nodeIndex = i;
15926         }
15927     },
15928
15929     /**
15930      * Changes the data store this view uses and refresh the view.
15931      * @param {Store} store
15932      */
15933     setStore : function(store, initial){
15934         if(!initial && this.store){
15935             this.store.un("datachanged", this.refresh);
15936             this.store.un("add", this.onAdd);
15937             this.store.un("remove", this.onRemove);
15938             this.store.un("update", this.onUpdate);
15939             this.store.un("clear", this.refresh);
15940             this.store.un("beforeload", this.onBeforeLoad);
15941             this.store.un("load", this.onLoad);
15942             this.store.un("loadexception", this.onLoad);
15943         }
15944         if(store){
15945           
15946             store.on("datachanged", this.refresh, this);
15947             store.on("add", this.onAdd, this);
15948             store.on("remove", this.onRemove, this);
15949             store.on("update", this.onUpdate, this);
15950             store.on("clear", this.refresh, this);
15951             store.on("beforeload", this.onBeforeLoad, this);
15952             store.on("load", this.onLoad, this);
15953             store.on("loadexception", this.onLoad, this);
15954         }
15955         
15956         if(store){
15957             this.refresh();
15958         }
15959     },
15960     /**
15961      * onbeforeLoad - masks the loading area.
15962      *
15963      */
15964     onBeforeLoad : function(store,opts)
15965     {
15966          //Roo.log('onBeforeLoad');   
15967         if (!opts.add) {
15968             this.el.update("");
15969         }
15970         this.el.mask(this.mask ? this.mask : "Loading" ); 
15971     },
15972     onLoad : function ()
15973     {
15974         this.el.unmask();
15975     },
15976     
15977
15978     /**
15979      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
15980      * @param {HTMLElement} node
15981      * @return {HTMLElement} The template node
15982      */
15983     findItemFromChild : function(node){
15984         var el = this.dataName  ?
15985             this.el.child('.roo-tpl-' + this.dataName,true) :
15986             this.el.dom; 
15987         
15988         if(!node || node.parentNode == el){
15989                     return node;
15990             }
15991             var p = node.parentNode;
15992             while(p && p != el){
15993             if(p.parentNode == el){
15994                 return p;
15995             }
15996             p = p.parentNode;
15997         }
15998             return null;
15999     },
16000
16001     /** @ignore */
16002     onClick : function(e){
16003         var item = this.findItemFromChild(e.getTarget());
16004         if(item){
16005             var index = this.indexOf(item);
16006             if(this.onItemClick(item, index, e) !== false){
16007                 this.fireEvent("click", this, index, item, e);
16008             }
16009         }else{
16010             this.clearSelections();
16011         }
16012     },
16013
16014     /** @ignore */
16015     onContextMenu : function(e){
16016         var item = this.findItemFromChild(e.getTarget());
16017         if(item){
16018             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
16019         }
16020     },
16021
16022     /** @ignore */
16023     onDblClick : function(e){
16024         var item = this.findItemFromChild(e.getTarget());
16025         if(item){
16026             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
16027         }
16028     },
16029
16030     onItemClick : function(item, index, e)
16031     {
16032         if(this.fireEvent("beforeclick", this, index, item, e) === false){
16033             return false;
16034         }
16035         if (this.toggleSelect) {
16036             var m = this.isSelected(item) ? 'unselect' : 'select';
16037             //Roo.log(m);
16038             var _t = this;
16039             _t[m](item, true, false);
16040             return true;
16041         }
16042         if(this.multiSelect || this.singleSelect){
16043             if(this.multiSelect && e.shiftKey && this.lastSelection){
16044                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
16045             }else{
16046                 this.select(item, this.multiSelect && e.ctrlKey);
16047                 this.lastSelection = item;
16048             }
16049             
16050             if(!this.tickable){
16051                 e.preventDefault();
16052             }
16053             
16054         }
16055         return true;
16056     },
16057
16058     /**
16059      * Get the number of selected nodes.
16060      * @return {Number}
16061      */
16062     getSelectionCount : function(){
16063         return this.selections.length;
16064     },
16065
16066     /**
16067      * Get the currently selected nodes.
16068      * @return {Array} An array of HTMLElements
16069      */
16070     getSelectedNodes : function(){
16071         return this.selections;
16072     },
16073
16074     /**
16075      * Get the indexes of the selected nodes.
16076      * @return {Array}
16077      */
16078     getSelectedIndexes : function(){
16079         var indexes = [], s = this.selections;
16080         for(var i = 0, len = s.length; i < len; i++){
16081             indexes.push(s[i].nodeIndex);
16082         }
16083         return indexes;
16084     },
16085
16086     /**
16087      * Clear all selections
16088      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
16089      */
16090     clearSelections : function(suppressEvent){
16091         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
16092             this.cmp.elements = this.selections;
16093             this.cmp.removeClass(this.selectedClass);
16094             this.selections = [];
16095             if(!suppressEvent){
16096                 this.fireEvent("selectionchange", this, this.selections);
16097             }
16098         }
16099     },
16100
16101     /**
16102      * Returns true if the passed node is selected
16103      * @param {HTMLElement/Number} node The node or node index
16104      * @return {Boolean}
16105      */
16106     isSelected : function(node){
16107         var s = this.selections;
16108         if(s.length < 1){
16109             return false;
16110         }
16111         node = this.getNode(node);
16112         return s.indexOf(node) !== -1;
16113     },
16114
16115     /**
16116      * Selects nodes.
16117      * @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
16118      * @param {Boolean} keepExisting (optional) true to keep existing selections
16119      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16120      */
16121     select : function(nodeInfo, keepExisting, suppressEvent){
16122         if(nodeInfo instanceof Array){
16123             if(!keepExisting){
16124                 this.clearSelections(true);
16125             }
16126             for(var i = 0, len = nodeInfo.length; i < len; i++){
16127                 this.select(nodeInfo[i], true, true);
16128             }
16129             return;
16130         } 
16131         var node = this.getNode(nodeInfo);
16132         if(!node || this.isSelected(node)){
16133             return; // already selected.
16134         }
16135         if(!keepExisting){
16136             this.clearSelections(true);
16137         }
16138         
16139         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
16140             Roo.fly(node).addClass(this.selectedClass);
16141             this.selections.push(node);
16142             if(!suppressEvent){
16143                 this.fireEvent("selectionchange", this, this.selections);
16144             }
16145         }
16146         
16147         
16148     },
16149       /**
16150      * Unselects nodes.
16151      * @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
16152      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
16153      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
16154      */
16155     unselect : function(nodeInfo, keepExisting, suppressEvent)
16156     {
16157         if(nodeInfo instanceof Array){
16158             Roo.each(this.selections, function(s) {
16159                 this.unselect(s, nodeInfo);
16160             }, this);
16161             return;
16162         }
16163         var node = this.getNode(nodeInfo);
16164         if(!node || !this.isSelected(node)){
16165             //Roo.log("not selected");
16166             return; // not selected.
16167         }
16168         // fireevent???
16169         var ns = [];
16170         Roo.each(this.selections, function(s) {
16171             if (s == node ) {
16172                 Roo.fly(node).removeClass(this.selectedClass);
16173
16174                 return;
16175             }
16176             ns.push(s);
16177         },this);
16178         
16179         this.selections= ns;
16180         this.fireEvent("selectionchange", this, this.selections);
16181     },
16182
16183     /**
16184      * Gets a template node.
16185      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16186      * @return {HTMLElement} The node or null if it wasn't found
16187      */
16188     getNode : function(nodeInfo){
16189         if(typeof nodeInfo == "string"){
16190             return document.getElementById(nodeInfo);
16191         }else if(typeof nodeInfo == "number"){
16192             return this.nodes[nodeInfo];
16193         }
16194         return nodeInfo;
16195     },
16196
16197     /**
16198      * Gets a range template nodes.
16199      * @param {Number} startIndex
16200      * @param {Number} endIndex
16201      * @return {Array} An array of nodes
16202      */
16203     getNodes : function(start, end){
16204         var ns = this.nodes;
16205         start = start || 0;
16206         end = typeof end == "undefined" ? ns.length - 1 : end;
16207         var nodes = [];
16208         if(start <= end){
16209             for(var i = start; i <= end; i++){
16210                 nodes.push(ns[i]);
16211             }
16212         } else{
16213             for(var i = start; i >= end; i--){
16214                 nodes.push(ns[i]);
16215             }
16216         }
16217         return nodes;
16218     },
16219
16220     /**
16221      * Finds the index of the passed node
16222      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
16223      * @return {Number} The index of the node or -1
16224      */
16225     indexOf : function(node){
16226         node = this.getNode(node);
16227         if(typeof node.nodeIndex == "number"){
16228             return node.nodeIndex;
16229         }
16230         var ns = this.nodes;
16231         for(var i = 0, len = ns.length; i < len; i++){
16232             if(ns[i] == node){
16233                 return i;
16234             }
16235         }
16236         return -1;
16237     }
16238 });
16239 /*
16240  * - LGPL
16241  *
16242  * based on jquery fullcalendar
16243  * 
16244  */
16245
16246 Roo.bootstrap = Roo.bootstrap || {};
16247 /**
16248  * @class Roo.bootstrap.Calendar
16249  * @extends Roo.bootstrap.Component
16250  * Bootstrap Calendar class
16251  * @cfg {Boolean} loadMask (true|false) default false
16252  * @cfg {Object} header generate the user specific header of the calendar, default false
16253
16254  * @constructor
16255  * Create a new Container
16256  * @param {Object} config The config object
16257  */
16258
16259
16260
16261 Roo.bootstrap.Calendar = function(config){
16262     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
16263      this.addEvents({
16264         /**
16265              * @event select
16266              * Fires when a date is selected
16267              * @param {DatePicker} this
16268              * @param {Date} date The selected date
16269              */
16270         'select': true,
16271         /**
16272              * @event monthchange
16273              * Fires when the displayed month changes 
16274              * @param {DatePicker} this
16275              * @param {Date} date The selected month
16276              */
16277         'monthchange': true,
16278         /**
16279              * @event evententer
16280              * Fires when mouse over an event
16281              * @param {Calendar} this
16282              * @param {event} Event
16283              */
16284         'evententer': true,
16285         /**
16286              * @event eventleave
16287              * Fires when the mouse leaves an
16288              * @param {Calendar} this
16289              * @param {event}
16290              */
16291         'eventleave': true,
16292         /**
16293              * @event eventclick
16294              * Fires when the mouse click an
16295              * @param {Calendar} this
16296              * @param {event}
16297              */
16298         'eventclick': true
16299         
16300     });
16301
16302 };
16303
16304 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
16305     
16306      /**
16307      * @cfg {Number} startDay
16308      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
16309      */
16310     startDay : 0,
16311     
16312     loadMask : false,
16313     
16314     header : false,
16315       
16316     getAutoCreate : function(){
16317         
16318         
16319         var fc_button = function(name, corner, style, content ) {
16320             return Roo.apply({},{
16321                 tag : 'span',
16322                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
16323                          (corner.length ?
16324                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
16325                             ''
16326                         ),
16327                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
16328                 unselectable: 'on'
16329             });
16330         };
16331         
16332         var header = {};
16333         
16334         if(!this.header){
16335             header = {
16336                 tag : 'table',
16337                 cls : 'fc-header',
16338                 style : 'width:100%',
16339                 cn : [
16340                     {
16341                         tag: 'tr',
16342                         cn : [
16343                             {
16344                                 tag : 'td',
16345                                 cls : 'fc-header-left',
16346                                 cn : [
16347                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
16348                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
16349                                     { tag: 'span', cls: 'fc-header-space' },
16350                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
16351
16352
16353                                 ]
16354                             },
16355
16356                             {
16357                                 tag : 'td',
16358                                 cls : 'fc-header-center',
16359                                 cn : [
16360                                     {
16361                                         tag: 'span',
16362                                         cls: 'fc-header-title',
16363                                         cn : {
16364                                             tag: 'H2',
16365                                             html : 'month / year'
16366                                         }
16367                                     }
16368
16369                                 ]
16370                             },
16371                             {
16372                                 tag : 'td',
16373                                 cls : 'fc-header-right',
16374                                 cn : [
16375                               /*      fc_button('month', 'left', '', 'month' ),
16376                                     fc_button('week', '', '', 'week' ),
16377                                     fc_button('day', 'right', '', 'day' )
16378                                 */    
16379
16380                                 ]
16381                             }
16382
16383                         ]
16384                     }
16385                 ]
16386             };
16387         }
16388         
16389         header = this.header;
16390         
16391        
16392         var cal_heads = function() {
16393             var ret = [];
16394             // fixme - handle this.
16395             
16396             for (var i =0; i < Date.dayNames.length; i++) {
16397                 var d = Date.dayNames[i];
16398                 ret.push({
16399                     tag: 'th',
16400                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
16401                     html : d.substring(0,3)
16402                 });
16403                 
16404             }
16405             ret[0].cls += ' fc-first';
16406             ret[6].cls += ' fc-last';
16407             return ret;
16408         };
16409         var cal_cell = function(n) {
16410             return  {
16411                 tag: 'td',
16412                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
16413                 cn : [
16414                     {
16415                         cn : [
16416                             {
16417                                 cls: 'fc-day-number',
16418                                 html: 'D'
16419                             },
16420                             {
16421                                 cls: 'fc-day-content',
16422                              
16423                                 cn : [
16424                                      {
16425                                         style: 'position: relative;' // height: 17px;
16426                                     }
16427                                 ]
16428                             }
16429                             
16430                             
16431                         ]
16432                     }
16433                 ]
16434                 
16435             }
16436         };
16437         var cal_rows = function() {
16438             
16439             var ret = [];
16440             for (var r = 0; r < 6; r++) {
16441                 var row= {
16442                     tag : 'tr',
16443                     cls : 'fc-week',
16444                     cn : []
16445                 };
16446                 
16447                 for (var i =0; i < Date.dayNames.length; i++) {
16448                     var d = Date.dayNames[i];
16449                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
16450
16451                 }
16452                 row.cn[0].cls+=' fc-first';
16453                 row.cn[0].cn[0].style = 'min-height:90px';
16454                 row.cn[6].cls+=' fc-last';
16455                 ret.push(row);
16456                 
16457             }
16458             ret[0].cls += ' fc-first';
16459             ret[4].cls += ' fc-prev-last';
16460             ret[5].cls += ' fc-last';
16461             return ret;
16462             
16463         };
16464         
16465         var cal_table = {
16466             tag: 'table',
16467             cls: 'fc-border-separate',
16468             style : 'width:100%',
16469             cellspacing  : 0,
16470             cn : [
16471                 { 
16472                     tag: 'thead',
16473                     cn : [
16474                         { 
16475                             tag: 'tr',
16476                             cls : 'fc-first fc-last',
16477                             cn : cal_heads()
16478                         }
16479                     ]
16480                 },
16481                 { 
16482                     tag: 'tbody',
16483                     cn : cal_rows()
16484                 }
16485                   
16486             ]
16487         };
16488          
16489          var cfg = {
16490             cls : 'fc fc-ltr',
16491             cn : [
16492                 header,
16493                 {
16494                     cls : 'fc-content',
16495                     style : "position: relative;",
16496                     cn : [
16497                         {
16498                             cls : 'fc-view fc-view-month fc-grid',
16499                             style : 'position: relative',
16500                             unselectable : 'on',
16501                             cn : [
16502                                 {
16503                                     cls : 'fc-event-container',
16504                                     style : 'position:absolute;z-index:8;top:0;left:0;'
16505                                 },
16506                                 cal_table
16507                             ]
16508                         }
16509                     ]
16510     
16511                 }
16512            ] 
16513             
16514         };
16515         
16516          
16517         
16518         return cfg;
16519     },
16520     
16521     
16522     initEvents : function()
16523     {
16524         if(!this.store){
16525             throw "can not find store for calendar";
16526         }
16527         
16528         var mark = {
16529             tag: "div",
16530             cls:"x-dlg-mask",
16531             style: "text-align:center",
16532             cn: [
16533                 {
16534                     tag: "div",
16535                     style: "background-color:white;width:50%;margin:250 auto",
16536                     cn: [
16537                         {
16538                             tag: "img",
16539                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
16540                         },
16541                         {
16542                             tag: "span",
16543                             html: "Loading"
16544                         }
16545                         
16546                     ]
16547                 }
16548             ]
16549         };
16550         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
16551         
16552         var size = this.el.select('.fc-content', true).first().getSize();
16553         this.maskEl.setSize(size.width, size.height);
16554         this.maskEl.enableDisplayMode("block");
16555         if(!this.loadMask){
16556             this.maskEl.hide();
16557         }
16558         
16559         this.store = Roo.factory(this.store, Roo.data);
16560         this.store.on('load', this.onLoad, this);
16561         this.store.on('beforeload', this.onBeforeLoad, this);
16562         
16563         this.resize();
16564         
16565         this.cells = this.el.select('.fc-day',true);
16566         //Roo.log(this.cells);
16567         this.textNodes = this.el.query('.fc-day-number');
16568         this.cells.addClassOnOver('fc-state-hover');
16569         
16570         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
16571         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
16572         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
16573         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
16574         
16575         this.on('monthchange', this.onMonthChange, this);
16576         
16577         this.update(new Date().clearTime());
16578     },
16579     
16580     resize : function() {
16581         var sz  = this.el.getSize();
16582         
16583         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
16584         this.el.select('.fc-day-content div',true).setHeight(34);
16585     },
16586     
16587     
16588     // private
16589     showPrevMonth : function(e){
16590         this.update(this.activeDate.add("mo", -1));
16591     },
16592     showToday : function(e){
16593         this.update(new Date().clearTime());
16594     },
16595     // private
16596     showNextMonth : function(e){
16597         this.update(this.activeDate.add("mo", 1));
16598     },
16599
16600     // private
16601     showPrevYear : function(){
16602         this.update(this.activeDate.add("y", -1));
16603     },
16604
16605     // private
16606     showNextYear : function(){
16607         this.update(this.activeDate.add("y", 1));
16608     },
16609
16610     
16611    // private
16612     update : function(date)
16613     {
16614         var vd = this.activeDate;
16615         this.activeDate = date;
16616 //        if(vd && this.el){
16617 //            var t = date.getTime();
16618 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
16619 //                Roo.log('using add remove');
16620 //                
16621 //                this.fireEvent('monthchange', this, date);
16622 //                
16623 //                this.cells.removeClass("fc-state-highlight");
16624 //                this.cells.each(function(c){
16625 //                   if(c.dateValue == t){
16626 //                       c.addClass("fc-state-highlight");
16627 //                       setTimeout(function(){
16628 //                            try{c.dom.firstChild.focus();}catch(e){}
16629 //                       }, 50);
16630 //                       return false;
16631 //                   }
16632 //                   return true;
16633 //                });
16634 //                return;
16635 //            }
16636 //        }
16637         
16638         var days = date.getDaysInMonth();
16639         
16640         var firstOfMonth = date.getFirstDateOfMonth();
16641         var startingPos = firstOfMonth.getDay()-this.startDay;
16642         
16643         if(startingPos < this.startDay){
16644             startingPos += 7;
16645         }
16646         
16647         var pm = date.add(Date.MONTH, -1);
16648         var prevStart = pm.getDaysInMonth()-startingPos;
16649 //        
16650         this.cells = this.el.select('.fc-day',true);
16651         this.textNodes = this.el.query('.fc-day-number');
16652         this.cells.addClassOnOver('fc-state-hover');
16653         
16654         var cells = this.cells.elements;
16655         var textEls = this.textNodes;
16656         
16657         Roo.each(cells, function(cell){
16658             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
16659         });
16660         
16661         days += startingPos;
16662
16663         // convert everything to numbers so it's fast
16664         var day = 86400000;
16665         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
16666         //Roo.log(d);
16667         //Roo.log(pm);
16668         //Roo.log(prevStart);
16669         
16670         var today = new Date().clearTime().getTime();
16671         var sel = date.clearTime().getTime();
16672         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
16673         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
16674         var ddMatch = this.disabledDatesRE;
16675         var ddText = this.disabledDatesText;
16676         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
16677         var ddaysText = this.disabledDaysText;
16678         var format = this.format;
16679         
16680         var setCellClass = function(cal, cell){
16681             cell.row = 0;
16682             cell.events = [];
16683             cell.more = [];
16684             //Roo.log('set Cell Class');
16685             cell.title = "";
16686             var t = d.getTime();
16687             
16688             //Roo.log(d);
16689             
16690             cell.dateValue = t;
16691             if(t == today){
16692                 cell.className += " fc-today";
16693                 cell.className += " fc-state-highlight";
16694                 cell.title = cal.todayText;
16695             }
16696             if(t == sel){
16697                 // disable highlight in other month..
16698                 //cell.className += " fc-state-highlight";
16699                 
16700             }
16701             // disabling
16702             if(t < min) {
16703                 cell.className = " fc-state-disabled";
16704                 cell.title = cal.minText;
16705                 return;
16706             }
16707             if(t > max) {
16708                 cell.className = " fc-state-disabled";
16709                 cell.title = cal.maxText;
16710                 return;
16711             }
16712             if(ddays){
16713                 if(ddays.indexOf(d.getDay()) != -1){
16714                     cell.title = ddaysText;
16715                     cell.className = " fc-state-disabled";
16716                 }
16717             }
16718             if(ddMatch && format){
16719                 var fvalue = d.dateFormat(format);
16720                 if(ddMatch.test(fvalue)){
16721                     cell.title = ddText.replace("%0", fvalue);
16722                     cell.className = " fc-state-disabled";
16723                 }
16724             }
16725             
16726             if (!cell.initialClassName) {
16727                 cell.initialClassName = cell.dom.className;
16728             }
16729             
16730             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
16731         };
16732
16733         var i = 0;
16734         
16735         for(; i < startingPos; i++) {
16736             textEls[i].innerHTML = (++prevStart);
16737             d.setDate(d.getDate()+1);
16738             
16739             cells[i].className = "fc-past fc-other-month";
16740             setCellClass(this, cells[i]);
16741         }
16742         
16743         var intDay = 0;
16744         
16745         for(; i < days; i++){
16746             intDay = i - startingPos + 1;
16747             textEls[i].innerHTML = (intDay);
16748             d.setDate(d.getDate()+1);
16749             
16750             cells[i].className = ''; // "x-date-active";
16751             setCellClass(this, cells[i]);
16752         }
16753         var extraDays = 0;
16754         
16755         for(; i < 42; i++) {
16756             textEls[i].innerHTML = (++extraDays);
16757             d.setDate(d.getDate()+1);
16758             
16759             cells[i].className = "fc-future fc-other-month";
16760             setCellClass(this, cells[i]);
16761         }
16762         
16763         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
16764         
16765         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
16766         
16767         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
16768         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
16769         
16770         if(totalRows != 6){
16771             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
16772             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
16773         }
16774         
16775         this.fireEvent('monthchange', this, date);
16776         
16777         
16778         /*
16779         if(!this.internalRender){
16780             var main = this.el.dom.firstChild;
16781             var w = main.offsetWidth;
16782             this.el.setWidth(w + this.el.getBorderWidth("lr"));
16783             Roo.fly(main).setWidth(w);
16784             this.internalRender = true;
16785             // opera does not respect the auto grow header center column
16786             // then, after it gets a width opera refuses to recalculate
16787             // without a second pass
16788             if(Roo.isOpera && !this.secondPass){
16789                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
16790                 this.secondPass = true;
16791                 this.update.defer(10, this, [date]);
16792             }
16793         }
16794         */
16795         
16796     },
16797     
16798     findCell : function(dt) {
16799         dt = dt.clearTime().getTime();
16800         var ret = false;
16801         this.cells.each(function(c){
16802             //Roo.log("check " +c.dateValue + '?=' + dt);
16803             if(c.dateValue == dt){
16804                 ret = c;
16805                 return false;
16806             }
16807             return true;
16808         });
16809         
16810         return ret;
16811     },
16812     
16813     findCells : function(ev) {
16814         var s = ev.start.clone().clearTime().getTime();
16815        // Roo.log(s);
16816         var e= ev.end.clone().clearTime().getTime();
16817        // Roo.log(e);
16818         var ret = [];
16819         this.cells.each(function(c){
16820              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
16821             
16822             if(c.dateValue > e){
16823                 return ;
16824             }
16825             if(c.dateValue < s){
16826                 return ;
16827             }
16828             ret.push(c);
16829         });
16830         
16831         return ret;    
16832     },
16833     
16834 //    findBestRow: function(cells)
16835 //    {
16836 //        var ret = 0;
16837 //        
16838 //        for (var i =0 ; i < cells.length;i++) {
16839 //            ret  = Math.max(cells[i].rows || 0,ret);
16840 //        }
16841 //        return ret;
16842 //        
16843 //    },
16844     
16845     
16846     addItem : function(ev)
16847     {
16848         // look for vertical location slot in
16849         var cells = this.findCells(ev);
16850         
16851 //        ev.row = this.findBestRow(cells);
16852         
16853         // work out the location.
16854         
16855         var crow = false;
16856         var rows = [];
16857         for(var i =0; i < cells.length; i++) {
16858             
16859             cells[i].row = cells[0].row;
16860             
16861             if(i == 0){
16862                 cells[i].row = cells[i].row + 1;
16863             }
16864             
16865             if (!crow) {
16866                 crow = {
16867                     start : cells[i],
16868                     end :  cells[i]
16869                 };
16870                 continue;
16871             }
16872             if (crow.start.getY() == cells[i].getY()) {
16873                 // on same row.
16874                 crow.end = cells[i];
16875                 continue;
16876             }
16877             // different row.
16878             rows.push(crow);
16879             crow = {
16880                 start: cells[i],
16881                 end : cells[i]
16882             };
16883             
16884         }
16885         
16886         rows.push(crow);
16887         ev.els = [];
16888         ev.rows = rows;
16889         ev.cells = cells;
16890         
16891         cells[0].events.push(ev);
16892         
16893         this.calevents.push(ev);
16894     },
16895     
16896     clearEvents: function() {
16897         
16898         if(!this.calevents){
16899             return;
16900         }
16901         
16902         Roo.each(this.cells.elements, function(c){
16903             c.row = 0;
16904             c.events = [];
16905             c.more = [];
16906         });
16907         
16908         Roo.each(this.calevents, function(e) {
16909             Roo.each(e.els, function(el) {
16910                 el.un('mouseenter' ,this.onEventEnter, this);
16911                 el.un('mouseleave' ,this.onEventLeave, this);
16912                 el.remove();
16913             },this);
16914         },this);
16915         
16916         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
16917             e.remove();
16918         });
16919         
16920     },
16921     
16922     renderEvents: function()
16923     {   
16924         var _this = this;
16925         
16926         this.cells.each(function(c) {
16927             
16928             if(c.row < 5){
16929                 return;
16930             }
16931             
16932             var ev = c.events;
16933             
16934             var r = 4;
16935             if(c.row != c.events.length){
16936                 r = 4 - (4 - (c.row - c.events.length));
16937             }
16938             
16939             c.events = ev.slice(0, r);
16940             c.more = ev.slice(r);
16941             
16942             if(c.more.length && c.more.length == 1){
16943                 c.events.push(c.more.pop());
16944             }
16945             
16946             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
16947             
16948         });
16949             
16950         this.cells.each(function(c) {
16951             
16952             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
16953             
16954             
16955             for (var e = 0; e < c.events.length; e++){
16956                 var ev = c.events[e];
16957                 var rows = ev.rows;
16958                 
16959                 for(var i = 0; i < rows.length; i++) {
16960                 
16961                     // how many rows should it span..
16962
16963                     var  cfg = {
16964                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
16965                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
16966
16967                         unselectable : "on",
16968                         cn : [
16969                             {
16970                                 cls: 'fc-event-inner',
16971                                 cn : [
16972     //                                {
16973     //                                  tag:'span',
16974     //                                  cls: 'fc-event-time',
16975     //                                  html : cells.length > 1 ? '' : ev.time
16976     //                                },
16977                                     {
16978                                       tag:'span',
16979                                       cls: 'fc-event-title',
16980                                       html : String.format('{0}', ev.title)
16981                                     }
16982
16983
16984                                 ]
16985                             },
16986                             {
16987                                 cls: 'ui-resizable-handle ui-resizable-e',
16988                                 html : '&nbsp;&nbsp;&nbsp'
16989                             }
16990
16991                         ]
16992                     };
16993
16994                     if (i == 0) {
16995                         cfg.cls += ' fc-event-start';
16996                     }
16997                     if ((i+1) == rows.length) {
16998                         cfg.cls += ' fc-event-end';
16999                     }
17000
17001                     var ctr = _this.el.select('.fc-event-container',true).first();
17002                     var cg = ctr.createChild(cfg);
17003
17004                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
17005                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
17006
17007                     var r = (c.more.length) ? 1 : 0;
17008                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
17009                     cg.setWidth(ebox.right - sbox.x -2);
17010
17011                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
17012                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
17013                     cg.on('click', _this.onEventClick, _this, ev);
17014
17015                     ev.els.push(cg);
17016                     
17017                 }
17018                 
17019             }
17020             
17021             
17022             if(c.more.length){
17023                 var  cfg = {
17024                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
17025                     style : 'position: absolute',
17026                     unselectable : "on",
17027                     cn : [
17028                         {
17029                             cls: 'fc-event-inner',
17030                             cn : [
17031                                 {
17032                                   tag:'span',
17033                                   cls: 'fc-event-title',
17034                                   html : 'More'
17035                                 }
17036
17037
17038                             ]
17039                         },
17040                         {
17041                             cls: 'ui-resizable-handle ui-resizable-e',
17042                             html : '&nbsp;&nbsp;&nbsp'
17043                         }
17044
17045                     ]
17046                 };
17047
17048                 var ctr = _this.el.select('.fc-event-container',true).first();
17049                 var cg = ctr.createChild(cfg);
17050
17051                 var sbox = c.select('.fc-day-content',true).first().getBox();
17052                 var ebox = c.select('.fc-day-content',true).first().getBox();
17053                 //Roo.log(cg);
17054                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
17055                 cg.setWidth(ebox.right - sbox.x -2);
17056
17057                 cg.on('click', _this.onMoreEventClick, _this, c.more);
17058                 
17059             }
17060             
17061         });
17062         
17063         
17064         
17065     },
17066     
17067     onEventEnter: function (e, el,event,d) {
17068         this.fireEvent('evententer', this, el, event);
17069     },
17070     
17071     onEventLeave: function (e, el,event,d) {
17072         this.fireEvent('eventleave', this, el, event);
17073     },
17074     
17075     onEventClick: function (e, el,event,d) {
17076         this.fireEvent('eventclick', this, el, event);
17077     },
17078     
17079     onMonthChange: function () {
17080         this.store.load();
17081     },
17082     
17083     onMoreEventClick: function(e, el, more)
17084     {
17085         var _this = this;
17086         
17087         this.calpopover.placement = 'right';
17088         this.calpopover.setTitle('More');
17089         
17090         this.calpopover.setContent('');
17091         
17092         var ctr = this.calpopover.el.select('.popover-content', true).first();
17093         
17094         Roo.each(more, function(m){
17095             var cfg = {
17096                 cls : 'fc-event-hori fc-event-draggable',
17097                 html : m.title
17098             };
17099             var cg = ctr.createChild(cfg);
17100             
17101             cg.on('click', _this.onEventClick, _this, m);
17102         });
17103         
17104         this.calpopover.show(el);
17105         
17106         
17107     },
17108     
17109     onLoad: function () 
17110     {   
17111         this.calevents = [];
17112         var cal = this;
17113         
17114         if(this.store.getCount() > 0){
17115             this.store.data.each(function(d){
17116                cal.addItem({
17117                     id : d.data.id,
17118                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
17119                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
17120                     time : d.data.start_time,
17121                     title : d.data.title,
17122                     description : d.data.description,
17123                     venue : d.data.venue
17124                 });
17125             });
17126         }
17127         
17128         this.renderEvents();
17129         
17130         if(this.calevents.length && this.loadMask){
17131             this.maskEl.hide();
17132         }
17133     },
17134     
17135     onBeforeLoad: function()
17136     {
17137         this.clearEvents();
17138         if(this.loadMask){
17139             this.maskEl.show();
17140         }
17141     }
17142 });
17143
17144  
17145  /*
17146  * - LGPL
17147  *
17148  * element
17149  * 
17150  */
17151
17152 /**
17153  * @class Roo.bootstrap.Popover
17154  * @extends Roo.bootstrap.Component
17155  * Bootstrap Popover class
17156  * @cfg {String} html contents of the popover   (or false to use children..)
17157  * @cfg {String} title of popover (or false to hide)
17158  * @cfg {String} placement how it is placed
17159  * @cfg {String} trigger click || hover (or false to trigger manually)
17160  * @cfg {String} over what (parent or false to trigger manually.)
17161  * @cfg {Number} delay - delay before showing
17162  
17163  * @constructor
17164  * Create a new Popover
17165  * @param {Object} config The config object
17166  */
17167
17168 Roo.bootstrap.Popover = function(config){
17169     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
17170     
17171     this.addEvents({
17172         // raw events
17173          /**
17174          * @event show
17175          * After the popover show
17176          * 
17177          * @param {Roo.bootstrap.Popover} this
17178          */
17179         "show" : true,
17180         /**
17181          * @event hide
17182          * After the popover hide
17183          * 
17184          * @param {Roo.bootstrap.Popover} this
17185          */
17186         "hide" : true
17187     });
17188 };
17189
17190 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
17191     
17192     title: 'Fill in a title',
17193     html: false,
17194     
17195     placement : 'right',
17196     trigger : 'hover', // hover
17197     
17198     delay : 0,
17199     
17200     over: 'parent',
17201     
17202     can_build_overlaid : false,
17203     
17204     getChildContainer : function()
17205     {
17206         return this.el.select('.popover-content',true).first();
17207     },
17208     
17209     getAutoCreate : function(){
17210          
17211         var cfg = {
17212            cls : 'popover roo-dynamic',
17213            style: 'display:block',
17214            cn : [
17215                 {
17216                     cls : 'arrow'
17217                 },
17218                 {
17219                     cls : 'popover-inner',
17220                     cn : [
17221                         {
17222                             tag: 'h3',
17223                             cls: 'popover-title',
17224                             html : this.title
17225                         },
17226                         {
17227                             cls : 'popover-content',
17228                             html : this.html
17229                         }
17230                     ]
17231                     
17232                 }
17233            ]
17234         };
17235         
17236         return cfg;
17237     },
17238     setTitle: function(str)
17239     {
17240         this.title = str;
17241         this.el.select('.popover-title',true).first().dom.innerHTML = str;
17242     },
17243     setContent: function(str)
17244     {
17245         this.html = str;
17246         this.el.select('.popover-content',true).first().dom.innerHTML = str;
17247     },
17248     // as it get's added to the bottom of the page.
17249     onRender : function(ct, position)
17250     {
17251         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
17252         if(!this.el){
17253             var cfg = Roo.apply({},  this.getAutoCreate());
17254             cfg.id = Roo.id();
17255             
17256             if (this.cls) {
17257                 cfg.cls += ' ' + this.cls;
17258             }
17259             if (this.style) {
17260                 cfg.style = this.style;
17261             }
17262             //Roo.log("adding to ");
17263             this.el = Roo.get(document.body).createChild(cfg, position);
17264 //            Roo.log(this.el);
17265         }
17266         this.initEvents();
17267     },
17268     
17269     initEvents : function()
17270     {
17271         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
17272         this.el.enableDisplayMode('block');
17273         this.el.hide();
17274         if (this.over === false) {
17275             return; 
17276         }
17277         if (this.triggers === false) {
17278             return;
17279         }
17280         var on_el = (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17281         var triggers = this.trigger ? this.trigger.split(' ') : [];
17282         Roo.each(triggers, function(trigger) {
17283         
17284             if (trigger == 'click') {
17285                 on_el.on('click', this.toggle, this);
17286             } else if (trigger != 'manual') {
17287                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
17288                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
17289       
17290                 on_el.on(eventIn  ,this.enter, this);
17291                 on_el.on(eventOut, this.leave, this);
17292             }
17293         }, this);
17294         
17295     },
17296     
17297     
17298     // private
17299     timeout : null,
17300     hoverState : null,
17301     
17302     toggle : function () {
17303         this.hoverState == 'in' ? this.leave() : this.enter();
17304     },
17305     
17306     enter : function () {
17307         
17308         clearTimeout(this.timeout);
17309     
17310         this.hoverState = 'in';
17311     
17312         if (!this.delay || !this.delay.show) {
17313             this.show();
17314             return;
17315         }
17316         var _t = this;
17317         this.timeout = setTimeout(function () {
17318             if (_t.hoverState == 'in') {
17319                 _t.show();
17320             }
17321         }, this.delay.show)
17322     },
17323     
17324     leave : function() {
17325         clearTimeout(this.timeout);
17326     
17327         this.hoverState = 'out';
17328     
17329         if (!this.delay || !this.delay.hide) {
17330             this.hide();
17331             return;
17332         }
17333         var _t = this;
17334         this.timeout = setTimeout(function () {
17335             if (_t.hoverState == 'out') {
17336                 _t.hide();
17337             }
17338         }, this.delay.hide)
17339     },
17340     
17341     show : function (on_el)
17342     {
17343         if (!on_el) {
17344             on_el= (this.over == 'parent') ? this.parent().el : Roo.get(this.over);
17345         }
17346         
17347         // set content.
17348         this.el.select('.popover-title',true).first().dom.innerHtml = this.title;
17349         if (this.html !== false) {
17350             this.el.select('.popover-content',true).first().dom.innerHtml = this.html;
17351         }
17352         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
17353         if (!this.title.length) {
17354             this.el.select('.popover-title',true).hide();
17355         }
17356         
17357         var placement = typeof this.placement == 'function' ?
17358             this.placement.call(this, this.el, on_el) :
17359             this.placement;
17360             
17361         var autoToken = /\s?auto?\s?/i;
17362         var autoPlace = autoToken.test(placement);
17363         if (autoPlace) {
17364             placement = placement.replace(autoToken, '') || 'top';
17365         }
17366         
17367         //this.el.detach()
17368         //this.el.setXY([0,0]);
17369         this.el.show();
17370         this.el.dom.style.display='block';
17371         this.el.addClass(placement);
17372         
17373         //this.el.appendTo(on_el);
17374         
17375         var p = this.getPosition();
17376         var box = this.el.getBox();
17377         
17378         if (autoPlace) {
17379             // fixme..
17380         }
17381         var align = Roo.bootstrap.Popover.alignment[placement];
17382         
17383 //        Roo.log(align);
17384         this.el.alignTo(on_el, align[0],align[1]);
17385         //var arrow = this.el.select('.arrow',true).first();
17386         //arrow.set(align[2], 
17387         
17388         this.el.addClass('in');
17389         
17390         
17391         if (this.el.hasClass('fade')) {
17392             // fade it?
17393         }
17394         
17395         this.hoverState = 'in';
17396         
17397         this.fireEvent('show', this);
17398         
17399     },
17400     hide : function()
17401     {
17402         this.el.setXY([0,0]);
17403         this.el.removeClass('in');
17404         this.el.hide();
17405         this.hoverState = null;
17406         
17407         this.fireEvent('hide', this);
17408     }
17409     
17410 });
17411
17412 Roo.bootstrap.Popover.alignment = {
17413     'left' : ['r-l', [-10,0], 'right'],
17414     'right' : ['l-r', [10,0], 'left'],
17415     'bottom' : ['t-b', [0,10], 'top'],
17416     'top' : [ 'b-t', [0,-10], 'bottom']
17417 };
17418
17419  /*
17420  * - LGPL
17421  *
17422  * Progress
17423  * 
17424  */
17425
17426 /**
17427  * @class Roo.bootstrap.Progress
17428  * @extends Roo.bootstrap.Component
17429  * Bootstrap Progress class
17430  * @cfg {Boolean} striped striped of the progress bar
17431  * @cfg {Boolean} active animated of the progress bar
17432  * 
17433  * 
17434  * @constructor
17435  * Create a new Progress
17436  * @param {Object} config The config object
17437  */
17438
17439 Roo.bootstrap.Progress = function(config){
17440     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
17441 };
17442
17443 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
17444     
17445     striped : false,
17446     active: false,
17447     
17448     getAutoCreate : function(){
17449         var cfg = {
17450             tag: 'div',
17451             cls: 'progress'
17452         };
17453         
17454         
17455         if(this.striped){
17456             cfg.cls += ' progress-striped';
17457         }
17458       
17459         if(this.active){
17460             cfg.cls += ' active';
17461         }
17462         
17463         
17464         return cfg;
17465     }
17466    
17467 });
17468
17469  
17470
17471  /*
17472  * - LGPL
17473  *
17474  * ProgressBar
17475  * 
17476  */
17477
17478 /**
17479  * @class Roo.bootstrap.ProgressBar
17480  * @extends Roo.bootstrap.Component
17481  * Bootstrap ProgressBar class
17482  * @cfg {Number} aria_valuenow aria-value now
17483  * @cfg {Number} aria_valuemin aria-value min
17484  * @cfg {Number} aria_valuemax aria-value max
17485  * @cfg {String} label label for the progress bar
17486  * @cfg {String} panel (success | info | warning | danger )
17487  * @cfg {String} role role of the progress bar
17488  * @cfg {String} sr_only text
17489  * 
17490  * 
17491  * @constructor
17492  * Create a new ProgressBar
17493  * @param {Object} config The config object
17494  */
17495
17496 Roo.bootstrap.ProgressBar = function(config){
17497     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
17498 };
17499
17500 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
17501     
17502     aria_valuenow : 0,
17503     aria_valuemin : 0,
17504     aria_valuemax : 100,
17505     label : false,
17506     panel : false,
17507     role : false,
17508     sr_only: false,
17509     
17510     getAutoCreate : function()
17511     {
17512         
17513         var cfg = {
17514             tag: 'div',
17515             cls: 'progress-bar',
17516             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
17517         };
17518         
17519         if(this.sr_only){
17520             cfg.cn = {
17521                 tag: 'span',
17522                 cls: 'sr-only',
17523                 html: this.sr_only
17524             }
17525         }
17526         
17527         if(this.role){
17528             cfg.role = this.role;
17529         }
17530         
17531         if(this.aria_valuenow){
17532             cfg['aria-valuenow'] = this.aria_valuenow;
17533         }
17534         
17535         if(this.aria_valuemin){
17536             cfg['aria-valuemin'] = this.aria_valuemin;
17537         }
17538         
17539         if(this.aria_valuemax){
17540             cfg['aria-valuemax'] = this.aria_valuemax;
17541         }
17542         
17543         if(this.label && !this.sr_only){
17544             cfg.html = this.label;
17545         }
17546         
17547         if(this.panel){
17548             cfg.cls += ' progress-bar-' + this.panel;
17549         }
17550         
17551         return cfg;
17552     },
17553     
17554     update : function(aria_valuenow)
17555     {
17556         this.aria_valuenow = aria_valuenow;
17557         
17558         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
17559     }
17560    
17561 });
17562
17563  
17564
17565  /*
17566  * - LGPL
17567  *
17568  * column
17569  * 
17570  */
17571
17572 /**
17573  * @class Roo.bootstrap.TabGroup
17574  * @extends Roo.bootstrap.Column
17575  * Bootstrap Column class
17576  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
17577  * @cfg {Boolean} carousel true to make the group behave like a carousel
17578  * @cfg {Boolean} bullets show bullets for the panels
17579  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
17580  * @cfg {Number} timer auto slide timer .. default 0 millisecond
17581  * @cfg {Boolean} showarrow (true|false) show arrow default true
17582  * 
17583  * @constructor
17584  * Create a new TabGroup
17585  * @param {Object} config The config object
17586  */
17587
17588 Roo.bootstrap.TabGroup = function(config){
17589     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
17590     if (!this.navId) {
17591         this.navId = Roo.id();
17592     }
17593     this.tabs = [];
17594     Roo.bootstrap.TabGroup.register(this);
17595     
17596 };
17597
17598 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
17599     
17600     carousel : false,
17601     transition : false,
17602     bullets : 0,
17603     timer : 0,
17604     autoslide : false,
17605     slideFn : false,
17606     slideOnTouch : false,
17607     showarrow : true,
17608     
17609     getAutoCreate : function()
17610     {
17611         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
17612         
17613         cfg.cls += ' tab-content';
17614         
17615         if (this.carousel) {
17616             cfg.cls += ' carousel slide';
17617             
17618             cfg.cn = [{
17619                cls : 'carousel-inner',
17620                cn : []
17621             }];
17622         
17623             if(this.bullets  && !Roo.isTouch){
17624                 
17625                 var bullets = {
17626                     cls : 'carousel-bullets',
17627                     cn : []
17628                 };
17629                
17630                 if(this.bullets_cls){
17631                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
17632                 }
17633                 
17634                 bullets.cn.push({
17635                     cls : 'clear'
17636                 });
17637                 
17638                 cfg.cn[0].cn.push(bullets);
17639             }
17640             
17641             if(this.showarrow){
17642                 cfg.cn[0].cn.push({
17643                     tag : 'div',
17644                     class : 'carousel-arrow',
17645                     cn : [
17646                         {
17647                             tag : 'div',
17648                             class : 'carousel-prev',
17649                             cn : [
17650                                 {
17651                                     tag : 'i',
17652                                     class : 'fa fa-chevron-left'
17653                                 }
17654                             ]
17655                         },
17656                         {
17657                             tag : 'div',
17658                             class : 'carousel-next',
17659                             cn : [
17660                                 {
17661                                     tag : 'i',
17662                                     class : 'fa fa-chevron-right'
17663                                 }
17664                             ]
17665                         }
17666                     ]
17667                 });
17668             }
17669             
17670         }
17671         
17672         return cfg;
17673     },
17674     
17675     initEvents:  function()
17676     {
17677 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
17678 //            this.el.on("touchstart", this.onTouchStart, this);
17679 //        }
17680         
17681         if(this.autoslide){
17682             var _this = this;
17683             
17684             this.slideFn = window.setInterval(function() {
17685                 _this.showPanelNext();
17686             }, this.timer);
17687         }
17688         
17689         if(this.showarrow){
17690             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
17691             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
17692         }
17693         
17694         
17695     },
17696     
17697 //    onTouchStart : function(e, el, o)
17698 //    {
17699 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
17700 //            return;
17701 //        }
17702 //        
17703 //        this.showPanelNext();
17704 //    },
17705     
17706     
17707     getChildContainer : function()
17708     {
17709         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
17710     },
17711     
17712     /**
17713     * register a Navigation item
17714     * @param {Roo.bootstrap.NavItem} the navitem to add
17715     */
17716     register : function(item)
17717     {
17718         this.tabs.push( item);
17719         item.navId = this.navId; // not really needed..
17720         this.addBullet();
17721     
17722     },
17723     
17724     getActivePanel : function()
17725     {
17726         var r = false;
17727         Roo.each(this.tabs, function(t) {
17728             if (t.active) {
17729                 r = t;
17730                 return false;
17731             }
17732             return null;
17733         });
17734         return r;
17735         
17736     },
17737     getPanelByName : function(n)
17738     {
17739         var r = false;
17740         Roo.each(this.tabs, function(t) {
17741             if (t.tabId == n) {
17742                 r = t;
17743                 return false;
17744             }
17745             return null;
17746         });
17747         return r;
17748     },
17749     indexOfPanel : function(p)
17750     {
17751         var r = false;
17752         Roo.each(this.tabs, function(t,i) {
17753             if (t.tabId == p.tabId) {
17754                 r = i;
17755                 return false;
17756             }
17757             return null;
17758         });
17759         return r;
17760     },
17761     /**
17762      * show a specific panel
17763      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
17764      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
17765      */
17766     showPanel : function (pan)
17767     {
17768         if(this.transition || typeof(pan) == 'undefined'){
17769             Roo.log("waiting for the transitionend");
17770             return;
17771         }
17772         
17773         if (typeof(pan) == 'number') {
17774             pan = this.tabs[pan];
17775         }
17776         
17777         if (typeof(pan) == 'string') {
17778             pan = this.getPanelByName(pan);
17779         }
17780         
17781         var cur = this.getActivePanel();
17782         
17783         if(!pan || !cur){
17784             Roo.log('pan or acitve pan is undefined');
17785             return false;
17786         }
17787         
17788         if (pan.tabId == this.getActivePanel().tabId) {
17789             return true;
17790         }
17791         
17792         if (false === cur.fireEvent('beforedeactivate')) {
17793             return false;
17794         }
17795         
17796         if(this.bullets > 0 && !Roo.isTouch){
17797             this.setActiveBullet(this.indexOfPanel(pan));
17798         }
17799         
17800         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
17801             
17802             this.transition = true;
17803             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
17804             var lr = dir == 'next' ? 'left' : 'right';
17805             pan.el.addClass(dir); // or prev
17806             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
17807             cur.el.addClass(lr); // or right
17808             pan.el.addClass(lr);
17809             
17810             var _this = this;
17811             cur.el.on('transitionend', function() {
17812                 Roo.log("trans end?");
17813                 
17814                 pan.el.removeClass([lr,dir]);
17815                 pan.setActive(true);
17816                 
17817                 cur.el.removeClass([lr]);
17818                 cur.setActive(false);
17819                 
17820                 _this.transition = false;
17821                 
17822             }, this, { single:  true } );
17823             
17824             return true;
17825         }
17826         
17827         cur.setActive(false);
17828         pan.setActive(true);
17829         
17830         return true;
17831         
17832     },
17833     showPanelNext : function()
17834     {
17835         var i = this.indexOfPanel(this.getActivePanel());
17836         
17837         if (i >= this.tabs.length - 1 && !this.autoslide) {
17838             return;
17839         }
17840         
17841         if (i >= this.tabs.length - 1 && this.autoslide) {
17842             i = -1;
17843         }
17844         
17845         this.showPanel(this.tabs[i+1]);
17846     },
17847     
17848     showPanelPrev : function()
17849     {
17850         var i = this.indexOfPanel(this.getActivePanel());
17851         
17852         if (i  < 1 && !this.autoslide) {
17853             return;
17854         }
17855         
17856         if (i < 1 && this.autoslide) {
17857             i = this.tabs.length;
17858         }
17859         
17860         this.showPanel(this.tabs[i-1]);
17861     },
17862     
17863     
17864     addBullet: function()
17865     {
17866         if(!this.bullets || Roo.isTouch){
17867             return;
17868         }
17869         var ctr = this.el.select('.carousel-bullets',true).first();
17870         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
17871         var bullet = ctr.createChild({
17872             cls : 'bullet bullet-' + i
17873         },ctr.dom.lastChild);
17874         
17875         
17876         var _this = this;
17877         
17878         bullet.on('click', (function(e, el, o, ii, t){
17879
17880             e.preventDefault();
17881
17882             this.showPanel(ii);
17883
17884             if(this.autoslide && this.slideFn){
17885                 clearInterval(this.slideFn);
17886                 this.slideFn = window.setInterval(function() {
17887                     _this.showPanelNext();
17888                 }, this.timer);
17889             }
17890
17891         }).createDelegate(this, [i, bullet], true));
17892                 
17893         
17894     },
17895      
17896     setActiveBullet : function(i)
17897     {
17898         if(Roo.isTouch){
17899             return;
17900         }
17901         
17902         Roo.each(this.el.select('.bullet', true).elements, function(el){
17903             el.removeClass('selected');
17904         });
17905
17906         var bullet = this.el.select('.bullet-' + i, true).first();
17907         
17908         if(!bullet){
17909             return;
17910         }
17911         
17912         bullet.addClass('selected');
17913     }
17914     
17915     
17916   
17917 });
17918
17919  
17920
17921  
17922  
17923 Roo.apply(Roo.bootstrap.TabGroup, {
17924     
17925     groups: {},
17926      /**
17927     * register a Navigation Group
17928     * @param {Roo.bootstrap.NavGroup} the navgroup to add
17929     */
17930     register : function(navgrp)
17931     {
17932         this.groups[navgrp.navId] = navgrp;
17933         
17934     },
17935     /**
17936     * fetch a Navigation Group based on the navigation ID
17937     * if one does not exist , it will get created.
17938     * @param {string} the navgroup to add
17939     * @returns {Roo.bootstrap.NavGroup} the navgroup 
17940     */
17941     get: function(navId) {
17942         if (typeof(this.groups[navId]) == 'undefined') {
17943             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
17944         }
17945         return this.groups[navId] ;
17946     }
17947     
17948     
17949     
17950 });
17951
17952  /*
17953  * - LGPL
17954  *
17955  * TabPanel
17956  * 
17957  */
17958
17959 /**
17960  * @class Roo.bootstrap.TabPanel
17961  * @extends Roo.bootstrap.Component
17962  * Bootstrap TabPanel class
17963  * @cfg {Boolean} active panel active
17964  * @cfg {String} html panel content
17965  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
17966  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
17967  * @cfg {String} href click to link..
17968  * 
17969  * 
17970  * @constructor
17971  * Create a new TabPanel
17972  * @param {Object} config The config object
17973  */
17974
17975 Roo.bootstrap.TabPanel = function(config){
17976     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
17977     this.addEvents({
17978         /**
17979              * @event changed
17980              * Fires when the active status changes
17981              * @param {Roo.bootstrap.TabPanel} this
17982              * @param {Boolean} state the new state
17983             
17984          */
17985         'changed': true,
17986         /**
17987              * @event beforedeactivate
17988              * Fires before a tab is de-activated - can be used to do validation on a form.
17989              * @param {Roo.bootstrap.TabPanel} this
17990              * @return {Boolean} false if there is an error
17991             
17992          */
17993         'beforedeactivate': true
17994      });
17995     
17996     this.tabId = this.tabId || Roo.id();
17997   
17998 };
17999
18000 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
18001     
18002     active: false,
18003     html: false,
18004     tabId: false,
18005     navId : false,
18006     href : '',
18007     
18008     getAutoCreate : function(){
18009         var cfg = {
18010             tag: 'div',
18011             // item is needed for carousel - not sure if it has any effect otherwise
18012             cls: 'tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
18013             html: this.html || ''
18014         };
18015         
18016         if(this.active){
18017             cfg.cls += ' active';
18018         }
18019         
18020         if(this.tabId){
18021             cfg.tabId = this.tabId;
18022         }
18023         
18024         
18025         return cfg;
18026     },
18027     
18028     initEvents:  function()
18029     {
18030         var p = this.parent();
18031         
18032         this.navId = this.navId || p.navId;
18033         
18034         if (typeof(this.navId) != 'undefined') {
18035             // not really needed.. but just in case.. parent should be a NavGroup.
18036             var tg = Roo.bootstrap.TabGroup.get(this.navId);
18037             
18038             tg.register(this);
18039             
18040             var i = tg.tabs.length - 1;
18041             
18042             if(this.active && tg.bullets > 0 && i < tg.bullets){
18043                 tg.setActiveBullet(i);
18044             }
18045         }
18046         
18047         this.el.on('click', this.onClick, this);
18048         
18049         if(Roo.isTouch){
18050             this.el.on("touchstart", this.onTouchStart, this);
18051             this.el.on("touchmove", this.onTouchMove, this);
18052             this.el.on("touchend", this.onTouchEnd, this);
18053         }
18054         
18055     },
18056     
18057     onRender : function(ct, position)
18058     {
18059         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
18060     },
18061     
18062     setActive : function(state)
18063     {
18064         Roo.log("panel - set active " + this.tabId + "=" + state);
18065         
18066         this.active = state;
18067         if (!state) {
18068             this.el.removeClass('active');
18069             
18070         } else  if (!this.el.hasClass('active')) {
18071             this.el.addClass('active');
18072         }
18073         
18074         this.fireEvent('changed', this, state);
18075     },
18076     
18077     onClick : function(e)
18078     {
18079         e.preventDefault();
18080         
18081         if(!this.href.length){
18082             return;
18083         }
18084         
18085         window.location.href = this.href;
18086     },
18087     
18088     startX : 0,
18089     startY : 0,
18090     endX : 0,
18091     endY : 0,
18092     swiping : false,
18093     
18094     onTouchStart : function(e)
18095     {
18096         this.swiping = false;
18097         
18098         this.startX = e.browserEvent.touches[0].clientX;
18099         this.startY = e.browserEvent.touches[0].clientY;
18100     },
18101     
18102     onTouchMove : function(e)
18103     {
18104         this.swiping = true;
18105         
18106         this.endX = e.browserEvent.touches[0].clientX;
18107         this.endY = e.browserEvent.touches[0].clientY;
18108     },
18109     
18110     onTouchEnd : function(e)
18111     {
18112         if(!this.swiping){
18113             this.onClick(e);
18114             return;
18115         }
18116         
18117         var tabGroup = this.parent();
18118         
18119         if(this.endX > this.startX){ // swiping right
18120             tabGroup.showPanelPrev();
18121             return;
18122         }
18123         
18124         if(this.startX > this.endX){ // swiping left
18125             tabGroup.showPanelNext();
18126             return;
18127         }
18128     }
18129     
18130     
18131 });
18132  
18133
18134  
18135
18136  /*
18137  * - LGPL
18138  *
18139  * DateField
18140  * 
18141  */
18142
18143 /**
18144  * @class Roo.bootstrap.DateField
18145  * @extends Roo.bootstrap.Input
18146  * Bootstrap DateField class
18147  * @cfg {Number} weekStart default 0
18148  * @cfg {String} viewMode default empty, (months|years)
18149  * @cfg {String} minViewMode default empty, (months|years)
18150  * @cfg {Number} startDate default -Infinity
18151  * @cfg {Number} endDate default Infinity
18152  * @cfg {Boolean} todayHighlight default false
18153  * @cfg {Boolean} todayBtn default false
18154  * @cfg {Boolean} calendarWeeks default false
18155  * @cfg {Object} daysOfWeekDisabled default empty
18156  * @cfg {Boolean} singleMode default false (true | false)
18157  * 
18158  * @cfg {Boolean} keyboardNavigation default true
18159  * @cfg {String} language default en
18160  * 
18161  * @constructor
18162  * Create a new DateField
18163  * @param {Object} config The config object
18164  */
18165
18166 Roo.bootstrap.DateField = function(config){
18167     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
18168      this.addEvents({
18169             /**
18170              * @event show
18171              * Fires when this field show.
18172              * @param {Roo.bootstrap.DateField} this
18173              * @param {Mixed} date The date value
18174              */
18175             show : true,
18176             /**
18177              * @event show
18178              * Fires when this field hide.
18179              * @param {Roo.bootstrap.DateField} this
18180              * @param {Mixed} date The date value
18181              */
18182             hide : true,
18183             /**
18184              * @event select
18185              * Fires when select a date.
18186              * @param {Roo.bootstrap.DateField} this
18187              * @param {Mixed} date The date value
18188              */
18189             select : true,
18190             /**
18191              * @event beforeselect
18192              * Fires when before select a date.
18193              * @param {Roo.bootstrap.DateField} this
18194              * @param {Mixed} date The date value
18195              */
18196             beforeselect : true
18197         });
18198 };
18199
18200 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
18201     
18202     /**
18203      * @cfg {String} format
18204      * The default date format string which can be overriden for localization support.  The format must be
18205      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
18206      */
18207     format : "m/d/y",
18208     /**
18209      * @cfg {String} altFormats
18210      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
18211      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
18212      */
18213     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
18214     
18215     weekStart : 0,
18216     
18217     viewMode : '',
18218     
18219     minViewMode : '',
18220     
18221     todayHighlight : false,
18222     
18223     todayBtn: false,
18224     
18225     language: 'en',
18226     
18227     keyboardNavigation: true,
18228     
18229     calendarWeeks: false,
18230     
18231     startDate: -Infinity,
18232     
18233     endDate: Infinity,
18234     
18235     daysOfWeekDisabled: [],
18236     
18237     _events: [],
18238     
18239     singleMode : false,
18240     
18241     UTCDate: function()
18242     {
18243         return new Date(Date.UTC.apply(Date, arguments));
18244     },
18245     
18246     UTCToday: function()
18247     {
18248         var today = new Date();
18249         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
18250     },
18251     
18252     getDate: function() {
18253             var d = this.getUTCDate();
18254             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
18255     },
18256     
18257     getUTCDate: function() {
18258             return this.date;
18259     },
18260     
18261     setDate: function(d) {
18262             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
18263     },
18264     
18265     setUTCDate: function(d) {
18266             this.date = d;
18267             this.setValue(this.formatDate(this.date));
18268     },
18269         
18270     onRender: function(ct, position)
18271     {
18272         
18273         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
18274         
18275         this.language = this.language || 'en';
18276         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
18277         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
18278         
18279         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
18280         this.format = this.format || 'm/d/y';
18281         this.isInline = false;
18282         this.isInput = true;
18283         this.component = this.el.select('.add-on', true).first() || false;
18284         this.component = (this.component && this.component.length === 0) ? false : this.component;
18285         this.hasInput = this.component && this.inputEl().length;
18286         
18287         if (typeof(this.minViewMode === 'string')) {
18288             switch (this.minViewMode) {
18289                 case 'months':
18290                     this.minViewMode = 1;
18291                     break;
18292                 case 'years':
18293                     this.minViewMode = 2;
18294                     break;
18295                 default:
18296                     this.minViewMode = 0;
18297                     break;
18298             }
18299         }
18300         
18301         if (typeof(this.viewMode === 'string')) {
18302             switch (this.viewMode) {
18303                 case 'months':
18304                     this.viewMode = 1;
18305                     break;
18306                 case 'years':
18307                     this.viewMode = 2;
18308                     break;
18309                 default:
18310                     this.viewMode = 0;
18311                     break;
18312             }
18313         }
18314                 
18315         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
18316         
18317 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
18318         
18319         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18320         
18321         this.picker().on('mousedown', this.onMousedown, this);
18322         this.picker().on('click', this.onClick, this);
18323         
18324         this.picker().addClass('datepicker-dropdown');
18325         
18326         this.startViewMode = this.viewMode;
18327         
18328         if(this.singleMode){
18329             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
18330                 v.setVisibilityMode(Roo.Element.DISPLAY);
18331                 v.hide();
18332             });
18333             
18334             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
18335                 v.setStyle('width', '189px');
18336             });
18337         }
18338         
18339         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
18340             if(!this.calendarWeeks){
18341                 v.remove();
18342                 return;
18343             }
18344             
18345             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18346             v.attr('colspan', function(i, val){
18347                 return parseInt(val) + 1;
18348             });
18349         });
18350                         
18351         
18352         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
18353         
18354         this.setStartDate(this.startDate);
18355         this.setEndDate(this.endDate);
18356         
18357         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
18358         
18359         this.fillDow();
18360         this.fillMonths();
18361         this.update();
18362         this.showMode();
18363         
18364         if(this.isInline) {
18365             this.show();
18366         }
18367     },
18368     
18369     picker : function()
18370     {
18371         return this.pickerEl;
18372 //        return this.el.select('.datepicker', true).first();
18373     },
18374     
18375     fillDow: function()
18376     {
18377         var dowCnt = this.weekStart;
18378         
18379         var dow = {
18380             tag: 'tr',
18381             cn: [
18382                 
18383             ]
18384         };
18385         
18386         if(this.calendarWeeks){
18387             dow.cn.push({
18388                 tag: 'th',
18389                 cls: 'cw',
18390                 html: '&nbsp;'
18391             })
18392         }
18393         
18394         while (dowCnt < this.weekStart + 7) {
18395             dow.cn.push({
18396                 tag: 'th',
18397                 cls: 'dow',
18398                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
18399             });
18400         }
18401         
18402         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
18403     },
18404     
18405     fillMonths: function()
18406     {    
18407         var i = 0;
18408         var months = this.picker().select('>.datepicker-months td', true).first();
18409         
18410         months.dom.innerHTML = '';
18411         
18412         while (i < 12) {
18413             var month = {
18414                 tag: 'span',
18415                 cls: 'month',
18416                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
18417             };
18418             
18419             months.createChild(month);
18420         }
18421         
18422     },
18423     
18424     update: function()
18425     {
18426         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;
18427         
18428         if (this.date < this.startDate) {
18429             this.viewDate = new Date(this.startDate);
18430         } else if (this.date > this.endDate) {
18431             this.viewDate = new Date(this.endDate);
18432         } else {
18433             this.viewDate = new Date(this.date);
18434         }
18435         
18436         this.fill();
18437     },
18438     
18439     fill: function() 
18440     {
18441         var d = new Date(this.viewDate),
18442                 year = d.getUTCFullYear(),
18443                 month = d.getUTCMonth(),
18444                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
18445                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
18446                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
18447                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
18448                 currentDate = this.date && this.date.valueOf(),
18449                 today = this.UTCToday();
18450         
18451         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
18452         
18453 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
18454         
18455 //        this.picker.select('>tfoot th.today').
18456 //                                              .text(dates[this.language].today)
18457 //                                              .toggle(this.todayBtn !== false);
18458     
18459         this.updateNavArrows();
18460         this.fillMonths();
18461                                                 
18462         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
18463         
18464         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
18465          
18466         prevMonth.setUTCDate(day);
18467         
18468         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
18469         
18470         var nextMonth = new Date(prevMonth);
18471         
18472         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
18473         
18474         nextMonth = nextMonth.valueOf();
18475         
18476         var fillMonths = false;
18477         
18478         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
18479         
18480         while(prevMonth.valueOf() < nextMonth) {
18481             var clsName = '';
18482             
18483             if (prevMonth.getUTCDay() === this.weekStart) {
18484                 if(fillMonths){
18485                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
18486                 }
18487                     
18488                 fillMonths = {
18489                     tag: 'tr',
18490                     cn: []
18491                 };
18492                 
18493                 if(this.calendarWeeks){
18494                     // ISO 8601: First week contains first thursday.
18495                     // ISO also states week starts on Monday, but we can be more abstract here.
18496                     var
18497                     // Start of current week: based on weekstart/current date
18498                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
18499                     // Thursday of this week
18500                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
18501                     // First Thursday of year, year from thursday
18502                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
18503                     // Calendar week: ms between thursdays, div ms per day, div 7 days
18504                     calWeek =  (th - yth) / 864e5 / 7 + 1;
18505                     
18506                     fillMonths.cn.push({
18507                         tag: 'td',
18508                         cls: 'cw',
18509                         html: calWeek
18510                     });
18511                 }
18512             }
18513             
18514             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
18515                 clsName += ' old';
18516             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
18517                 clsName += ' new';
18518             }
18519             if (this.todayHighlight &&
18520                 prevMonth.getUTCFullYear() == today.getFullYear() &&
18521                 prevMonth.getUTCMonth() == today.getMonth() &&
18522                 prevMonth.getUTCDate() == today.getDate()) {
18523                 clsName += ' today';
18524             }
18525             
18526             if (currentDate && prevMonth.valueOf() === currentDate) {
18527                 clsName += ' active';
18528             }
18529             
18530             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
18531                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
18532                     clsName += ' disabled';
18533             }
18534             
18535             fillMonths.cn.push({
18536                 tag: 'td',
18537                 cls: 'day ' + clsName,
18538                 html: prevMonth.getDate()
18539             });
18540             
18541             prevMonth.setDate(prevMonth.getDate()+1);
18542         }
18543           
18544         var currentYear = this.date && this.date.getUTCFullYear();
18545         var currentMonth = this.date && this.date.getUTCMonth();
18546         
18547         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
18548         
18549         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
18550             v.removeClass('active');
18551             
18552             if(currentYear === year && k === currentMonth){
18553                 v.addClass('active');
18554             }
18555             
18556             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
18557                 v.addClass('disabled');
18558             }
18559             
18560         });
18561         
18562         
18563         year = parseInt(year/10, 10) * 10;
18564         
18565         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
18566         
18567         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
18568         
18569         year -= 1;
18570         for (var i = -1; i < 11; i++) {
18571             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
18572                 tag: 'span',
18573                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
18574                 html: year
18575             });
18576             
18577             year += 1;
18578         }
18579     },
18580     
18581     showMode: function(dir) 
18582     {
18583         if (dir) {
18584             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
18585         }
18586         
18587         Roo.each(this.picker().select('>div',true).elements, function(v){
18588             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18589             v.hide();
18590         });
18591         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
18592     },
18593     
18594     place: function()
18595     {
18596         if(this.isInline) {
18597             return;
18598         }
18599         
18600         this.picker().removeClass(['bottom', 'top']);
18601         
18602         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
18603             /*
18604              * place to the top of element!
18605              *
18606              */
18607             
18608             this.picker().addClass('top');
18609             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
18610             
18611             return;
18612         }
18613         
18614         this.picker().addClass('bottom');
18615         
18616         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
18617     },
18618     
18619     parseDate : function(value)
18620     {
18621         if(!value || value instanceof Date){
18622             return value;
18623         }
18624         var v = Date.parseDate(value, this.format);
18625         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
18626             v = Date.parseDate(value, 'Y-m-d');
18627         }
18628         if(!v && this.altFormats){
18629             if(!this.altFormatsArray){
18630                 this.altFormatsArray = this.altFormats.split("|");
18631             }
18632             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
18633                 v = Date.parseDate(value, this.altFormatsArray[i]);
18634             }
18635         }
18636         return v;
18637     },
18638     
18639     formatDate : function(date, fmt)
18640     {   
18641         return (!date || !(date instanceof Date)) ?
18642         date : date.dateFormat(fmt || this.format);
18643     },
18644     
18645     onFocus : function()
18646     {
18647         Roo.bootstrap.DateField.superclass.onFocus.call(this);
18648         this.show();
18649     },
18650     
18651     onBlur : function()
18652     {
18653         Roo.bootstrap.DateField.superclass.onBlur.call(this);
18654         
18655         var d = this.inputEl().getValue();
18656         
18657         this.setValue(d);
18658                 
18659         this.hide();
18660     },
18661     
18662     show : function()
18663     {
18664         this.picker().show();
18665         this.update();
18666         this.place();
18667         
18668         this.fireEvent('show', this, this.date);
18669     },
18670     
18671     hide : function()
18672     {
18673         if(this.isInline) {
18674             return;
18675         }
18676         this.picker().hide();
18677         this.viewMode = this.startViewMode;
18678         this.showMode();
18679         
18680         this.fireEvent('hide', this, this.date);
18681         
18682     },
18683     
18684     onMousedown: function(e)
18685     {
18686         e.stopPropagation();
18687         e.preventDefault();
18688     },
18689     
18690     keyup: function(e)
18691     {
18692         Roo.bootstrap.DateField.superclass.keyup.call(this);
18693         this.update();
18694     },
18695
18696     setValue: function(v)
18697     {
18698         if(this.fireEvent('beforeselect', this, v) !== false){
18699             var d = new Date(this.parseDate(v) ).clearTime();
18700         
18701             if(isNaN(d.getTime())){
18702                 this.date = this.viewDate = '';
18703                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
18704                 return;
18705             }
18706
18707             v = this.formatDate(d);
18708
18709             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
18710
18711             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
18712
18713             this.update();
18714
18715             this.fireEvent('select', this, this.date);
18716         }
18717     },
18718     
18719     getValue: function()
18720     {
18721         return this.formatDate(this.date);
18722     },
18723     
18724     fireKey: function(e)
18725     {
18726         if (!this.picker().isVisible()){
18727             if (e.keyCode == 27) { // allow escape to hide and re-show picker
18728                 this.show();
18729             }
18730             return;
18731         }
18732         
18733         var dateChanged = false,
18734         dir, day, month,
18735         newDate, newViewDate;
18736         
18737         switch(e.keyCode){
18738             case 27: // escape
18739                 this.hide();
18740                 e.preventDefault();
18741                 break;
18742             case 37: // left
18743             case 39: // right
18744                 if (!this.keyboardNavigation) {
18745                     break;
18746                 }
18747                 dir = e.keyCode == 37 ? -1 : 1;
18748                 
18749                 if (e.ctrlKey){
18750                     newDate = this.moveYear(this.date, dir);
18751                     newViewDate = this.moveYear(this.viewDate, dir);
18752                 } else if (e.shiftKey){
18753                     newDate = this.moveMonth(this.date, dir);
18754                     newViewDate = this.moveMonth(this.viewDate, dir);
18755                 } else {
18756                     newDate = new Date(this.date);
18757                     newDate.setUTCDate(this.date.getUTCDate() + dir);
18758                     newViewDate = new Date(this.viewDate);
18759                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
18760                 }
18761                 if (this.dateWithinRange(newDate)){
18762                     this.date = newDate;
18763                     this.viewDate = newViewDate;
18764                     this.setValue(this.formatDate(this.date));
18765 //                    this.update();
18766                     e.preventDefault();
18767                     dateChanged = true;
18768                 }
18769                 break;
18770             case 38: // up
18771             case 40: // down
18772                 if (!this.keyboardNavigation) {
18773                     break;
18774                 }
18775                 dir = e.keyCode == 38 ? -1 : 1;
18776                 if (e.ctrlKey){
18777                     newDate = this.moveYear(this.date, dir);
18778                     newViewDate = this.moveYear(this.viewDate, dir);
18779                 } else if (e.shiftKey){
18780                     newDate = this.moveMonth(this.date, dir);
18781                     newViewDate = this.moveMonth(this.viewDate, dir);
18782                 } else {
18783                     newDate = new Date(this.date);
18784                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
18785                     newViewDate = new Date(this.viewDate);
18786                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
18787                 }
18788                 if (this.dateWithinRange(newDate)){
18789                     this.date = newDate;
18790                     this.viewDate = newViewDate;
18791                     this.setValue(this.formatDate(this.date));
18792 //                    this.update();
18793                     e.preventDefault();
18794                     dateChanged = true;
18795                 }
18796                 break;
18797             case 13: // enter
18798                 this.setValue(this.formatDate(this.date));
18799                 this.hide();
18800                 e.preventDefault();
18801                 break;
18802             case 9: // tab
18803                 this.setValue(this.formatDate(this.date));
18804                 this.hide();
18805                 break;
18806             case 16: // shift
18807             case 17: // ctrl
18808             case 18: // alt
18809                 break;
18810             default :
18811                 this.hide();
18812                 
18813         }
18814     },
18815     
18816     
18817     onClick: function(e) 
18818     {
18819         e.stopPropagation();
18820         e.preventDefault();
18821         
18822         var target = e.getTarget();
18823         
18824         if(target.nodeName.toLowerCase() === 'i'){
18825             target = Roo.get(target).dom.parentNode;
18826         }
18827         
18828         var nodeName = target.nodeName;
18829         var className = target.className;
18830         var html = target.innerHTML;
18831         //Roo.log(nodeName);
18832         
18833         switch(nodeName.toLowerCase()) {
18834             case 'th':
18835                 switch(className) {
18836                     case 'switch':
18837                         this.showMode(1);
18838                         break;
18839                     case 'prev':
18840                     case 'next':
18841                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
18842                         switch(this.viewMode){
18843                                 case 0:
18844                                         this.viewDate = this.moveMonth(this.viewDate, dir);
18845                                         break;
18846                                 case 1:
18847                                 case 2:
18848                                         this.viewDate = this.moveYear(this.viewDate, dir);
18849                                         break;
18850                         }
18851                         this.fill();
18852                         break;
18853                     case 'today':
18854                         var date = new Date();
18855                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
18856 //                        this.fill()
18857                         this.setValue(this.formatDate(this.date));
18858                         
18859                         this.hide();
18860                         break;
18861                 }
18862                 break;
18863             case 'span':
18864                 if (className.indexOf('disabled') < 0) {
18865                     this.viewDate.setUTCDate(1);
18866                     if (className.indexOf('month') > -1) {
18867                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
18868                     } else {
18869                         var year = parseInt(html, 10) || 0;
18870                         this.viewDate.setUTCFullYear(year);
18871                         
18872                     }
18873                     
18874                     if(this.singleMode){
18875                         this.setValue(this.formatDate(this.viewDate));
18876                         this.hide();
18877                         return;
18878                     }
18879                     
18880                     this.showMode(-1);
18881                     this.fill();
18882                 }
18883                 break;
18884                 
18885             case 'td':
18886                 //Roo.log(className);
18887                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
18888                     var day = parseInt(html, 10) || 1;
18889                     var year = this.viewDate.getUTCFullYear(),
18890                         month = this.viewDate.getUTCMonth();
18891
18892                     if (className.indexOf('old') > -1) {
18893                         if(month === 0 ){
18894                             month = 11;
18895                             year -= 1;
18896                         }else{
18897                             month -= 1;
18898                         }
18899                     } else if (className.indexOf('new') > -1) {
18900                         if (month == 11) {
18901                             month = 0;
18902                             year += 1;
18903                         } else {
18904                             month += 1;
18905                         }
18906                     }
18907                     //Roo.log([year,month,day]);
18908                     this.date = this.UTCDate(year, month, day,0,0,0,0);
18909                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
18910 //                    this.fill();
18911                     //Roo.log(this.formatDate(this.date));
18912                     this.setValue(this.formatDate(this.date));
18913                     this.hide();
18914                 }
18915                 break;
18916         }
18917     },
18918     
18919     setStartDate: function(startDate)
18920     {
18921         this.startDate = startDate || -Infinity;
18922         if (this.startDate !== -Infinity) {
18923             this.startDate = this.parseDate(this.startDate);
18924         }
18925         this.update();
18926         this.updateNavArrows();
18927     },
18928
18929     setEndDate: function(endDate)
18930     {
18931         this.endDate = endDate || Infinity;
18932         if (this.endDate !== Infinity) {
18933             this.endDate = this.parseDate(this.endDate);
18934         }
18935         this.update();
18936         this.updateNavArrows();
18937     },
18938     
18939     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
18940     {
18941         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
18942         if (typeof(this.daysOfWeekDisabled) !== 'object') {
18943             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
18944         }
18945         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
18946             return parseInt(d, 10);
18947         });
18948         this.update();
18949         this.updateNavArrows();
18950     },
18951     
18952     updateNavArrows: function() 
18953     {
18954         if(this.singleMode){
18955             return;
18956         }
18957         
18958         var d = new Date(this.viewDate),
18959         year = d.getUTCFullYear(),
18960         month = d.getUTCMonth();
18961         
18962         Roo.each(this.picker().select('.prev', true).elements, function(v){
18963             v.show();
18964             switch (this.viewMode) {
18965                 case 0:
18966
18967                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
18968                         v.hide();
18969                     }
18970                     break;
18971                 case 1:
18972                 case 2:
18973                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
18974                         v.hide();
18975                     }
18976                     break;
18977             }
18978         });
18979         
18980         Roo.each(this.picker().select('.next', true).elements, function(v){
18981             v.show();
18982             switch (this.viewMode) {
18983                 case 0:
18984
18985                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
18986                         v.hide();
18987                     }
18988                     break;
18989                 case 1:
18990                 case 2:
18991                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
18992                         v.hide();
18993                     }
18994                     break;
18995             }
18996         })
18997     },
18998     
18999     moveMonth: function(date, dir)
19000     {
19001         if (!dir) {
19002             return date;
19003         }
19004         var new_date = new Date(date.valueOf()),
19005         day = new_date.getUTCDate(),
19006         month = new_date.getUTCMonth(),
19007         mag = Math.abs(dir),
19008         new_month, test;
19009         dir = dir > 0 ? 1 : -1;
19010         if (mag == 1){
19011             test = dir == -1
19012             // If going back one month, make sure month is not current month
19013             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
19014             ? function(){
19015                 return new_date.getUTCMonth() == month;
19016             }
19017             // If going forward one month, make sure month is as expected
19018             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
19019             : function(){
19020                 return new_date.getUTCMonth() != new_month;
19021             };
19022             new_month = month + dir;
19023             new_date.setUTCMonth(new_month);
19024             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
19025             if (new_month < 0 || new_month > 11) {
19026                 new_month = (new_month + 12) % 12;
19027             }
19028         } else {
19029             // For magnitudes >1, move one month at a time...
19030             for (var i=0; i<mag; i++) {
19031                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
19032                 new_date = this.moveMonth(new_date, dir);
19033             }
19034             // ...then reset the day, keeping it in the new month
19035             new_month = new_date.getUTCMonth();
19036             new_date.setUTCDate(day);
19037             test = function(){
19038                 return new_month != new_date.getUTCMonth();
19039             };
19040         }
19041         // Common date-resetting loop -- if date is beyond end of month, make it
19042         // end of month
19043         while (test()){
19044             new_date.setUTCDate(--day);
19045             new_date.setUTCMonth(new_month);
19046         }
19047         return new_date;
19048     },
19049
19050     moveYear: function(date, dir)
19051     {
19052         return this.moveMonth(date, dir*12);
19053     },
19054
19055     dateWithinRange: function(date)
19056     {
19057         return date >= this.startDate && date <= this.endDate;
19058     },
19059
19060     
19061     remove: function() 
19062     {
19063         this.picker().remove();
19064     },
19065     
19066     validateValue : function(value)
19067     {
19068         if(value.length < 1)  {
19069             if(this.allowBlank){
19070                 return true;
19071             }
19072             return false;
19073         }
19074         
19075         if(value.length < this.minLength){
19076             return false;
19077         }
19078         if(value.length > this.maxLength){
19079             return false;
19080         }
19081         if(this.vtype){
19082             var vt = Roo.form.VTypes;
19083             if(!vt[this.vtype](value, this)){
19084                 return false;
19085             }
19086         }
19087         if(typeof this.validator == "function"){
19088             var msg = this.validator(value);
19089             if(msg !== true){
19090                 return false;
19091             }
19092         }
19093         
19094         if(this.regex && !this.regex.test(value)){
19095             return false;
19096         }
19097         
19098         if(typeof(this.parseDate(value)) == 'undefined'){
19099             return false;
19100         }
19101         
19102         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
19103             return false;
19104         }      
19105         
19106         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
19107             return false;
19108         } 
19109         
19110         
19111         return true;
19112     }
19113    
19114 });
19115
19116 Roo.apply(Roo.bootstrap.DateField,  {
19117     
19118     head : {
19119         tag: 'thead',
19120         cn: [
19121         {
19122             tag: 'tr',
19123             cn: [
19124             {
19125                 tag: 'th',
19126                 cls: 'prev',
19127                 html: '<i class="fa fa-arrow-left"/>'
19128             },
19129             {
19130                 tag: 'th',
19131                 cls: 'switch',
19132                 colspan: '5'
19133             },
19134             {
19135                 tag: 'th',
19136                 cls: 'next',
19137                 html: '<i class="fa fa-arrow-right"/>'
19138             }
19139
19140             ]
19141         }
19142         ]
19143     },
19144     
19145     content : {
19146         tag: 'tbody',
19147         cn: [
19148         {
19149             tag: 'tr',
19150             cn: [
19151             {
19152                 tag: 'td',
19153                 colspan: '7'
19154             }
19155             ]
19156         }
19157         ]
19158     },
19159     
19160     footer : {
19161         tag: 'tfoot',
19162         cn: [
19163         {
19164             tag: 'tr',
19165             cn: [
19166             {
19167                 tag: 'th',
19168                 colspan: '7',
19169                 cls: 'today'
19170             }
19171                     
19172             ]
19173         }
19174         ]
19175     },
19176     
19177     dates:{
19178         en: {
19179             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
19180             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
19181             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
19182             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
19183             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
19184             today: "Today"
19185         }
19186     },
19187     
19188     modes: [
19189     {
19190         clsName: 'days',
19191         navFnc: 'Month',
19192         navStep: 1
19193     },
19194     {
19195         clsName: 'months',
19196         navFnc: 'FullYear',
19197         navStep: 1
19198     },
19199     {
19200         clsName: 'years',
19201         navFnc: 'FullYear',
19202         navStep: 10
19203     }]
19204 });
19205
19206 Roo.apply(Roo.bootstrap.DateField,  {
19207   
19208     template : {
19209         tag: 'div',
19210         cls: 'datepicker dropdown-menu roo-dynamic',
19211         cn: [
19212         {
19213             tag: 'div',
19214             cls: 'datepicker-days',
19215             cn: [
19216             {
19217                 tag: 'table',
19218                 cls: 'table-condensed',
19219                 cn:[
19220                 Roo.bootstrap.DateField.head,
19221                 {
19222                     tag: 'tbody'
19223                 },
19224                 Roo.bootstrap.DateField.footer
19225                 ]
19226             }
19227             ]
19228         },
19229         {
19230             tag: 'div',
19231             cls: 'datepicker-months',
19232             cn: [
19233             {
19234                 tag: 'table',
19235                 cls: 'table-condensed',
19236                 cn:[
19237                 Roo.bootstrap.DateField.head,
19238                 Roo.bootstrap.DateField.content,
19239                 Roo.bootstrap.DateField.footer
19240                 ]
19241             }
19242             ]
19243         },
19244         {
19245             tag: 'div',
19246             cls: 'datepicker-years',
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     }
19261 });
19262
19263  
19264
19265  /*
19266  * - LGPL
19267  *
19268  * TimeField
19269  * 
19270  */
19271
19272 /**
19273  * @class Roo.bootstrap.TimeField
19274  * @extends Roo.bootstrap.Input
19275  * Bootstrap DateField class
19276  * 
19277  * 
19278  * @constructor
19279  * Create a new TimeField
19280  * @param {Object} config The config object
19281  */
19282
19283 Roo.bootstrap.TimeField = function(config){
19284     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
19285     this.addEvents({
19286             /**
19287              * @event show
19288              * Fires when this field show.
19289              * @param {Roo.bootstrap.DateField} thisthis
19290              * @param {Mixed} date The date value
19291              */
19292             show : true,
19293             /**
19294              * @event show
19295              * Fires when this field hide.
19296              * @param {Roo.bootstrap.DateField} this
19297              * @param {Mixed} date The date value
19298              */
19299             hide : true,
19300             /**
19301              * @event select
19302              * Fires when select a date.
19303              * @param {Roo.bootstrap.DateField} this
19304              * @param {Mixed} date The date value
19305              */
19306             select : true
19307         });
19308 };
19309
19310 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
19311     
19312     /**
19313      * @cfg {String} format
19314      * The default time format string which can be overriden for localization support.  The format must be
19315      * valid according to {@link Date#parseDate} (defaults to 'H:i').
19316      */
19317     format : "H:i",
19318        
19319     onRender: function(ct, position)
19320     {
19321         
19322         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
19323                 
19324         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
19325         
19326         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19327         
19328         this.pop = this.picker().select('>.datepicker-time',true).first();
19329         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19330         
19331         this.picker().on('mousedown', this.onMousedown, this);
19332         this.picker().on('click', this.onClick, this);
19333         
19334         this.picker().addClass('datepicker-dropdown');
19335     
19336         this.fillTime();
19337         this.update();
19338             
19339         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
19340         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
19341         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
19342         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
19343         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
19344         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
19345
19346     },
19347     
19348     fireKey: function(e){
19349         if (!this.picker().isVisible()){
19350             if (e.keyCode == 27) { // allow escape to hide and re-show picker
19351                 this.show();
19352             }
19353             return;
19354         }
19355
19356         e.preventDefault();
19357         
19358         switch(e.keyCode){
19359             case 27: // escape
19360                 this.hide();
19361                 break;
19362             case 37: // left
19363             case 39: // right
19364                 this.onTogglePeriod();
19365                 break;
19366             case 38: // up
19367                 this.onIncrementMinutes();
19368                 break;
19369             case 40: // down
19370                 this.onDecrementMinutes();
19371                 break;
19372             case 13: // enter
19373             case 9: // tab
19374                 this.setTime();
19375                 break;
19376         }
19377     },
19378     
19379     onClick: function(e) {
19380         e.stopPropagation();
19381         e.preventDefault();
19382     },
19383     
19384     picker : function()
19385     {
19386         return this.el.select('.datepicker', true).first();
19387     },
19388     
19389     fillTime: function()
19390     {    
19391         var time = this.pop.select('tbody', true).first();
19392         
19393         time.dom.innerHTML = '';
19394         
19395         time.createChild({
19396             tag: 'tr',
19397             cn: [
19398                 {
19399                     tag: 'td',
19400                     cn: [
19401                         {
19402                             tag: 'a',
19403                             href: '#',
19404                             cls: 'btn',
19405                             cn: [
19406                                 {
19407                                     tag: 'span',
19408                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
19409                                 }
19410                             ]
19411                         } 
19412                     ]
19413                 },
19414                 {
19415                     tag: 'td',
19416                     cls: 'separator'
19417                 },
19418                 {
19419                     tag: 'td',
19420                     cn: [
19421                         {
19422                             tag: 'a',
19423                             href: '#',
19424                             cls: 'btn',
19425                             cn: [
19426                                 {
19427                                     tag: 'span',
19428                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
19429                                 }
19430                             ]
19431                         }
19432                     ]
19433                 },
19434                 {
19435                     tag: 'td',
19436                     cls: 'separator'
19437                 }
19438             ]
19439         });
19440         
19441         time.createChild({
19442             tag: 'tr',
19443             cn: [
19444                 {
19445                     tag: 'td',
19446                     cn: [
19447                         {
19448                             tag: 'span',
19449                             cls: 'timepicker-hour',
19450                             html: '00'
19451                         }  
19452                     ]
19453                 },
19454                 {
19455                     tag: 'td',
19456                     cls: 'separator',
19457                     html: ':'
19458                 },
19459                 {
19460                     tag: 'td',
19461                     cn: [
19462                         {
19463                             tag: 'span',
19464                             cls: 'timepicker-minute',
19465                             html: '00'
19466                         }  
19467                     ]
19468                 },
19469                 {
19470                     tag: 'td',
19471                     cls: 'separator'
19472                 },
19473                 {
19474                     tag: 'td',
19475                     cn: [
19476                         {
19477                             tag: 'button',
19478                             type: 'button',
19479                             cls: 'btn btn-primary period',
19480                             html: 'AM'
19481                             
19482                         }
19483                     ]
19484                 }
19485             ]
19486         });
19487         
19488         time.createChild({
19489             tag: 'tr',
19490             cn: [
19491                 {
19492                     tag: 'td',
19493                     cn: [
19494                         {
19495                             tag: 'a',
19496                             href: '#',
19497                             cls: 'btn',
19498                             cn: [
19499                                 {
19500                                     tag: 'span',
19501                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
19502                                 }
19503                             ]
19504                         }
19505                     ]
19506                 },
19507                 {
19508                     tag: 'td',
19509                     cls: 'separator'
19510                 },
19511                 {
19512                     tag: 'td',
19513                     cn: [
19514                         {
19515                             tag: 'a',
19516                             href: '#',
19517                             cls: 'btn',
19518                             cn: [
19519                                 {
19520                                     tag: 'span',
19521                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
19522                                 }
19523                             ]
19524                         }
19525                     ]
19526                 },
19527                 {
19528                     tag: 'td',
19529                     cls: 'separator'
19530                 }
19531             ]
19532         });
19533         
19534     },
19535     
19536     update: function()
19537     {
19538         
19539         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
19540         
19541         this.fill();
19542     },
19543     
19544     fill: function() 
19545     {
19546         var hours = this.time.getHours();
19547         var minutes = this.time.getMinutes();
19548         var period = 'AM';
19549         
19550         if(hours > 11){
19551             period = 'PM';
19552         }
19553         
19554         if(hours == 0){
19555             hours = 12;
19556         }
19557         
19558         
19559         if(hours > 12){
19560             hours = hours - 12;
19561         }
19562         
19563         if(hours < 10){
19564             hours = '0' + hours;
19565         }
19566         
19567         if(minutes < 10){
19568             minutes = '0' + minutes;
19569         }
19570         
19571         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
19572         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
19573         this.pop.select('button', true).first().dom.innerHTML = period;
19574         
19575     },
19576     
19577     place: function()
19578     {   
19579         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
19580         
19581         var cls = ['bottom'];
19582         
19583         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
19584             cls.pop();
19585             cls.push('top');
19586         }
19587         
19588         cls.push('right');
19589         
19590         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
19591             cls.pop();
19592             cls.push('left');
19593         }
19594         
19595         this.picker().addClass(cls.join('-'));
19596         
19597         var _this = this;
19598         
19599         Roo.each(cls, function(c){
19600             if(c == 'bottom'){
19601                 _this.picker().setTop(_this.inputEl().getHeight());
19602                 return;
19603             }
19604             if(c == 'top'){
19605                 _this.picker().setTop(0 - _this.picker().getHeight());
19606                 return;
19607             }
19608             
19609             if(c == 'left'){
19610                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
19611                 return;
19612             }
19613             if(c == 'right'){
19614                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
19615                 return;
19616             }
19617         });
19618         
19619     },
19620   
19621     onFocus : function()
19622     {
19623         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
19624         this.show();
19625     },
19626     
19627     onBlur : function()
19628     {
19629         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
19630         this.hide();
19631     },
19632     
19633     show : function()
19634     {
19635         this.picker().show();
19636         this.pop.show();
19637         this.update();
19638         this.place();
19639         
19640         this.fireEvent('show', this, this.date);
19641     },
19642     
19643     hide : function()
19644     {
19645         this.picker().hide();
19646         this.pop.hide();
19647         
19648         this.fireEvent('hide', this, this.date);
19649     },
19650     
19651     setTime : function()
19652     {
19653         this.hide();
19654         this.setValue(this.time.format(this.format));
19655         
19656         this.fireEvent('select', this, this.date);
19657         
19658         
19659     },
19660     
19661     onMousedown: function(e){
19662         e.stopPropagation();
19663         e.preventDefault();
19664     },
19665     
19666     onIncrementHours: function()
19667     {
19668         Roo.log('onIncrementHours');
19669         this.time = this.time.add(Date.HOUR, 1);
19670         this.update();
19671         
19672     },
19673     
19674     onDecrementHours: function()
19675     {
19676         Roo.log('onDecrementHours');
19677         this.time = this.time.add(Date.HOUR, -1);
19678         this.update();
19679     },
19680     
19681     onIncrementMinutes: function()
19682     {
19683         Roo.log('onIncrementMinutes');
19684         this.time = this.time.add(Date.MINUTE, 1);
19685         this.update();
19686     },
19687     
19688     onDecrementMinutes: function()
19689     {
19690         Roo.log('onDecrementMinutes');
19691         this.time = this.time.add(Date.MINUTE, -1);
19692         this.update();
19693     },
19694     
19695     onTogglePeriod: function()
19696     {
19697         Roo.log('onTogglePeriod');
19698         this.time = this.time.add(Date.HOUR, 12);
19699         this.update();
19700     }
19701     
19702    
19703 });
19704
19705 Roo.apply(Roo.bootstrap.TimeField,  {
19706     
19707     content : {
19708         tag: 'tbody',
19709         cn: [
19710             {
19711                 tag: 'tr',
19712                 cn: [
19713                 {
19714                     tag: 'td',
19715                     colspan: '7'
19716                 }
19717                 ]
19718             }
19719         ]
19720     },
19721     
19722     footer : {
19723         tag: 'tfoot',
19724         cn: [
19725             {
19726                 tag: 'tr',
19727                 cn: [
19728                 {
19729                     tag: 'th',
19730                     colspan: '7',
19731                     cls: '',
19732                     cn: [
19733                         {
19734                             tag: 'button',
19735                             cls: 'btn btn-info ok',
19736                             html: 'OK'
19737                         }
19738                     ]
19739                 }
19740
19741                 ]
19742             }
19743         ]
19744     }
19745 });
19746
19747 Roo.apply(Roo.bootstrap.TimeField,  {
19748   
19749     template : {
19750         tag: 'div',
19751         cls: 'datepicker dropdown-menu',
19752         cn: [
19753             {
19754                 tag: 'div',
19755                 cls: 'datepicker-time',
19756                 cn: [
19757                 {
19758                     tag: 'table',
19759                     cls: 'table-condensed',
19760                     cn:[
19761                     Roo.bootstrap.TimeField.content,
19762                     Roo.bootstrap.TimeField.footer
19763                     ]
19764                 }
19765                 ]
19766             }
19767         ]
19768     }
19769 });
19770
19771  
19772
19773  /*
19774  * - LGPL
19775  *
19776  * MonthField
19777  * 
19778  */
19779
19780 /**
19781  * @class Roo.bootstrap.MonthField
19782  * @extends Roo.bootstrap.Input
19783  * Bootstrap MonthField class
19784  * 
19785  * @cfg {String} language default en
19786  * 
19787  * @constructor
19788  * Create a new MonthField
19789  * @param {Object} config The config object
19790  */
19791
19792 Roo.bootstrap.MonthField = function(config){
19793     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
19794     
19795     this.addEvents({
19796         /**
19797          * @event show
19798          * Fires when this field show.
19799          * @param {Roo.bootstrap.MonthField} this
19800          * @param {Mixed} date The date value
19801          */
19802         show : true,
19803         /**
19804          * @event show
19805          * Fires when this field hide.
19806          * @param {Roo.bootstrap.MonthField} this
19807          * @param {Mixed} date The date value
19808          */
19809         hide : true,
19810         /**
19811          * @event select
19812          * Fires when select a date.
19813          * @param {Roo.bootstrap.MonthField} this
19814          * @param {String} oldvalue The old value
19815          * @param {String} newvalue The new value
19816          */
19817         select : true
19818     });
19819 };
19820
19821 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
19822     
19823     onRender: function(ct, position)
19824     {
19825         
19826         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
19827         
19828         this.language = this.language || 'en';
19829         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
19830         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
19831         
19832         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
19833         this.isInline = false;
19834         this.isInput = true;
19835         this.component = this.el.select('.add-on', true).first() || false;
19836         this.component = (this.component && this.component.length === 0) ? false : this.component;
19837         this.hasInput = this.component && this.inputEL().length;
19838         
19839         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
19840         
19841         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
19842         
19843         this.picker().on('mousedown', this.onMousedown, this);
19844         this.picker().on('click', this.onClick, this);
19845         
19846         this.picker().addClass('datepicker-dropdown');
19847         
19848         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
19849             v.setStyle('width', '189px');
19850         });
19851         
19852         this.fillMonths();
19853         
19854         this.update();
19855         
19856         if(this.isInline) {
19857             this.show();
19858         }
19859         
19860     },
19861     
19862     setValue: function(v, suppressEvent)
19863     {   
19864         var o = this.getValue();
19865         
19866         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
19867         
19868         this.update();
19869
19870         if(suppressEvent !== true){
19871             this.fireEvent('select', this, o, v);
19872         }
19873         
19874     },
19875     
19876     getValue: function()
19877     {
19878         return this.value;
19879     },
19880     
19881     onClick: function(e) 
19882     {
19883         e.stopPropagation();
19884         e.preventDefault();
19885         
19886         var target = e.getTarget();
19887         
19888         if(target.nodeName.toLowerCase() === 'i'){
19889             target = Roo.get(target).dom.parentNode;
19890         }
19891         
19892         var nodeName = target.nodeName;
19893         var className = target.className;
19894         var html = target.innerHTML;
19895         
19896         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
19897             return;
19898         }
19899         
19900         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
19901         
19902         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
19903         
19904         this.hide();
19905                         
19906     },
19907     
19908     picker : function()
19909     {
19910         return this.pickerEl;
19911     },
19912     
19913     fillMonths: function()
19914     {    
19915         var i = 0;
19916         var months = this.picker().select('>.datepicker-months td', true).first();
19917         
19918         months.dom.innerHTML = '';
19919         
19920         while (i < 12) {
19921             var month = {
19922                 tag: 'span',
19923                 cls: 'month',
19924                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
19925             };
19926             
19927             months.createChild(month);
19928         }
19929         
19930     },
19931     
19932     update: function()
19933     {
19934         var _this = this;
19935         
19936         if(typeof(this.vIndex) == 'undefined' && this.value.length){
19937             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
19938         }
19939         
19940         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
19941             e.removeClass('active');
19942             
19943             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
19944                 e.addClass('active');
19945             }
19946         })
19947     },
19948     
19949     place: function()
19950     {
19951         if(this.isInline) {
19952             return;
19953         }
19954         
19955         this.picker().removeClass(['bottom', 'top']);
19956         
19957         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
19958             /*
19959              * place to the top of element!
19960              *
19961              */
19962             
19963             this.picker().addClass('top');
19964             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
19965             
19966             return;
19967         }
19968         
19969         this.picker().addClass('bottom');
19970         
19971         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
19972     },
19973     
19974     onFocus : function()
19975     {
19976         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
19977         this.show();
19978     },
19979     
19980     onBlur : function()
19981     {
19982         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
19983         
19984         var d = this.inputEl().getValue();
19985         
19986         this.setValue(d);
19987                 
19988         this.hide();
19989     },
19990     
19991     show : function()
19992     {
19993         this.picker().show();
19994         this.picker().select('>.datepicker-months', true).first().show();
19995         this.update();
19996         this.place();
19997         
19998         this.fireEvent('show', this, this.date);
19999     },
20000     
20001     hide : function()
20002     {
20003         if(this.isInline) {
20004             return;
20005         }
20006         this.picker().hide();
20007         this.fireEvent('hide', this, this.date);
20008         
20009     },
20010     
20011     onMousedown: function(e)
20012     {
20013         e.stopPropagation();
20014         e.preventDefault();
20015     },
20016     
20017     keyup: function(e)
20018     {
20019         Roo.bootstrap.MonthField.superclass.keyup.call(this);
20020         this.update();
20021     },
20022
20023     fireKey: function(e)
20024     {
20025         if (!this.picker().isVisible()){
20026             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
20027                 this.show();
20028             }
20029             return;
20030         }
20031         
20032         var dir;
20033         
20034         switch(e.keyCode){
20035             case 27: // escape
20036                 this.hide();
20037                 e.preventDefault();
20038                 break;
20039             case 37: // left
20040             case 39: // right
20041                 dir = e.keyCode == 37 ? -1 : 1;
20042                 
20043                 this.vIndex = this.vIndex + dir;
20044                 
20045                 if(this.vIndex < 0){
20046                     this.vIndex = 0;
20047                 }
20048                 
20049                 if(this.vIndex > 11){
20050                     this.vIndex = 11;
20051                 }
20052                 
20053                 if(isNaN(this.vIndex)){
20054                     this.vIndex = 0;
20055                 }
20056                 
20057                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20058                 
20059                 break;
20060             case 38: // up
20061             case 40: // down
20062                 
20063                 dir = e.keyCode == 38 ? -1 : 1;
20064                 
20065                 this.vIndex = this.vIndex + dir * 4;
20066                 
20067                 if(this.vIndex < 0){
20068                     this.vIndex = 0;
20069                 }
20070                 
20071                 if(this.vIndex > 11){
20072                     this.vIndex = 11;
20073                 }
20074                 
20075                 if(isNaN(this.vIndex)){
20076                     this.vIndex = 0;
20077                 }
20078                 
20079                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20080                 break;
20081                 
20082             case 13: // enter
20083                 
20084                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20085                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20086                 }
20087                 
20088                 this.hide();
20089                 e.preventDefault();
20090                 break;
20091             case 9: // tab
20092                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
20093                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
20094                 }
20095                 this.hide();
20096                 break;
20097             case 16: // shift
20098             case 17: // ctrl
20099             case 18: // alt
20100                 break;
20101             default :
20102                 this.hide();
20103                 
20104         }
20105     },
20106     
20107     remove: function() 
20108     {
20109         this.picker().remove();
20110     }
20111    
20112 });
20113
20114 Roo.apply(Roo.bootstrap.MonthField,  {
20115     
20116     content : {
20117         tag: 'tbody',
20118         cn: [
20119         {
20120             tag: 'tr',
20121             cn: [
20122             {
20123                 tag: 'td',
20124                 colspan: '7'
20125             }
20126             ]
20127         }
20128         ]
20129     },
20130     
20131     dates:{
20132         en: {
20133             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
20134             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
20135         }
20136     }
20137 });
20138
20139 Roo.apply(Roo.bootstrap.MonthField,  {
20140   
20141     template : {
20142         tag: 'div',
20143         cls: 'datepicker dropdown-menu roo-dynamic',
20144         cn: [
20145             {
20146                 tag: 'div',
20147                 cls: 'datepicker-months',
20148                 cn: [
20149                 {
20150                     tag: 'table',
20151                     cls: 'table-condensed',
20152                     cn:[
20153                         Roo.bootstrap.DateField.content
20154                     ]
20155                 }
20156                 ]
20157             }
20158         ]
20159     }
20160 });
20161
20162  
20163
20164  
20165  /*
20166  * - LGPL
20167  *
20168  * CheckBox
20169  * 
20170  */
20171
20172 /**
20173  * @class Roo.bootstrap.CheckBox
20174  * @extends Roo.bootstrap.Input
20175  * Bootstrap CheckBox class
20176  * 
20177  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
20178  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
20179  * @cfg {String} boxLabel The text that appears beside the checkbox
20180  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
20181  * @cfg {Boolean} checked initnal the element
20182  * @cfg {Boolean} inline inline the element (default false)
20183  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
20184  * @cfg {String} tooltip label tooltip
20185  * 
20186  * @constructor
20187  * Create a new CheckBox
20188  * @param {Object} config The config object
20189  */
20190
20191 Roo.bootstrap.CheckBox = function(config){
20192     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
20193    
20194     this.addEvents({
20195         /**
20196         * @event check
20197         * Fires when the element is checked or unchecked.
20198         * @param {Roo.bootstrap.CheckBox} this This input
20199         * @param {Boolean} checked The new checked value
20200         */
20201        check : true,
20202        /**
20203         * @event click
20204         * Fires when the element is click.
20205         * @param {Roo.bootstrap.CheckBox} this This input
20206         */
20207        click : true
20208     });
20209     
20210 };
20211
20212 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
20213   
20214     inputType: 'checkbox',
20215     inputValue: 1,
20216     valueOff: 0,
20217     boxLabel: false,
20218     checked: false,
20219     weight : false,
20220     inline: false,
20221     tooltip : '',
20222     
20223     getAutoCreate : function()
20224     {
20225         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
20226         
20227         var id = Roo.id();
20228         
20229         var cfg = {};
20230         
20231         cfg.cls = 'form-group ' + this.inputType; //input-group
20232         
20233         if(this.inline){
20234             cfg.cls += ' ' + this.inputType + '-inline';
20235         }
20236         
20237         var input =  {
20238             tag: 'input',
20239             id : id,
20240             type : this.inputType,
20241             value : this.inputValue,
20242             cls : 'roo-' + this.inputType, //'form-box',
20243             placeholder : this.placeholder || ''
20244             
20245         };
20246         
20247         if(this.inputType != 'radio'){
20248             var hidden =  {
20249                 tag: 'input',
20250                 type : 'hidden',
20251                 cls : 'roo-hidden-value',
20252                 value : this.checked ? this.inputValue : this.valueOff
20253             };
20254         }
20255         
20256             
20257         if (this.weight) { // Validity check?
20258             cfg.cls += " " + this.inputType + "-" + this.weight;
20259         }
20260         
20261         if (this.disabled) {
20262             input.disabled=true;
20263         }
20264         
20265         if(this.checked){
20266             input.checked = this.checked;
20267         }
20268         
20269         if (this.name) {
20270             
20271             input.name = this.name;
20272             
20273             if(this.inputType != 'radio'){
20274                 hidden.name = this.name;
20275                 input.name = '_hidden_' + this.name;
20276             }
20277         }
20278         
20279         if (this.size) {
20280             input.cls += ' input-' + this.size;
20281         }
20282         
20283         var settings=this;
20284         
20285         ['xs','sm','md','lg'].map(function(size){
20286             if (settings[size]) {
20287                 cfg.cls += ' col-' + size + '-' + settings[size];
20288             }
20289         });
20290         
20291         var inputblock = input;
20292          
20293         if (this.before || this.after) {
20294             
20295             inputblock = {
20296                 cls : 'input-group',
20297                 cn :  [] 
20298             };
20299             
20300             if (this.before) {
20301                 inputblock.cn.push({
20302                     tag :'span',
20303                     cls : 'input-group-addon',
20304                     html : this.before
20305                 });
20306             }
20307             
20308             inputblock.cn.push(input);
20309             
20310             if(this.inputType != 'radio'){
20311                 inputblock.cn.push(hidden);
20312             }
20313             
20314             if (this.after) {
20315                 inputblock.cn.push({
20316                     tag :'span',
20317                     cls : 'input-group-addon',
20318                     html : this.after
20319                 });
20320             }
20321             
20322         }
20323         
20324         if (align ==='left' && this.fieldLabel.length) {
20325 //                Roo.log("left and has label");
20326             cfg.cn = [
20327                 {
20328                     tag: 'label',
20329                     'for' :  id,
20330                     cls : 'control-label',
20331                     html : this.fieldLabel
20332                 },
20333                 {
20334                     cls : "", 
20335                     cn: [
20336                         inputblock
20337                     ]
20338                 }
20339             ];
20340             
20341             if(this.labelWidth > 12){
20342                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
20343             }
20344             
20345             if(this.labelWidth < 13 && this.labelmd == 0){
20346                 this.labelmd = this.labelWidth;
20347             }
20348             
20349             if(this.labellg > 0){
20350                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
20351                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
20352             }
20353             
20354             if(this.labelmd > 0){
20355                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
20356                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
20357             }
20358             
20359             if(this.labelsm > 0){
20360                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
20361                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
20362             }
20363             
20364             if(this.labelxs > 0){
20365                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
20366                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
20367             }
20368             
20369         } else if ( this.fieldLabel.length) {
20370 //                Roo.log(" label");
20371                 cfg.cn = [
20372                    
20373                     {
20374                         tag: this.boxLabel ? 'span' : 'label',
20375                         'for': id,
20376                         cls: 'control-label box-input-label',
20377                         //cls : 'input-group-addon',
20378                         html : this.fieldLabel
20379                     },
20380                     
20381                     inputblock
20382                     
20383                 ];
20384
20385         } else {
20386             
20387 //                Roo.log(" no label && no align");
20388                 cfg.cn = [  inputblock ] ;
20389                 
20390                 
20391         }
20392         
20393         if(this.boxLabel){
20394              var boxLabelCfg = {
20395                 tag: 'label',
20396                 //'for': id, // box label is handled by onclick - so no for...
20397                 cls: 'box-label',
20398                 html: this.boxLabel
20399             };
20400             
20401             if(this.tooltip){
20402                 boxLabelCfg.tooltip = this.tooltip;
20403             }
20404              
20405             cfg.cn.push(boxLabelCfg);
20406         }
20407         
20408         if(this.inputType != 'radio'){
20409             cfg.cn.push(hidden);
20410         }
20411         
20412         return cfg;
20413         
20414     },
20415     
20416     /**
20417      * return the real input element.
20418      */
20419     inputEl: function ()
20420     {
20421         return this.el.select('input.roo-' + this.inputType,true).first();
20422     },
20423     hiddenEl: function ()
20424     {
20425         return this.el.select('input.roo-hidden-value',true).first();
20426     },
20427     
20428     labelEl: function()
20429     {
20430         return this.el.select('label.control-label',true).first();
20431     },
20432     /* depricated... */
20433     
20434     label: function()
20435     {
20436         return this.labelEl();
20437     },
20438     
20439     boxLabelEl: function()
20440     {
20441         return this.el.select('label.box-label',true).first();
20442     },
20443     
20444     initEvents : function()
20445     {
20446 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
20447         
20448         this.inputEl().on('click', this.onClick,  this);
20449         
20450         if (this.boxLabel) { 
20451             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
20452         }
20453         
20454         this.startValue = this.getValue();
20455         
20456         if(this.groupId){
20457             Roo.bootstrap.CheckBox.register(this);
20458         }
20459     },
20460     
20461     onClick : function(e)
20462     {   
20463         if(this.fireEvent('click', this, e) !== false){
20464             this.setChecked(!this.checked);
20465         }
20466         
20467     },
20468     
20469     setChecked : function(state,suppressEvent)
20470     {
20471         this.startValue = this.getValue();
20472
20473         if(this.inputType == 'radio'){
20474             
20475             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20476                 e.dom.checked = false;
20477             });
20478             
20479             this.inputEl().dom.checked = true;
20480             
20481             this.inputEl().dom.value = this.inputValue;
20482             
20483             if(suppressEvent !== true){
20484                 this.fireEvent('check', this, true);
20485             }
20486             
20487             this.validate();
20488             
20489             return;
20490         }
20491         
20492         this.checked = state;
20493         
20494         this.inputEl().dom.checked = state;
20495         
20496         
20497         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
20498         
20499         if(suppressEvent !== true){
20500             this.fireEvent('check', this, state);
20501         }
20502         
20503         this.validate();
20504     },
20505     
20506     getValue : function()
20507     {
20508         if(this.inputType == 'radio'){
20509             return this.getGroupValue();
20510         }
20511         
20512         return this.hiddenEl().dom.value;
20513         
20514     },
20515     
20516     getGroupValue : function()
20517     {
20518         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
20519             return '';
20520         }
20521         
20522         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
20523     },
20524     
20525     setValue : function(v,suppressEvent)
20526     {
20527         if(this.inputType == 'radio'){
20528             this.setGroupValue(v, suppressEvent);
20529             return;
20530         }
20531         
20532         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
20533         
20534         this.validate();
20535     },
20536     
20537     setGroupValue : function(v, suppressEvent)
20538     {
20539         this.startValue = this.getValue();
20540         
20541         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20542             e.dom.checked = false;
20543             
20544             if(e.dom.value == v){
20545                 e.dom.checked = true;
20546             }
20547         });
20548         
20549         if(suppressEvent !== true){
20550             this.fireEvent('check', this, true);
20551         }
20552
20553         this.validate();
20554         
20555         return;
20556     },
20557     
20558     validate : function()
20559     {
20560         if(
20561                 this.disabled || 
20562                 (this.inputType == 'radio' && this.validateRadio()) ||
20563                 (this.inputType == 'checkbox' && this.validateCheckbox())
20564         ){
20565             this.markValid();
20566             return true;
20567         }
20568         
20569         this.markInvalid();
20570         return false;
20571     },
20572     
20573     validateRadio : function()
20574     {
20575         if(this.allowBlank){
20576             return true;
20577         }
20578         
20579         var valid = false;
20580         
20581         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20582             if(!e.dom.checked){
20583                 return;
20584             }
20585             
20586             valid = true;
20587             
20588             return false;
20589         });
20590         
20591         return valid;
20592     },
20593     
20594     validateCheckbox : function()
20595     {
20596         if(!this.groupId){
20597             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
20598             //return (this.getValue() == this.inputValue) ? true : false;
20599         }
20600         
20601         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20602         
20603         if(!group){
20604             return false;
20605         }
20606         
20607         var r = false;
20608         
20609         for(var i in group){
20610             if(group[i].el.isVisible(true)){
20611                 r = false;
20612                 break;
20613             }
20614             
20615             r = true;
20616         }
20617         
20618         for(var i in group){
20619             if(r){
20620                 break;
20621             }
20622             
20623             r = (group[i].getValue() == group[i].inputValue) ? true : false;
20624         }
20625         
20626         return r;
20627     },
20628     
20629     /**
20630      * Mark this field as valid
20631      */
20632     markValid : function()
20633     {
20634         var _this = this;
20635         
20636         this.fireEvent('valid', this);
20637         
20638         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20639         
20640         if(this.groupId){
20641             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20642         }
20643         
20644         if(label){
20645             label.markValid();
20646         }
20647
20648         if(this.inputType == 'radio'){
20649             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20650                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20651                 e.findParent('.form-group', false, true).addClass(_this.validClass);
20652             });
20653             
20654             return;
20655         }
20656
20657         if(!this.groupId){
20658             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20659             this.el.findParent('.form-group', false, true).addClass(this.validClass);
20660             return;
20661         }
20662         
20663         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20664         
20665         if(!group){
20666             return;
20667         }
20668         
20669         for(var i in group){
20670             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20671             group[i].el.findParent('.form-group', false, true).addClass(this.validClass);
20672         }
20673     },
20674     
20675      /**
20676      * Mark this field as invalid
20677      * @param {String} msg The validation message
20678      */
20679     markInvalid : function(msg)
20680     {
20681         if(this.allowBlank){
20682             return;
20683         }
20684         
20685         var _this = this;
20686         
20687         this.fireEvent('invalid', this, msg);
20688         
20689         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20690         
20691         if(this.groupId){
20692             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
20693         }
20694         
20695         if(label){
20696             label.markInvalid();
20697         }
20698             
20699         if(this.inputType == 'radio'){
20700             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20701                 e.findParent('.form-group', false, true).removeClass([_this.invalidClass, _this.validClass]);
20702                 e.findParent('.form-group', false, true).addClass(_this.invalidClass);
20703             });
20704             
20705             return;
20706         }
20707         
20708         if(!this.groupId){
20709             this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20710             this.el.findParent('.form-group', false, true).addClass(this.invalidClass);
20711             return;
20712         }
20713         
20714         var group = Roo.bootstrap.CheckBox.get(this.groupId);
20715         
20716         if(!group){
20717             return;
20718         }
20719         
20720         for(var i in group){
20721             group[i].el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20722             group[i].el.findParent('.form-group', false, true).addClass(this.invalidClass);
20723         }
20724         
20725     },
20726     
20727     clearInvalid : function()
20728     {
20729         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
20730         
20731         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
20732         
20733         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
20734         
20735         if (label && label.iconEl) {
20736             label.iconEl.removeClass(label.validClass);
20737             label.iconEl.removeClass(label.invalidClass);
20738         }
20739     },
20740     
20741     disable : function()
20742     {
20743         if(this.inputType != 'radio'){
20744             Roo.bootstrap.CheckBox.superclass.disable.call(this);
20745             return;
20746         }
20747         
20748         var _this = this;
20749         
20750         if(this.rendered){
20751             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20752                 _this.getActionEl().addClass(this.disabledClass);
20753                 e.dom.disabled = true;
20754             });
20755         }
20756         
20757         this.disabled = true;
20758         this.fireEvent("disable", this);
20759         return this;
20760     },
20761
20762     enable : function()
20763     {
20764         if(this.inputType != 'radio'){
20765             Roo.bootstrap.CheckBox.superclass.enable.call(this);
20766             return;
20767         }
20768         
20769         var _this = this;
20770         
20771         if(this.rendered){
20772             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
20773                 _this.getActionEl().removeClass(this.disabledClass);
20774                 e.dom.disabled = false;
20775             });
20776         }
20777         
20778         this.disabled = false;
20779         this.fireEvent("enable", this);
20780         return this;
20781     },
20782     
20783     setBoxLabel : function(v)
20784     {
20785         this.boxLabel = v;
20786         
20787         if(this.rendered){
20788             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
20789         }
20790     }
20791
20792 });
20793
20794 Roo.apply(Roo.bootstrap.CheckBox, {
20795     
20796     groups: {},
20797     
20798      /**
20799     * register a CheckBox Group
20800     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
20801     */
20802     register : function(checkbox)
20803     {
20804         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
20805             this.groups[checkbox.groupId] = {};
20806         }
20807         
20808         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
20809             return;
20810         }
20811         
20812         this.groups[checkbox.groupId][checkbox.name] = checkbox;
20813         
20814     },
20815     /**
20816     * fetch a CheckBox Group based on the group ID
20817     * @param {string} the group ID
20818     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
20819     */
20820     get: function(groupId) {
20821         if (typeof(this.groups[groupId]) == 'undefined') {
20822             return false;
20823         }
20824         
20825         return this.groups[groupId] ;
20826     }
20827     
20828     
20829 });
20830 /*
20831  * - LGPL
20832  *
20833  * RadioItem
20834  * 
20835  */
20836
20837 /**
20838  * @class Roo.bootstrap.Radio
20839  * @extends Roo.bootstrap.Component
20840  * Bootstrap Radio class
20841  * @cfg {String} boxLabel - the label associated
20842  * @cfg {String} value - the value of radio
20843  * 
20844  * @constructor
20845  * Create a new Radio
20846  * @param {Object} config The config object
20847  */
20848 Roo.bootstrap.Radio = function(config){
20849     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
20850     
20851 };
20852
20853 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
20854     
20855     boxLabel : '',
20856     
20857     value : '',
20858     
20859     getAutoCreate : function()
20860     {
20861         var cfg = {
20862             tag : 'div',
20863             cls : 'form-group radio',
20864             cn : [
20865                 {
20866                     tag : 'label',
20867                     cls : 'box-label',
20868                     html : this.boxLabel
20869                 }
20870             ]
20871         };
20872         
20873         return cfg;
20874     },
20875     
20876     initEvents : function() 
20877     {
20878         this.parent().register(this);
20879         
20880         this.el.on('click', this.onClick, this);
20881         
20882     },
20883     
20884     onClick : function()
20885     {
20886         this.setChecked(true);
20887     },
20888     
20889     setChecked : function(state, suppressEvent)
20890     {
20891         this.parent().setValue(this.value, suppressEvent);
20892         
20893     },
20894     
20895     setBoxLabel : function(v)
20896     {
20897         this.boxLabel = v;
20898         
20899         if(this.rendered){
20900             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
20901         }
20902     }
20903     
20904 });
20905  
20906
20907  /*
20908  * - LGPL
20909  *
20910  * Input
20911  * 
20912  */
20913
20914 /**
20915  * @class Roo.bootstrap.SecurePass
20916  * @extends Roo.bootstrap.Input
20917  * Bootstrap SecurePass class
20918  *
20919  * 
20920  * @constructor
20921  * Create a new SecurePass
20922  * @param {Object} config The config object
20923  */
20924  
20925 Roo.bootstrap.SecurePass = function (config) {
20926     // these go here, so the translation tool can replace them..
20927     this.errors = {
20928         PwdEmpty: "Please type a password, and then retype it to confirm.",
20929         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20930         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20931         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20932         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20933         FNInPwd: "Your password can't contain your first name. Please type a different password.",
20934         LNInPwd: "Your password can't contain your last name. Please type a different password.",
20935         TooWeak: "Your password is Too Weak."
20936     },
20937     this.meterLabel = "Password strength:";
20938     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
20939     this.meterClass = [
20940         "roo-password-meter-tooweak", 
20941         "roo-password-meter-weak", 
20942         "roo-password-meter-medium", 
20943         "roo-password-meter-strong", 
20944         "roo-password-meter-grey"
20945     ];
20946     
20947     this.errors = {};
20948     
20949     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
20950 }
20951
20952 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
20953     /**
20954      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
20955      * {
20956      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
20957      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
20958      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
20959      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
20960      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
20961      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
20962      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
20963      * })
20964      */
20965     // private
20966     
20967     meterWidth: 300,
20968     errorMsg :'',    
20969     errors: false,
20970     imageRoot: '/',
20971     /**
20972      * @cfg {String/Object} Label for the strength meter (defaults to
20973      * 'Password strength:')
20974      */
20975     // private
20976     meterLabel: '',
20977     /**
20978      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
20979      * ['Weak', 'Medium', 'Strong'])
20980      */
20981     // private    
20982     pwdStrengths: false,    
20983     // private
20984     strength: 0,
20985     // private
20986     _lastPwd: null,
20987     // private
20988     kCapitalLetter: 0,
20989     kSmallLetter: 1,
20990     kDigit: 2,
20991     kPunctuation: 3,
20992     
20993     insecure: false,
20994     // private
20995     initEvents: function ()
20996     {
20997         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
20998
20999         if (this.el.is('input[type=password]') && Roo.isSafari) {
21000             this.el.on('keydown', this.SafariOnKeyDown, this);
21001         }
21002
21003         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
21004     },
21005     // private
21006     onRender: function (ct, position)
21007     {
21008         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
21009         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
21010         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
21011
21012         this.trigger.createChild({
21013                    cn: [
21014                     {
21015                     //id: 'PwdMeter',
21016                     tag: 'div',
21017                     cls: 'roo-password-meter-grey col-xs-12',
21018                     style: {
21019                         //width: 0,
21020                         //width: this.meterWidth + 'px'                                                
21021                         }
21022                     },
21023                     {                            
21024                          cls: 'roo-password-meter-text'                          
21025                     }
21026                 ]            
21027         });
21028
21029          
21030         if (this.hideTrigger) {
21031             this.trigger.setDisplayed(false);
21032         }
21033         this.setSize(this.width || '', this.height || '');
21034     },
21035     // private
21036     onDestroy: function ()
21037     {
21038         if (this.trigger) {
21039             this.trigger.removeAllListeners();
21040             this.trigger.remove();
21041         }
21042         if (this.wrap) {
21043             this.wrap.remove();
21044         }
21045         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
21046     },
21047     // private
21048     checkStrength: function ()
21049     {
21050         var pwd = this.inputEl().getValue();
21051         if (pwd == this._lastPwd) {
21052             return;
21053         }
21054
21055         var strength;
21056         if (this.ClientSideStrongPassword(pwd)) {
21057             strength = 3;
21058         } else if (this.ClientSideMediumPassword(pwd)) {
21059             strength = 2;
21060         } else if (this.ClientSideWeakPassword(pwd)) {
21061             strength = 1;
21062         } else {
21063             strength = 0;
21064         }
21065         
21066         Roo.log('strength1: ' + strength);
21067         
21068         //var pm = this.trigger.child('div/div/div').dom;
21069         var pm = this.trigger.child('div/div');
21070         pm.removeClass(this.meterClass);
21071         pm.addClass(this.meterClass[strength]);
21072                 
21073         
21074         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21075                 
21076         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21077         
21078         this._lastPwd = pwd;
21079     },
21080     reset: function ()
21081     {
21082         Roo.bootstrap.SecurePass.superclass.reset.call(this);
21083         
21084         this._lastPwd = '';
21085         
21086         var pm = this.trigger.child('div/div');
21087         pm.removeClass(this.meterClass);
21088         pm.addClass('roo-password-meter-grey');        
21089         
21090         
21091         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21092         
21093         pt.innerHTML = '';
21094         this.inputEl().dom.type='password';
21095     },
21096     // private
21097     validateValue: function (value)
21098     {
21099         
21100         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
21101             return false;
21102         }
21103         if (value.length == 0) {
21104             if (this.allowBlank) {
21105                 this.clearInvalid();
21106                 return true;
21107             }
21108
21109             this.markInvalid(this.errors.PwdEmpty);
21110             this.errorMsg = this.errors.PwdEmpty;
21111             return false;
21112         }
21113         
21114         if(this.insecure){
21115             return true;
21116         }
21117         
21118         if ('[\x21-\x7e]*'.match(value)) {
21119             this.markInvalid(this.errors.PwdBadChar);
21120             this.errorMsg = this.errors.PwdBadChar;
21121             return false;
21122         }
21123         if (value.length < 6) {
21124             this.markInvalid(this.errors.PwdShort);
21125             this.errorMsg = this.errors.PwdShort;
21126             return false;
21127         }
21128         if (value.length > 16) {
21129             this.markInvalid(this.errors.PwdLong);
21130             this.errorMsg = this.errors.PwdLong;
21131             return false;
21132         }
21133         var strength;
21134         if (this.ClientSideStrongPassword(value)) {
21135             strength = 3;
21136         } else if (this.ClientSideMediumPassword(value)) {
21137             strength = 2;
21138         } else if (this.ClientSideWeakPassword(value)) {
21139             strength = 1;
21140         } else {
21141             strength = 0;
21142         }
21143
21144         
21145         if (strength < 2) {
21146             //this.markInvalid(this.errors.TooWeak);
21147             this.errorMsg = this.errors.TooWeak;
21148             //return false;
21149         }
21150         
21151         
21152         console.log('strength2: ' + strength);
21153         
21154         //var pm = this.trigger.child('div/div/div').dom;
21155         
21156         var pm = this.trigger.child('div/div');
21157         pm.removeClass(this.meterClass);
21158         pm.addClass(this.meterClass[strength]);
21159                 
21160         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
21161                 
21162         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
21163         
21164         this.errorMsg = ''; 
21165         return true;
21166     },
21167     // private
21168     CharacterSetChecks: function (type)
21169     {
21170         this.type = type;
21171         this.fResult = false;
21172     },
21173     // private
21174     isctype: function (character, type)
21175     {
21176         switch (type) {  
21177             case this.kCapitalLetter:
21178                 if (character >= 'A' && character <= 'Z') {
21179                     return true;
21180                 }
21181                 break;
21182             
21183             case this.kSmallLetter:
21184                 if (character >= 'a' && character <= 'z') {
21185                     return true;
21186                 }
21187                 break;
21188             
21189             case this.kDigit:
21190                 if (character >= '0' && character <= '9') {
21191                     return true;
21192                 }
21193                 break;
21194             
21195             case this.kPunctuation:
21196                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
21197                     return true;
21198                 }
21199                 break;
21200             
21201             default:
21202                 return false;
21203         }
21204
21205     },
21206     // private
21207     IsLongEnough: function (pwd, size)
21208     {
21209         return !(pwd == null || isNaN(size) || pwd.length < size);
21210     },
21211     // private
21212     SpansEnoughCharacterSets: function (word, nb)
21213     {
21214         if (!this.IsLongEnough(word, nb))
21215         {
21216             return false;
21217         }
21218
21219         var characterSetChecks = new Array(
21220             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
21221             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
21222         );
21223         
21224         for (var index = 0; index < word.length; ++index) {
21225             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21226                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
21227                     characterSetChecks[nCharSet].fResult = true;
21228                     break;
21229                 }
21230             }
21231         }
21232
21233         var nCharSets = 0;
21234         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
21235             if (characterSetChecks[nCharSet].fResult) {
21236                 ++nCharSets;
21237             }
21238         }
21239
21240         if (nCharSets < nb) {
21241             return false;
21242         }
21243         return true;
21244     },
21245     // private
21246     ClientSideStrongPassword: function (pwd)
21247     {
21248         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
21249     },
21250     // private
21251     ClientSideMediumPassword: function (pwd)
21252     {
21253         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
21254     },
21255     // private
21256     ClientSideWeakPassword: function (pwd)
21257     {
21258         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
21259     }
21260           
21261 })//<script type="text/javascript">
21262
21263 /*
21264  * Based  Ext JS Library 1.1.1
21265  * Copyright(c) 2006-2007, Ext JS, LLC.
21266  * LGPL
21267  *
21268  */
21269  
21270 /**
21271  * @class Roo.HtmlEditorCore
21272  * @extends Roo.Component
21273  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
21274  *
21275  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
21276  */
21277
21278 Roo.HtmlEditorCore = function(config){
21279     
21280     
21281     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
21282     
21283     
21284     this.addEvents({
21285         /**
21286          * @event initialize
21287          * Fires when the editor is fully initialized (including the iframe)
21288          * @param {Roo.HtmlEditorCore} this
21289          */
21290         initialize: true,
21291         /**
21292          * @event activate
21293          * Fires when the editor is first receives the focus. Any insertion must wait
21294          * until after this event.
21295          * @param {Roo.HtmlEditorCore} this
21296          */
21297         activate: true,
21298          /**
21299          * @event beforesync
21300          * Fires before the textarea is updated with content from the editor iframe. Return false
21301          * to cancel the sync.
21302          * @param {Roo.HtmlEditorCore} this
21303          * @param {String} html
21304          */
21305         beforesync: true,
21306          /**
21307          * @event beforepush
21308          * Fires before the iframe editor is updated with content from the textarea. Return false
21309          * to cancel the push.
21310          * @param {Roo.HtmlEditorCore} this
21311          * @param {String} html
21312          */
21313         beforepush: true,
21314          /**
21315          * @event sync
21316          * Fires when the textarea is updated with content from the editor iframe.
21317          * @param {Roo.HtmlEditorCore} this
21318          * @param {String} html
21319          */
21320         sync: true,
21321          /**
21322          * @event push
21323          * Fires when the iframe editor is updated with content from the textarea.
21324          * @param {Roo.HtmlEditorCore} this
21325          * @param {String} html
21326          */
21327         push: true,
21328         
21329         /**
21330          * @event editorevent
21331          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
21332          * @param {Roo.HtmlEditorCore} this
21333          */
21334         editorevent: true
21335         
21336     });
21337     
21338     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
21339     
21340     // defaults : white / black...
21341     this.applyBlacklists();
21342     
21343     
21344     
21345 };
21346
21347
21348 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
21349
21350
21351      /**
21352      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
21353      */
21354     
21355     owner : false,
21356     
21357      /**
21358      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
21359      *                        Roo.resizable.
21360      */
21361     resizable : false,
21362      /**
21363      * @cfg {Number} height (in pixels)
21364      */   
21365     height: 300,
21366    /**
21367      * @cfg {Number} width (in pixels)
21368      */   
21369     width: 500,
21370     
21371     /**
21372      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
21373      * 
21374      */
21375     stylesheets: false,
21376     
21377     // id of frame..
21378     frameId: false,
21379     
21380     // private properties
21381     validationEvent : false,
21382     deferHeight: true,
21383     initialized : false,
21384     activated : false,
21385     sourceEditMode : false,
21386     onFocus : Roo.emptyFn,
21387     iframePad:3,
21388     hideMode:'offsets',
21389     
21390     clearUp: true,
21391     
21392     // blacklist + whitelisted elements..
21393     black: false,
21394     white: false,
21395      
21396     bodyCls : '',
21397
21398     /**
21399      * Protected method that will not generally be called directly. It
21400      * is called when the editor initializes the iframe with HTML contents. Override this method if you
21401      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
21402      */
21403     getDocMarkup : function(){
21404         // body styles..
21405         var st = '';
21406         
21407         // inherit styels from page...?? 
21408         if (this.stylesheets === false) {
21409             
21410             Roo.get(document.head).select('style').each(function(node) {
21411                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21412             });
21413             
21414             Roo.get(document.head).select('link').each(function(node) { 
21415                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
21416             });
21417             
21418         } else if (!this.stylesheets.length) {
21419                 // simple..
21420                 st = '<style type="text/css">' +
21421                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21422                    '</style>';
21423         } else { 
21424             st = '<style type="text/css">' +
21425                     this.stylesheets +
21426                 '</style>';
21427         }
21428         
21429         st +=  '<style type="text/css">' +
21430             'IMG { cursor: pointer } ' +
21431         '</style>';
21432
21433         var cls = 'roo-htmleditor-body';
21434         
21435         if(this.bodyCls.length){
21436             cls += ' ' + this.bodyCls;
21437         }
21438         
21439         return '<html><head>' + st  +
21440             //<style type="text/css">' +
21441             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
21442             //'</style>' +
21443             ' </head><body class="' +  cls + '"></body></html>';
21444     },
21445
21446     // private
21447     onRender : function(ct, position)
21448     {
21449         var _t = this;
21450         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
21451         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
21452         
21453         
21454         this.el.dom.style.border = '0 none';
21455         this.el.dom.setAttribute('tabIndex', -1);
21456         this.el.addClass('x-hidden hide');
21457         
21458         
21459         
21460         if(Roo.isIE){ // fix IE 1px bogus margin
21461             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
21462         }
21463        
21464         
21465         this.frameId = Roo.id();
21466         
21467          
21468         
21469         var iframe = this.owner.wrap.createChild({
21470             tag: 'iframe',
21471             cls: 'form-control', // bootstrap..
21472             id: this.frameId,
21473             name: this.frameId,
21474             frameBorder : 'no',
21475             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
21476         }, this.el
21477         );
21478         
21479         
21480         this.iframe = iframe.dom;
21481
21482          this.assignDocWin();
21483         
21484         this.doc.designMode = 'on';
21485        
21486         this.doc.open();
21487         this.doc.write(this.getDocMarkup());
21488         this.doc.close();
21489
21490         
21491         var task = { // must defer to wait for browser to be ready
21492             run : function(){
21493                 //console.log("run task?" + this.doc.readyState);
21494                 this.assignDocWin();
21495                 if(this.doc.body || this.doc.readyState == 'complete'){
21496                     try {
21497                         this.doc.designMode="on";
21498                     } catch (e) {
21499                         return;
21500                     }
21501                     Roo.TaskMgr.stop(task);
21502                     this.initEditor.defer(10, this);
21503                 }
21504             },
21505             interval : 10,
21506             duration: 10000,
21507             scope: this
21508         };
21509         Roo.TaskMgr.start(task);
21510
21511     },
21512
21513     // private
21514     onResize : function(w, h)
21515     {
21516          Roo.log('resize: ' +w + ',' + h );
21517         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
21518         if(!this.iframe){
21519             return;
21520         }
21521         if(typeof w == 'number'){
21522             
21523             this.iframe.style.width = w + 'px';
21524         }
21525         if(typeof h == 'number'){
21526             
21527             this.iframe.style.height = h + 'px';
21528             if(this.doc){
21529                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
21530             }
21531         }
21532         
21533     },
21534
21535     /**
21536      * Toggles the editor between standard and source edit mode.
21537      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
21538      */
21539     toggleSourceEdit : function(sourceEditMode){
21540         
21541         this.sourceEditMode = sourceEditMode === true;
21542         
21543         if(this.sourceEditMode){
21544  
21545             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
21546             
21547         }else{
21548             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
21549             //this.iframe.className = '';
21550             this.deferFocus();
21551         }
21552         //this.setSize(this.owner.wrap.getSize());
21553         //this.fireEvent('editmodechange', this, this.sourceEditMode);
21554     },
21555
21556     
21557   
21558
21559     /**
21560      * Protected method that will not generally be called directly. If you need/want
21561      * custom HTML cleanup, this is the method you should override.
21562      * @param {String} html The HTML to be cleaned
21563      * return {String} The cleaned HTML
21564      */
21565     cleanHtml : function(html){
21566         html = String(html);
21567         if(html.length > 5){
21568             if(Roo.isSafari){ // strip safari nonsense
21569                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
21570             }
21571         }
21572         if(html == '&nbsp;'){
21573             html = '';
21574         }
21575         return html;
21576     },
21577
21578     /**
21579      * HTML Editor -> Textarea
21580      * Protected method that will not generally be called directly. Syncs the contents
21581      * of the editor iframe with the textarea.
21582      */
21583     syncValue : function(){
21584         if(this.initialized){
21585             var bd = (this.doc.body || this.doc.documentElement);
21586             //this.cleanUpPaste(); -- this is done else where and causes havoc..
21587             var html = bd.innerHTML;
21588             if(Roo.isSafari){
21589                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
21590                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
21591                 if(m && m[1]){
21592                     html = '<div style="'+m[0]+'">' + html + '</div>';
21593                 }
21594             }
21595             html = this.cleanHtml(html);
21596             // fix up the special chars.. normaly like back quotes in word...
21597             // however we do not want to do this with chinese..
21598             html = html.replace(/([\x80-\uffff])/g, function (a, b) {
21599                 var cc = b.charCodeAt();
21600                 if (
21601                     (cc >= 0x4E00 && cc < 0xA000 ) ||
21602                     (cc >= 0x3400 && cc < 0x4E00 ) ||
21603                     (cc >= 0xf900 && cc < 0xfb00 )
21604                 ) {
21605                         return b;
21606                 }
21607                 return "&#"+cc+";" 
21608             });
21609             if(this.owner.fireEvent('beforesync', this, html) !== false){
21610                 this.el.dom.value = html;
21611                 this.owner.fireEvent('sync', this, html);
21612             }
21613         }
21614     },
21615
21616     /**
21617      * Protected method that will not generally be called directly. Pushes the value of the textarea
21618      * into the iframe editor.
21619      */
21620     pushValue : function(){
21621         if(this.initialized){
21622             var v = this.el.dom.value.trim();
21623             
21624 //            if(v.length < 1){
21625 //                v = '&#160;';
21626 //            }
21627             
21628             if(this.owner.fireEvent('beforepush', this, v) !== false){
21629                 var d = (this.doc.body || this.doc.documentElement);
21630                 d.innerHTML = v;
21631                 this.cleanUpPaste();
21632                 this.el.dom.value = d.innerHTML;
21633                 this.owner.fireEvent('push', this, v);
21634             }
21635         }
21636     },
21637
21638     // private
21639     deferFocus : function(){
21640         this.focus.defer(10, this);
21641     },
21642
21643     // doc'ed in Field
21644     focus : function(){
21645         if(this.win && !this.sourceEditMode){
21646             this.win.focus();
21647         }else{
21648             this.el.focus();
21649         }
21650     },
21651     
21652     assignDocWin: function()
21653     {
21654         var iframe = this.iframe;
21655         
21656          if(Roo.isIE){
21657             this.doc = iframe.contentWindow.document;
21658             this.win = iframe.contentWindow;
21659         } else {
21660 //            if (!Roo.get(this.frameId)) {
21661 //                return;
21662 //            }
21663 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21664 //            this.win = Roo.get(this.frameId).dom.contentWindow;
21665             
21666             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
21667                 return;
21668             }
21669             
21670             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
21671             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
21672         }
21673     },
21674     
21675     // private
21676     initEditor : function(){
21677         //console.log("INIT EDITOR");
21678         this.assignDocWin();
21679         
21680         
21681         
21682         this.doc.designMode="on";
21683         this.doc.open();
21684         this.doc.write(this.getDocMarkup());
21685         this.doc.close();
21686         
21687         var dbody = (this.doc.body || this.doc.documentElement);
21688         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
21689         // this copies styles from the containing element into thsi one..
21690         // not sure why we need all of this..
21691         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
21692         
21693         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
21694         //ss['background-attachment'] = 'fixed'; // w3c
21695         dbody.bgProperties = 'fixed'; // ie
21696         //Roo.DomHelper.applyStyles(dbody, ss);
21697         Roo.EventManager.on(this.doc, {
21698             //'mousedown': this.onEditorEvent,
21699             'mouseup': this.onEditorEvent,
21700             'dblclick': this.onEditorEvent,
21701             'click': this.onEditorEvent,
21702             'keyup': this.onEditorEvent,
21703             buffer:100,
21704             scope: this
21705         });
21706         if(Roo.isGecko){
21707             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
21708         }
21709         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
21710             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
21711         }
21712         this.initialized = true;
21713
21714         this.owner.fireEvent('initialize', this);
21715         this.pushValue();
21716     },
21717
21718     // private
21719     onDestroy : function(){
21720         
21721         
21722         
21723         if(this.rendered){
21724             
21725             //for (var i =0; i < this.toolbars.length;i++) {
21726             //    // fixme - ask toolbars for heights?
21727             //    this.toolbars[i].onDestroy();
21728            // }
21729             
21730             //this.wrap.dom.innerHTML = '';
21731             //this.wrap.remove();
21732         }
21733     },
21734
21735     // private
21736     onFirstFocus : function(){
21737         
21738         this.assignDocWin();
21739         
21740         
21741         this.activated = true;
21742          
21743     
21744         if(Roo.isGecko){ // prevent silly gecko errors
21745             this.win.focus();
21746             var s = this.win.getSelection();
21747             if(!s.focusNode || s.focusNode.nodeType != 3){
21748                 var r = s.getRangeAt(0);
21749                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
21750                 r.collapse(true);
21751                 this.deferFocus();
21752             }
21753             try{
21754                 this.execCmd('useCSS', true);
21755                 this.execCmd('styleWithCSS', false);
21756             }catch(e){}
21757         }
21758         this.owner.fireEvent('activate', this);
21759     },
21760
21761     // private
21762     adjustFont: function(btn){
21763         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
21764         //if(Roo.isSafari){ // safari
21765         //    adjust *= 2;
21766        // }
21767         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
21768         if(Roo.isSafari){ // safari
21769             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
21770             v =  (v < 10) ? 10 : v;
21771             v =  (v > 48) ? 48 : v;
21772             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
21773             
21774         }
21775         
21776         
21777         v = Math.max(1, v+adjust);
21778         
21779         this.execCmd('FontSize', v  );
21780     },
21781
21782     onEditorEvent : function(e)
21783     {
21784         this.owner.fireEvent('editorevent', this, e);
21785       //  this.updateToolbar();
21786         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
21787     },
21788
21789     insertTag : function(tg)
21790     {
21791         // could be a bit smarter... -> wrap the current selected tRoo..
21792         if (tg.toLowerCase() == 'span' || tg.toLowerCase() == 'code') {
21793             
21794             range = this.createRange(this.getSelection());
21795             var wrappingNode = this.doc.createElement(tg.toLowerCase());
21796             wrappingNode.appendChild(range.extractContents());
21797             range.insertNode(wrappingNode);
21798
21799             return;
21800             
21801             
21802             
21803         }
21804         this.execCmd("formatblock",   tg);
21805         
21806     },
21807     
21808     insertText : function(txt)
21809     {
21810         
21811         
21812         var range = this.createRange();
21813         range.deleteContents();
21814                //alert(Sender.getAttribute('label'));
21815                
21816         range.insertNode(this.doc.createTextNode(txt));
21817     } ,
21818     
21819      
21820
21821     /**
21822      * Executes a Midas editor command on the editor document and performs necessary focus and
21823      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
21824      * @param {String} cmd The Midas command
21825      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
21826      */
21827     relayCmd : function(cmd, value){
21828         this.win.focus();
21829         this.execCmd(cmd, value);
21830         this.owner.fireEvent('editorevent', this);
21831         //this.updateToolbar();
21832         this.owner.deferFocus();
21833     },
21834
21835     /**
21836      * Executes a Midas editor command directly on the editor document.
21837      * For visual commands, you should use {@link #relayCmd} instead.
21838      * <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     execCmd : function(cmd, value){
21843         this.doc.execCommand(cmd, false, value === undefined ? null : value);
21844         this.syncValue();
21845     },
21846  
21847  
21848    
21849     /**
21850      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
21851      * to insert tRoo.
21852      * @param {String} text | dom node.. 
21853      */
21854     insertAtCursor : function(text)
21855     {
21856         
21857         if(!this.activated){
21858             return;
21859         }
21860         /*
21861         if(Roo.isIE){
21862             this.win.focus();
21863             var r = this.doc.selection.createRange();
21864             if(r){
21865                 r.collapse(true);
21866                 r.pasteHTML(text);
21867                 this.syncValue();
21868                 this.deferFocus();
21869             
21870             }
21871             return;
21872         }
21873         */
21874         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
21875             this.win.focus();
21876             
21877             
21878             // from jquery ui (MIT licenced)
21879             var range, node;
21880             var win = this.win;
21881             
21882             if (win.getSelection && win.getSelection().getRangeAt) {
21883                 range = win.getSelection().getRangeAt(0);
21884                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
21885                 range.insertNode(node);
21886             } else if (win.document.selection && win.document.selection.createRange) {
21887                 // no firefox support
21888                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21889                 win.document.selection.createRange().pasteHTML(txt);
21890             } else {
21891                 // no firefox support
21892                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
21893                 this.execCmd('InsertHTML', txt);
21894             } 
21895             
21896             this.syncValue();
21897             
21898             this.deferFocus();
21899         }
21900     },
21901  // private
21902     mozKeyPress : function(e){
21903         if(e.ctrlKey){
21904             var c = e.getCharCode(), cmd;
21905           
21906             if(c > 0){
21907                 c = String.fromCharCode(c).toLowerCase();
21908                 switch(c){
21909                     case 'b':
21910                         cmd = 'bold';
21911                         break;
21912                     case 'i':
21913                         cmd = 'italic';
21914                         break;
21915                     
21916                     case 'u':
21917                         cmd = 'underline';
21918                         break;
21919                     
21920                     case 'v':
21921                         this.cleanUpPaste.defer(100, this);
21922                         return;
21923                         
21924                 }
21925                 if(cmd){
21926                     this.win.focus();
21927                     this.execCmd(cmd);
21928                     this.deferFocus();
21929                     e.preventDefault();
21930                 }
21931                 
21932             }
21933         }
21934     },
21935
21936     // private
21937     fixKeys : function(){ // load time branching for fastest keydown performance
21938         if(Roo.isIE){
21939             return function(e){
21940                 var k = e.getKey(), r;
21941                 if(k == e.TAB){
21942                     e.stopEvent();
21943                     r = this.doc.selection.createRange();
21944                     if(r){
21945                         r.collapse(true);
21946                         r.pasteHTML('&#160;&#160;&#160;&#160;');
21947                         this.deferFocus();
21948                     }
21949                     return;
21950                 }
21951                 
21952                 if(k == e.ENTER){
21953                     r = this.doc.selection.createRange();
21954                     if(r){
21955                         var target = r.parentElement();
21956                         if(!target || target.tagName.toLowerCase() != 'li'){
21957                             e.stopEvent();
21958                             r.pasteHTML('<br />');
21959                             r.collapse(false);
21960                             r.select();
21961                         }
21962                     }
21963                 }
21964                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21965                     this.cleanUpPaste.defer(100, this);
21966                     return;
21967                 }
21968                 
21969                 
21970             };
21971         }else if(Roo.isOpera){
21972             return function(e){
21973                 var k = e.getKey();
21974                 if(k == e.TAB){
21975                     e.stopEvent();
21976                     this.win.focus();
21977                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
21978                     this.deferFocus();
21979                 }
21980                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21981                     this.cleanUpPaste.defer(100, this);
21982                     return;
21983                 }
21984                 
21985             };
21986         }else if(Roo.isSafari){
21987             return function(e){
21988                 var k = e.getKey();
21989                 
21990                 if(k == e.TAB){
21991                     e.stopEvent();
21992                     this.execCmd('InsertText','\t');
21993                     this.deferFocus();
21994                     return;
21995                 }
21996                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
21997                     this.cleanUpPaste.defer(100, this);
21998                     return;
21999                 }
22000                 
22001              };
22002         }
22003     }(),
22004     
22005     getAllAncestors: function()
22006     {
22007         var p = this.getSelectedNode();
22008         var a = [];
22009         if (!p) {
22010             a.push(p); // push blank onto stack..
22011             p = this.getParentElement();
22012         }
22013         
22014         
22015         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
22016             a.push(p);
22017             p = p.parentNode;
22018         }
22019         a.push(this.doc.body);
22020         return a;
22021     },
22022     lastSel : false,
22023     lastSelNode : false,
22024     
22025     
22026     getSelection : function() 
22027     {
22028         this.assignDocWin();
22029         return Roo.isIE ? this.doc.selection : this.win.getSelection();
22030     },
22031     
22032     getSelectedNode: function() 
22033     {
22034         // this may only work on Gecko!!!
22035         
22036         // should we cache this!!!!
22037         
22038         
22039         
22040          
22041         var range = this.createRange(this.getSelection()).cloneRange();
22042         
22043         if (Roo.isIE) {
22044             var parent = range.parentElement();
22045             while (true) {
22046                 var testRange = range.duplicate();
22047                 testRange.moveToElementText(parent);
22048                 if (testRange.inRange(range)) {
22049                     break;
22050                 }
22051                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
22052                     break;
22053                 }
22054                 parent = parent.parentElement;
22055             }
22056             return parent;
22057         }
22058         
22059         // is ancestor a text element.
22060         var ac =  range.commonAncestorContainer;
22061         if (ac.nodeType == 3) {
22062             ac = ac.parentNode;
22063         }
22064         
22065         var ar = ac.childNodes;
22066          
22067         var nodes = [];
22068         var other_nodes = [];
22069         var has_other_nodes = false;
22070         for (var i=0;i<ar.length;i++) {
22071             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
22072                 continue;
22073             }
22074             // fullly contained node.
22075             
22076             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
22077                 nodes.push(ar[i]);
22078                 continue;
22079             }
22080             
22081             // probably selected..
22082             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
22083                 other_nodes.push(ar[i]);
22084                 continue;
22085             }
22086             // outer..
22087             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
22088                 continue;
22089             }
22090             
22091             
22092             has_other_nodes = true;
22093         }
22094         if (!nodes.length && other_nodes.length) {
22095             nodes= other_nodes;
22096         }
22097         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
22098             return false;
22099         }
22100         
22101         return nodes[0];
22102     },
22103     createRange: function(sel)
22104     {
22105         // this has strange effects when using with 
22106         // top toolbar - not sure if it's a great idea.
22107         //this.editor.contentWindow.focus();
22108         if (typeof sel != "undefined") {
22109             try {
22110                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
22111             } catch(e) {
22112                 return this.doc.createRange();
22113             }
22114         } else {
22115             return this.doc.createRange();
22116         }
22117     },
22118     getParentElement: function()
22119     {
22120         
22121         this.assignDocWin();
22122         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
22123         
22124         var range = this.createRange(sel);
22125          
22126         try {
22127             var p = range.commonAncestorContainer;
22128             while (p.nodeType == 3) { // text node
22129                 p = p.parentNode;
22130             }
22131             return p;
22132         } catch (e) {
22133             return null;
22134         }
22135     
22136     },
22137     /***
22138      *
22139      * Range intersection.. the hard stuff...
22140      *  '-1' = before
22141      *  '0' = hits..
22142      *  '1' = after.
22143      *         [ -- selected range --- ]
22144      *   [fail]                        [fail]
22145      *
22146      *    basically..
22147      *      if end is before start or  hits it. fail.
22148      *      if start is after end or hits it fail.
22149      *
22150      *   if either hits (but other is outside. - then it's not 
22151      *   
22152      *    
22153      **/
22154     
22155     
22156     // @see http://www.thismuchiknow.co.uk/?p=64.
22157     rangeIntersectsNode : function(range, node)
22158     {
22159         var nodeRange = node.ownerDocument.createRange();
22160         try {
22161             nodeRange.selectNode(node);
22162         } catch (e) {
22163             nodeRange.selectNodeContents(node);
22164         }
22165     
22166         var rangeStartRange = range.cloneRange();
22167         rangeStartRange.collapse(true);
22168     
22169         var rangeEndRange = range.cloneRange();
22170         rangeEndRange.collapse(false);
22171     
22172         var nodeStartRange = nodeRange.cloneRange();
22173         nodeStartRange.collapse(true);
22174     
22175         var nodeEndRange = nodeRange.cloneRange();
22176         nodeEndRange.collapse(false);
22177     
22178         return rangeStartRange.compareBoundaryPoints(
22179                  Range.START_TO_START, nodeEndRange) == -1 &&
22180                rangeEndRange.compareBoundaryPoints(
22181                  Range.START_TO_START, nodeStartRange) == 1;
22182         
22183          
22184     },
22185     rangeCompareNode : function(range, node)
22186     {
22187         var nodeRange = node.ownerDocument.createRange();
22188         try {
22189             nodeRange.selectNode(node);
22190         } catch (e) {
22191             nodeRange.selectNodeContents(node);
22192         }
22193         
22194         
22195         range.collapse(true);
22196     
22197         nodeRange.collapse(true);
22198      
22199         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
22200         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
22201          
22202         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
22203         
22204         var nodeIsBefore   =  ss == 1;
22205         var nodeIsAfter    = ee == -1;
22206         
22207         if (nodeIsBefore && nodeIsAfter) {
22208             return 0; // outer
22209         }
22210         if (!nodeIsBefore && nodeIsAfter) {
22211             return 1; //right trailed.
22212         }
22213         
22214         if (nodeIsBefore && !nodeIsAfter) {
22215             return 2;  // left trailed.
22216         }
22217         // fully contined.
22218         return 3;
22219     },
22220
22221     // private? - in a new class?
22222     cleanUpPaste :  function()
22223     {
22224         // cleans up the whole document..
22225         Roo.log('cleanuppaste');
22226         
22227         this.cleanUpChildren(this.doc.body);
22228         var clean = this.cleanWordChars(this.doc.body.innerHTML);
22229         if (clean != this.doc.body.innerHTML) {
22230             this.doc.body.innerHTML = clean;
22231         }
22232         
22233     },
22234     
22235     cleanWordChars : function(input) {// change the chars to hex code
22236         var he = Roo.HtmlEditorCore;
22237         
22238         var output = input;
22239         Roo.each(he.swapCodes, function(sw) { 
22240             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
22241             
22242             output = output.replace(swapper, sw[1]);
22243         });
22244         
22245         return output;
22246     },
22247     
22248     
22249     cleanUpChildren : function (n)
22250     {
22251         if (!n.childNodes.length) {
22252             return;
22253         }
22254         for (var i = n.childNodes.length-1; i > -1 ; i--) {
22255            this.cleanUpChild(n.childNodes[i]);
22256         }
22257     },
22258     
22259     
22260         
22261     
22262     cleanUpChild : function (node)
22263     {
22264         var ed = this;
22265         //console.log(node);
22266         if (node.nodeName == "#text") {
22267             // clean up silly Windows -- stuff?
22268             return; 
22269         }
22270         if (node.nodeName == "#comment") {
22271             node.parentNode.removeChild(node);
22272             // clean up silly Windows -- stuff?
22273             return; 
22274         }
22275         var lcname = node.tagName.toLowerCase();
22276         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
22277         // whitelist of tags..
22278         
22279         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
22280             // remove node.
22281             node.parentNode.removeChild(node);
22282             return;
22283             
22284         }
22285         
22286         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
22287         
22288         // remove <a name=....> as rendering on yahoo mailer is borked with this.
22289         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
22290         
22291         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
22292         //    remove_keep_children = true;
22293         //}
22294         
22295         if (remove_keep_children) {
22296             this.cleanUpChildren(node);
22297             // inserts everything just before this node...
22298             while (node.childNodes.length) {
22299                 var cn = node.childNodes[0];
22300                 node.removeChild(cn);
22301                 node.parentNode.insertBefore(cn, node);
22302             }
22303             node.parentNode.removeChild(node);
22304             return;
22305         }
22306         
22307         if (!node.attributes || !node.attributes.length) {
22308             this.cleanUpChildren(node);
22309             return;
22310         }
22311         
22312         function cleanAttr(n,v)
22313         {
22314             
22315             if (v.match(/^\./) || v.match(/^\//)) {
22316                 return;
22317             }
22318             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/)) {
22319                 return;
22320             }
22321             if (v.match(/^#/)) {
22322                 return;
22323             }
22324 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
22325             node.removeAttribute(n);
22326             
22327         }
22328         
22329         var cwhite = this.cwhite;
22330         var cblack = this.cblack;
22331             
22332         function cleanStyle(n,v)
22333         {
22334             if (v.match(/expression/)) { //XSS?? should we even bother..
22335                 node.removeAttribute(n);
22336                 return;
22337             }
22338             
22339             var parts = v.split(/;/);
22340             var clean = [];
22341             
22342             Roo.each(parts, function(p) {
22343                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
22344                 if (!p.length) {
22345                     return true;
22346                 }
22347                 var l = p.split(':').shift().replace(/\s+/g,'');
22348                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
22349                 
22350                 if ( cwhite.length && cblack.indexOf(l) > -1) {
22351 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22352                     //node.removeAttribute(n);
22353                     return true;
22354                 }
22355                 //Roo.log()
22356                 // only allow 'c whitelisted system attributes'
22357                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
22358 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
22359                     //node.removeAttribute(n);
22360                     return true;
22361                 }
22362                 
22363                 
22364                  
22365                 
22366                 clean.push(p);
22367                 return true;
22368             });
22369             if (clean.length) { 
22370                 node.setAttribute(n, clean.join(';'));
22371             } else {
22372                 node.removeAttribute(n);
22373             }
22374             
22375         }
22376         
22377         
22378         for (var i = node.attributes.length-1; i > -1 ; i--) {
22379             var a = node.attributes[i];
22380             //console.log(a);
22381             
22382             if (a.name.toLowerCase().substr(0,2)=='on')  {
22383                 node.removeAttribute(a.name);
22384                 continue;
22385             }
22386             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
22387                 node.removeAttribute(a.name);
22388                 continue;
22389             }
22390             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
22391                 cleanAttr(a.name,a.value); // fixme..
22392                 continue;
22393             }
22394             if (a.name == 'style') {
22395                 cleanStyle(a.name,a.value);
22396                 continue;
22397             }
22398             /// clean up MS crap..
22399             // tecnically this should be a list of valid class'es..
22400             
22401             
22402             if (a.name == 'class') {
22403                 if (a.value.match(/^Mso/)) {
22404                     node.className = '';
22405                 }
22406                 
22407                 if (a.value.match(/^body$/)) {
22408                     node.className = '';
22409                 }
22410                 continue;
22411             }
22412             
22413             // style cleanup!?
22414             // class cleanup?
22415             
22416         }
22417         
22418         
22419         this.cleanUpChildren(node);
22420         
22421         
22422     },
22423     
22424     /**
22425      * Clean up MS wordisms...
22426      */
22427     cleanWord : function(node)
22428     {
22429         
22430         
22431         if (!node) {
22432             this.cleanWord(this.doc.body);
22433             return;
22434         }
22435         if (node.nodeName == "#text") {
22436             // clean up silly Windows -- stuff?
22437             return; 
22438         }
22439         if (node.nodeName == "#comment") {
22440             node.parentNode.removeChild(node);
22441             // clean up silly Windows -- stuff?
22442             return; 
22443         }
22444         
22445         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
22446             node.parentNode.removeChild(node);
22447             return;
22448         }
22449         
22450         // remove - but keep children..
22451         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|font)/)) {
22452             while (node.childNodes.length) {
22453                 var cn = node.childNodes[0];
22454                 node.removeChild(cn);
22455                 node.parentNode.insertBefore(cn, node);
22456             }
22457             node.parentNode.removeChild(node);
22458             this.iterateChildren(node, this.cleanWord);
22459             return;
22460         }
22461         // clean styles
22462         if (node.className.length) {
22463             
22464             var cn = node.className.split(/\W+/);
22465             var cna = [];
22466             Roo.each(cn, function(cls) {
22467                 if (cls.match(/Mso[a-zA-Z]+/)) {
22468                     return;
22469                 }
22470                 cna.push(cls);
22471             });
22472             node.className = cna.length ? cna.join(' ') : '';
22473             if (!cna.length) {
22474                 node.removeAttribute("class");
22475             }
22476         }
22477         
22478         if (node.hasAttribute("lang")) {
22479             node.removeAttribute("lang");
22480         }
22481         
22482         if (node.hasAttribute("style")) {
22483             
22484             var styles = node.getAttribute("style").split(";");
22485             var nstyle = [];
22486             Roo.each(styles, function(s) {
22487                 if (!s.match(/:/)) {
22488                     return;
22489                 }
22490                 var kv = s.split(":");
22491                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
22492                     return;
22493                 }
22494                 // what ever is left... we allow.
22495                 nstyle.push(s);
22496             });
22497             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22498             if (!nstyle.length) {
22499                 node.removeAttribute('style');
22500             }
22501         }
22502         this.iterateChildren(node, this.cleanWord);
22503         
22504         
22505         
22506     },
22507     /**
22508      * iterateChildren of a Node, calling fn each time, using this as the scole..
22509      * @param {DomNode} node node to iterate children of.
22510      * @param {Function} fn method of this class to call on each item.
22511      */
22512     iterateChildren : function(node, fn)
22513     {
22514         if (!node.childNodes.length) {
22515                 return;
22516         }
22517         for (var i = node.childNodes.length-1; i > -1 ; i--) {
22518            fn.call(this, node.childNodes[i])
22519         }
22520     },
22521     
22522     
22523     /**
22524      * cleanTableWidths.
22525      *
22526      * Quite often pasting from word etc.. results in tables with column and widths.
22527      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
22528      *
22529      */
22530     cleanTableWidths : function(node)
22531     {
22532          
22533          
22534         if (!node) {
22535             this.cleanTableWidths(this.doc.body);
22536             return;
22537         }
22538         
22539         // ignore list...
22540         if (node.nodeName == "#text" || node.nodeName == "#comment") {
22541             return; 
22542         }
22543         Roo.log(node.tagName);
22544         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
22545             this.iterateChildren(node, this.cleanTableWidths);
22546             return;
22547         }
22548         if (node.hasAttribute('width')) {
22549             node.removeAttribute('width');
22550         }
22551         
22552          
22553         if (node.hasAttribute("style")) {
22554             // pretty basic...
22555             
22556             var styles = node.getAttribute("style").split(";");
22557             var nstyle = [];
22558             Roo.each(styles, function(s) {
22559                 if (!s.match(/:/)) {
22560                     return;
22561                 }
22562                 var kv = s.split(":");
22563                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
22564                     return;
22565                 }
22566                 // what ever is left... we allow.
22567                 nstyle.push(s);
22568             });
22569             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
22570             if (!nstyle.length) {
22571                 node.removeAttribute('style');
22572             }
22573         }
22574         
22575         this.iterateChildren(node, this.cleanTableWidths);
22576         
22577         
22578     },
22579     
22580     
22581     
22582     
22583     domToHTML : function(currentElement, depth, nopadtext) {
22584         
22585         depth = depth || 0;
22586         nopadtext = nopadtext || false;
22587     
22588         if (!currentElement) {
22589             return this.domToHTML(this.doc.body);
22590         }
22591         
22592         //Roo.log(currentElement);
22593         var j;
22594         var allText = false;
22595         var nodeName = currentElement.nodeName;
22596         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
22597         
22598         if  (nodeName == '#text') {
22599             
22600             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
22601         }
22602         
22603         
22604         var ret = '';
22605         if (nodeName != 'BODY') {
22606              
22607             var i = 0;
22608             // Prints the node tagName, such as <A>, <IMG>, etc
22609             if (tagName) {
22610                 var attr = [];
22611                 for(i = 0; i < currentElement.attributes.length;i++) {
22612                     // quoting?
22613                     var aname = currentElement.attributes.item(i).name;
22614                     if (!currentElement.attributes.item(i).value.length) {
22615                         continue;
22616                     }
22617                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
22618                 }
22619                 
22620                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
22621             } 
22622             else {
22623                 
22624                 // eack
22625             }
22626         } else {
22627             tagName = false;
22628         }
22629         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
22630             return ret;
22631         }
22632         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
22633             nopadtext = true;
22634         }
22635         
22636         
22637         // Traverse the tree
22638         i = 0;
22639         var currentElementChild = currentElement.childNodes.item(i);
22640         var allText = true;
22641         var innerHTML  = '';
22642         lastnode = '';
22643         while (currentElementChild) {
22644             // Formatting code (indent the tree so it looks nice on the screen)
22645             var nopad = nopadtext;
22646             if (lastnode == 'SPAN') {
22647                 nopad  = true;
22648             }
22649             // text
22650             if  (currentElementChild.nodeName == '#text') {
22651                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
22652                 toadd = nopadtext ? toadd : toadd.trim();
22653                 if (!nopad && toadd.length > 80) {
22654                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
22655                 }
22656                 innerHTML  += toadd;
22657                 
22658                 i++;
22659                 currentElementChild = currentElement.childNodes.item(i);
22660                 lastNode = '';
22661                 continue;
22662             }
22663             allText = false;
22664             
22665             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
22666                 
22667             // Recursively traverse the tree structure of the child node
22668             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
22669             lastnode = currentElementChild.nodeName;
22670             i++;
22671             currentElementChild=currentElement.childNodes.item(i);
22672         }
22673         
22674         ret += innerHTML;
22675         
22676         if (!allText) {
22677                 // The remaining code is mostly for formatting the tree
22678             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
22679         }
22680         
22681         
22682         if (tagName) {
22683             ret+= "</"+tagName+">";
22684         }
22685         return ret;
22686         
22687     },
22688         
22689     applyBlacklists : function()
22690     {
22691         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
22692         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
22693         
22694         this.white = [];
22695         this.black = [];
22696         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
22697             if (b.indexOf(tag) > -1) {
22698                 return;
22699             }
22700             this.white.push(tag);
22701             
22702         }, this);
22703         
22704         Roo.each(w, function(tag) {
22705             if (b.indexOf(tag) > -1) {
22706                 return;
22707             }
22708             if (this.white.indexOf(tag) > -1) {
22709                 return;
22710             }
22711             this.white.push(tag);
22712             
22713         }, this);
22714         
22715         
22716         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
22717             if (w.indexOf(tag) > -1) {
22718                 return;
22719             }
22720             this.black.push(tag);
22721             
22722         }, this);
22723         
22724         Roo.each(b, function(tag) {
22725             if (w.indexOf(tag) > -1) {
22726                 return;
22727             }
22728             if (this.black.indexOf(tag) > -1) {
22729                 return;
22730             }
22731             this.black.push(tag);
22732             
22733         }, this);
22734         
22735         
22736         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
22737         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
22738         
22739         this.cwhite = [];
22740         this.cblack = [];
22741         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
22742             if (b.indexOf(tag) > -1) {
22743                 return;
22744             }
22745             this.cwhite.push(tag);
22746             
22747         }, this);
22748         
22749         Roo.each(w, function(tag) {
22750             if (b.indexOf(tag) > -1) {
22751                 return;
22752             }
22753             if (this.cwhite.indexOf(tag) > -1) {
22754                 return;
22755             }
22756             this.cwhite.push(tag);
22757             
22758         }, this);
22759         
22760         
22761         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
22762             if (w.indexOf(tag) > -1) {
22763                 return;
22764             }
22765             this.cblack.push(tag);
22766             
22767         }, this);
22768         
22769         Roo.each(b, function(tag) {
22770             if (w.indexOf(tag) > -1) {
22771                 return;
22772             }
22773             if (this.cblack.indexOf(tag) > -1) {
22774                 return;
22775             }
22776             this.cblack.push(tag);
22777             
22778         }, this);
22779     },
22780     
22781     setStylesheets : function(stylesheets)
22782     {
22783         if(typeof(stylesheets) == 'string'){
22784             Roo.get(this.iframe.contentDocument.head).createChild({
22785                 tag : 'link',
22786                 rel : 'stylesheet',
22787                 type : 'text/css',
22788                 href : stylesheets
22789             });
22790             
22791             return;
22792         }
22793         var _this = this;
22794      
22795         Roo.each(stylesheets, function(s) {
22796             if(!s.length){
22797                 return;
22798             }
22799             
22800             Roo.get(_this.iframe.contentDocument.head).createChild({
22801                 tag : 'link',
22802                 rel : 'stylesheet',
22803                 type : 'text/css',
22804                 href : s
22805             });
22806         });
22807
22808         
22809     },
22810     
22811     removeStylesheets : function()
22812     {
22813         var _this = this;
22814         
22815         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
22816             s.remove();
22817         });
22818     },
22819     
22820     setStyle : function(style)
22821     {
22822         Roo.get(this.iframe.contentDocument.head).createChild({
22823             tag : 'style',
22824             type : 'text/css',
22825             html : style
22826         });
22827
22828         return;
22829     }
22830     
22831     // hide stuff that is not compatible
22832     /**
22833      * @event blur
22834      * @hide
22835      */
22836     /**
22837      * @event change
22838      * @hide
22839      */
22840     /**
22841      * @event focus
22842      * @hide
22843      */
22844     /**
22845      * @event specialkey
22846      * @hide
22847      */
22848     /**
22849      * @cfg {String} fieldClass @hide
22850      */
22851     /**
22852      * @cfg {String} focusClass @hide
22853      */
22854     /**
22855      * @cfg {String} autoCreate @hide
22856      */
22857     /**
22858      * @cfg {String} inputType @hide
22859      */
22860     /**
22861      * @cfg {String} invalidClass @hide
22862      */
22863     /**
22864      * @cfg {String} invalidText @hide
22865      */
22866     /**
22867      * @cfg {String} msgFx @hide
22868      */
22869     /**
22870      * @cfg {String} validateOnBlur @hide
22871      */
22872 });
22873
22874 Roo.HtmlEditorCore.white = [
22875         'area', 'br', 'img', 'input', 'hr', 'wbr',
22876         
22877        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
22878        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
22879        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
22880        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
22881        'table',   'ul',         'xmp', 
22882        
22883        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
22884       'thead',   'tr', 
22885      
22886       'dir', 'menu', 'ol', 'ul', 'dl',
22887        
22888       'embed',  'object'
22889 ];
22890
22891
22892 Roo.HtmlEditorCore.black = [
22893     //    'embed',  'object', // enable - backend responsiblity to clean thiese
22894         'applet', // 
22895         'base',   'basefont', 'bgsound', 'blink',  'body', 
22896         'frame',  'frameset', 'head',    'html',   'ilayer', 
22897         'iframe', 'layer',  'link',     'meta',    'object',   
22898         'script', 'style' ,'title',  'xml' // clean later..
22899 ];
22900 Roo.HtmlEditorCore.clean = [
22901     'script', 'style', 'title', 'xml'
22902 ];
22903 Roo.HtmlEditorCore.remove = [
22904     'font'
22905 ];
22906 // attributes..
22907
22908 Roo.HtmlEditorCore.ablack = [
22909     'on'
22910 ];
22911     
22912 Roo.HtmlEditorCore.aclean = [ 
22913     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
22914 ];
22915
22916 // protocols..
22917 Roo.HtmlEditorCore.pwhite= [
22918         'http',  'https',  'mailto'
22919 ];
22920
22921 // white listed style attributes.
22922 Roo.HtmlEditorCore.cwhite= [
22923       //  'text-align', /// default is to allow most things..
22924       
22925          
22926 //        'font-size'//??
22927 ];
22928
22929 // black listed style attributes.
22930 Roo.HtmlEditorCore.cblack= [
22931       //  'font-size' -- this can be set by the project 
22932 ];
22933
22934
22935 Roo.HtmlEditorCore.swapCodes   =[ 
22936     [    8211, "--" ], 
22937     [    8212, "--" ], 
22938     [    8216,  "'" ],  
22939     [    8217, "'" ],  
22940     [    8220, '"' ],  
22941     [    8221, '"' ],  
22942     [    8226, "*" ],  
22943     [    8230, "..." ]
22944 ]; 
22945
22946     /*
22947  * - LGPL
22948  *
22949  * HtmlEditor
22950  * 
22951  */
22952
22953 /**
22954  * @class Roo.bootstrap.HtmlEditor
22955  * @extends Roo.bootstrap.TextArea
22956  * Bootstrap HtmlEditor class
22957
22958  * @constructor
22959  * Create a new HtmlEditor
22960  * @param {Object} config The config object
22961  */
22962
22963 Roo.bootstrap.HtmlEditor = function(config){
22964     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
22965     if (!this.toolbars) {
22966         this.toolbars = [];
22967     }
22968     
22969     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
22970     this.addEvents({
22971             /**
22972              * @event initialize
22973              * Fires when the editor is fully initialized (including the iframe)
22974              * @param {HtmlEditor} this
22975              */
22976             initialize: true,
22977             /**
22978              * @event activate
22979              * Fires when the editor is first receives the focus. Any insertion must wait
22980              * until after this event.
22981              * @param {HtmlEditor} this
22982              */
22983             activate: true,
22984              /**
22985              * @event beforesync
22986              * Fires before the textarea is updated with content from the editor iframe. Return false
22987              * to cancel the sync.
22988              * @param {HtmlEditor} this
22989              * @param {String} html
22990              */
22991             beforesync: true,
22992              /**
22993              * @event beforepush
22994              * Fires before the iframe editor is updated with content from the textarea. Return false
22995              * to cancel the push.
22996              * @param {HtmlEditor} this
22997              * @param {String} html
22998              */
22999             beforepush: true,
23000              /**
23001              * @event sync
23002              * Fires when the textarea is updated with content from the editor iframe.
23003              * @param {HtmlEditor} this
23004              * @param {String} html
23005              */
23006             sync: true,
23007              /**
23008              * @event push
23009              * Fires when the iframe editor is updated with content from the textarea.
23010              * @param {HtmlEditor} this
23011              * @param {String} html
23012              */
23013             push: true,
23014              /**
23015              * @event editmodechange
23016              * Fires when the editor switches edit modes
23017              * @param {HtmlEditor} this
23018              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
23019              */
23020             editmodechange: true,
23021             /**
23022              * @event editorevent
23023              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23024              * @param {HtmlEditor} this
23025              */
23026             editorevent: true,
23027             /**
23028              * @event firstfocus
23029              * Fires when on first focus - needed by toolbars..
23030              * @param {HtmlEditor} this
23031              */
23032             firstfocus: true,
23033             /**
23034              * @event autosave
23035              * Auto save the htmlEditor value as a file into Events
23036              * @param {HtmlEditor} this
23037              */
23038             autosave: true,
23039             /**
23040              * @event savedpreview
23041              * preview the saved version of htmlEditor
23042              * @param {HtmlEditor} this
23043              */
23044             savedpreview: true
23045         });
23046 };
23047
23048
23049 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
23050     
23051     
23052       /**
23053      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
23054      */
23055     toolbars : false,
23056     
23057      /**
23058     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
23059     */
23060     btns : [],
23061    
23062      /**
23063      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
23064      *                        Roo.resizable.
23065      */
23066     resizable : false,
23067      /**
23068      * @cfg {Number} height (in pixels)
23069      */   
23070     height: 300,
23071    /**
23072      * @cfg {Number} width (in pixels)
23073      */   
23074     width: false,
23075     
23076     /**
23077      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
23078      * 
23079      */
23080     stylesheets: false,
23081     
23082     // id of frame..
23083     frameId: false,
23084     
23085     // private properties
23086     validationEvent : false,
23087     deferHeight: true,
23088     initialized : false,
23089     activated : false,
23090     
23091     onFocus : Roo.emptyFn,
23092     iframePad:3,
23093     hideMode:'offsets',
23094     
23095     tbContainer : false,
23096     
23097     bodyCls : '',
23098     
23099     toolbarContainer :function() {
23100         return this.wrap.select('.x-html-editor-tb',true).first();
23101     },
23102
23103     /**
23104      * Protected method that will not generally be called directly. It
23105      * is called when the editor creates its toolbar. Override this method if you need to
23106      * add custom toolbar buttons.
23107      * @param {HtmlEditor} editor
23108      */
23109     createToolbar : function(){
23110         Roo.log('renewing');
23111         Roo.log("create toolbars");
23112         
23113         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
23114         this.toolbars[0].render(this.toolbarContainer());
23115         
23116         return;
23117         
23118 //        if (!editor.toolbars || !editor.toolbars.length) {
23119 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
23120 //        }
23121 //        
23122 //        for (var i =0 ; i < editor.toolbars.length;i++) {
23123 //            editor.toolbars[i] = Roo.factory(
23124 //                    typeof(editor.toolbars[i]) == 'string' ?
23125 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
23126 //                Roo.bootstrap.HtmlEditor);
23127 //            editor.toolbars[i].init(editor);
23128 //        }
23129     },
23130
23131      
23132     // private
23133     onRender : function(ct, position)
23134     {
23135        // Roo.log("Call onRender: " + this.xtype);
23136         var _t = this;
23137         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
23138       
23139         this.wrap = this.inputEl().wrap({
23140             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
23141         });
23142         
23143         this.editorcore.onRender(ct, position);
23144          
23145         if (this.resizable) {
23146             this.resizeEl = new Roo.Resizable(this.wrap, {
23147                 pinned : true,
23148                 wrap: true,
23149                 dynamic : true,
23150                 minHeight : this.height,
23151                 height: this.height,
23152                 handles : this.resizable,
23153                 width: this.width,
23154                 listeners : {
23155                     resize : function(r, w, h) {
23156                         _t.onResize(w,h); // -something
23157                     }
23158                 }
23159             });
23160             
23161         }
23162         this.createToolbar(this);
23163        
23164         
23165         if(!this.width && this.resizable){
23166             this.setSize(this.wrap.getSize());
23167         }
23168         if (this.resizeEl) {
23169             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
23170             // should trigger onReize..
23171         }
23172         
23173     },
23174
23175     // private
23176     onResize : function(w, h)
23177     {
23178         Roo.log('resize: ' +w + ',' + h );
23179         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
23180         var ew = false;
23181         var eh = false;
23182         
23183         if(this.inputEl() ){
23184             if(typeof w == 'number'){
23185                 var aw = w - this.wrap.getFrameWidth('lr');
23186                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
23187                 ew = aw;
23188             }
23189             if(typeof h == 'number'){
23190                  var tbh = -11;  // fixme it needs to tool bar size!
23191                 for (var i =0; i < this.toolbars.length;i++) {
23192                     // fixme - ask toolbars for heights?
23193                     tbh += this.toolbars[i].el.getHeight();
23194                     //if (this.toolbars[i].footer) {
23195                     //    tbh += this.toolbars[i].footer.el.getHeight();
23196                     //}
23197                 }
23198               
23199                 
23200                 
23201                 
23202                 
23203                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
23204                 ah -= 5; // knock a few pixes off for look..
23205                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
23206                 var eh = ah;
23207             }
23208         }
23209         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
23210         this.editorcore.onResize(ew,eh);
23211         
23212     },
23213
23214     /**
23215      * Toggles the editor between standard and source edit mode.
23216      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
23217      */
23218     toggleSourceEdit : function(sourceEditMode)
23219     {
23220         this.editorcore.toggleSourceEdit(sourceEditMode);
23221         
23222         if(this.editorcore.sourceEditMode){
23223             Roo.log('editor - showing textarea');
23224             
23225 //            Roo.log('in');
23226 //            Roo.log(this.syncValue());
23227             this.syncValue();
23228             this.inputEl().removeClass(['hide', 'x-hidden']);
23229             this.inputEl().dom.removeAttribute('tabIndex');
23230             this.inputEl().focus();
23231         }else{
23232             Roo.log('editor - hiding textarea');
23233 //            Roo.log('out')
23234 //            Roo.log(this.pushValue()); 
23235             this.pushValue();
23236             
23237             this.inputEl().addClass(['hide', 'x-hidden']);
23238             this.inputEl().dom.setAttribute('tabIndex', -1);
23239             //this.deferFocus();
23240         }
23241          
23242         if(this.resizable){
23243             this.setSize(this.wrap.getSize());
23244         }
23245         
23246         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
23247     },
23248  
23249     // private (for BoxComponent)
23250     adjustSize : Roo.BoxComponent.prototype.adjustSize,
23251
23252     // private (for BoxComponent)
23253     getResizeEl : function(){
23254         return this.wrap;
23255     },
23256
23257     // private (for BoxComponent)
23258     getPositionEl : function(){
23259         return this.wrap;
23260     },
23261
23262     // private
23263     initEvents : function(){
23264         this.originalValue = this.getValue();
23265     },
23266
23267 //    /**
23268 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23269 //     * @method
23270 //     */
23271 //    markInvalid : Roo.emptyFn,
23272 //    /**
23273 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
23274 //     * @method
23275 //     */
23276 //    clearInvalid : Roo.emptyFn,
23277
23278     setValue : function(v){
23279         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
23280         this.editorcore.pushValue();
23281     },
23282
23283      
23284     // private
23285     deferFocus : function(){
23286         this.focus.defer(10, this);
23287     },
23288
23289     // doc'ed in Field
23290     focus : function(){
23291         this.editorcore.focus();
23292         
23293     },
23294       
23295
23296     // private
23297     onDestroy : function(){
23298         
23299         
23300         
23301         if(this.rendered){
23302             
23303             for (var i =0; i < this.toolbars.length;i++) {
23304                 // fixme - ask toolbars for heights?
23305                 this.toolbars[i].onDestroy();
23306             }
23307             
23308             this.wrap.dom.innerHTML = '';
23309             this.wrap.remove();
23310         }
23311     },
23312
23313     // private
23314     onFirstFocus : function(){
23315         //Roo.log("onFirstFocus");
23316         this.editorcore.onFirstFocus();
23317          for (var i =0; i < this.toolbars.length;i++) {
23318             this.toolbars[i].onFirstFocus();
23319         }
23320         
23321     },
23322     
23323     // private
23324     syncValue : function()
23325     {   
23326         this.editorcore.syncValue();
23327     },
23328     
23329     pushValue : function()
23330     {   
23331         this.editorcore.pushValue();
23332     }
23333      
23334     
23335     // hide stuff that is not compatible
23336     /**
23337      * @event blur
23338      * @hide
23339      */
23340     /**
23341      * @event change
23342      * @hide
23343      */
23344     /**
23345      * @event focus
23346      * @hide
23347      */
23348     /**
23349      * @event specialkey
23350      * @hide
23351      */
23352     /**
23353      * @cfg {String} fieldClass @hide
23354      */
23355     /**
23356      * @cfg {String} focusClass @hide
23357      */
23358     /**
23359      * @cfg {String} autoCreate @hide
23360      */
23361     /**
23362      * @cfg {String} inputType @hide
23363      */
23364     /**
23365      * @cfg {String} invalidClass @hide
23366      */
23367     /**
23368      * @cfg {String} invalidText @hide
23369      */
23370     /**
23371      * @cfg {String} msgFx @hide
23372      */
23373     /**
23374      * @cfg {String} validateOnBlur @hide
23375      */
23376 });
23377  
23378     
23379    
23380    
23381    
23382       
23383 Roo.namespace('Roo.bootstrap.htmleditor');
23384 /**
23385  * @class Roo.bootstrap.HtmlEditorToolbar1
23386  * Basic Toolbar
23387  * 
23388  * Usage:
23389  *
23390  new Roo.bootstrap.HtmlEditor({
23391     ....
23392     toolbars : [
23393         new Roo.bootstrap.HtmlEditorToolbar1({
23394             disable : { fonts: 1 , format: 1, ..., ... , ...],
23395             btns : [ .... ]
23396         })
23397     }
23398      
23399  * 
23400  * @cfg {Object} disable List of elements to disable..
23401  * @cfg {Array} btns List of additional buttons.
23402  * 
23403  * 
23404  * NEEDS Extra CSS? 
23405  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
23406  */
23407  
23408 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
23409 {
23410     
23411     Roo.apply(this, config);
23412     
23413     // default disabled, based on 'good practice'..
23414     this.disable = this.disable || {};
23415     Roo.applyIf(this.disable, {
23416         fontSize : true,
23417         colors : true,
23418         specialElements : true
23419     });
23420     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
23421     
23422     this.editor = config.editor;
23423     this.editorcore = config.editor.editorcore;
23424     
23425     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
23426     
23427     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
23428     // dont call parent... till later.
23429 }
23430 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
23431      
23432     bar : true,
23433     
23434     editor : false,
23435     editorcore : false,
23436     
23437     
23438     formats : [
23439         "p" ,  
23440         "h1","h2","h3","h4","h5","h6", 
23441         "pre", "code", 
23442         "abbr", "acronym", "address", "cite", "samp", "var",
23443         'div','span'
23444     ],
23445     
23446     onRender : function(ct, position)
23447     {
23448        // Roo.log("Call onRender: " + this.xtype);
23449         
23450        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
23451        Roo.log(this.el);
23452        this.el.dom.style.marginBottom = '0';
23453        var _this = this;
23454        var editorcore = this.editorcore;
23455        var editor= this.editor;
23456        
23457        var children = [];
23458        var btn = function(id,cmd , toggle, handler, html){
23459        
23460             var  event = toggle ? 'toggle' : 'click';
23461        
23462             var a = {
23463                 size : 'sm',
23464                 xtype: 'Button',
23465                 xns: Roo.bootstrap,
23466                 glyphicon : id,
23467                 cmd : id || cmd,
23468                 enableToggle:toggle !== false,
23469                 html : html || '',
23470                 pressed : toggle ? false : null,
23471                 listeners : {}
23472             };
23473             a.listeners[toggle ? 'toggle' : 'click'] = function() {
23474                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
23475             };
23476             children.push(a);
23477             return a;
23478        }
23479        
23480     //    var cb_box = function...
23481         
23482         var style = {
23483                 xtype: 'Button',
23484                 size : 'sm',
23485                 xns: Roo.bootstrap,
23486                 glyphicon : 'font',
23487                 //html : 'submit'
23488                 menu : {
23489                     xtype: 'Menu',
23490                     xns: Roo.bootstrap,
23491                     items:  []
23492                 }
23493         };
23494         Roo.each(this.formats, function(f) {
23495             style.menu.items.push({
23496                 xtype :'MenuItem',
23497                 xns: Roo.bootstrap,
23498                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
23499                 tagname : f,
23500                 listeners : {
23501                     click : function()
23502                     {
23503                         editorcore.insertTag(this.tagname);
23504                         editor.focus();
23505                     }
23506                 }
23507                 
23508             });
23509         });
23510         children.push(style);   
23511         
23512         btn('bold',false,true);
23513         btn('italic',false,true);
23514         btn('align-left', 'justifyleft',true);
23515         btn('align-center', 'justifycenter',true);
23516         btn('align-right' , 'justifyright',true);
23517         btn('link', false, false, function(btn) {
23518             //Roo.log("create link?");
23519             var url = prompt(this.createLinkText, this.defaultLinkValue);
23520             if(url && url != 'http:/'+'/'){
23521                 this.editorcore.relayCmd('createlink', url);
23522             }
23523         }),
23524         btn('list','insertunorderedlist',true);
23525         btn('pencil', false,true, function(btn){
23526                 Roo.log(this);
23527                 this.toggleSourceEdit(btn.pressed);
23528         });
23529         
23530         if (this.editor.btns.length > 0) {
23531             for (var i = 0; i<this.editor.btns.length; i++) {
23532                 children.push(this.editor.btns[i]);
23533             }
23534         }
23535         
23536         /*
23537         var cog = {
23538                 xtype: 'Button',
23539                 size : 'sm',
23540                 xns: Roo.bootstrap,
23541                 glyphicon : 'cog',
23542                 //html : 'submit'
23543                 menu : {
23544                     xtype: 'Menu',
23545                     xns: Roo.bootstrap,
23546                     items:  []
23547                 }
23548         };
23549         
23550         cog.menu.items.push({
23551             xtype :'MenuItem',
23552             xns: Roo.bootstrap,
23553             html : Clean styles,
23554             tagname : f,
23555             listeners : {
23556                 click : function()
23557                 {
23558                     editorcore.insertTag(this.tagname);
23559                     editor.focus();
23560                 }
23561             }
23562             
23563         });
23564        */
23565         
23566          
23567        this.xtype = 'NavSimplebar';
23568         
23569         for(var i=0;i< children.length;i++) {
23570             
23571             this.buttons.add(this.addxtypeChild(children[i]));
23572             
23573         }
23574         
23575         editor.on('editorevent', this.updateToolbar, this);
23576     },
23577     onBtnClick : function(id)
23578     {
23579        this.editorcore.relayCmd(id);
23580        this.editorcore.focus();
23581     },
23582     
23583     /**
23584      * Protected method that will not generally be called directly. It triggers
23585      * a toolbar update by reading the markup state of the current selection in the editor.
23586      */
23587     updateToolbar: function(){
23588
23589         if(!this.editorcore.activated){
23590             this.editor.onFirstFocus(); // is this neeed?
23591             return;
23592         }
23593
23594         var btns = this.buttons; 
23595         var doc = this.editorcore.doc;
23596         btns.get('bold').setActive(doc.queryCommandState('bold'));
23597         btns.get('italic').setActive(doc.queryCommandState('italic'));
23598         //btns.get('underline').setActive(doc.queryCommandState('underline'));
23599         
23600         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
23601         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
23602         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
23603         
23604         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
23605         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
23606          /*
23607         
23608         var ans = this.editorcore.getAllAncestors();
23609         if (this.formatCombo) {
23610             
23611             
23612             var store = this.formatCombo.store;
23613             this.formatCombo.setValue("");
23614             for (var i =0; i < ans.length;i++) {
23615                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
23616                     // select it..
23617                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
23618                     break;
23619                 }
23620             }
23621         }
23622         
23623         
23624         
23625         // hides menus... - so this cant be on a menu...
23626         Roo.bootstrap.MenuMgr.hideAll();
23627         */
23628         Roo.bootstrap.MenuMgr.hideAll();
23629         //this.editorsyncValue();
23630     },
23631     onFirstFocus: function() {
23632         this.buttons.each(function(item){
23633            item.enable();
23634         });
23635     },
23636     toggleSourceEdit : function(sourceEditMode){
23637         
23638           
23639         if(sourceEditMode){
23640             Roo.log("disabling buttons");
23641            this.buttons.each( function(item){
23642                 if(item.cmd != 'pencil'){
23643                     item.disable();
23644                 }
23645             });
23646           
23647         }else{
23648             Roo.log("enabling buttons");
23649             if(this.editorcore.initialized){
23650                 this.buttons.each( function(item){
23651                     item.enable();
23652                 });
23653             }
23654             
23655         }
23656         Roo.log("calling toggole on editor");
23657         // tell the editor that it's been pressed..
23658         this.editor.toggleSourceEdit(sourceEditMode);
23659        
23660     }
23661 });
23662
23663
23664
23665
23666
23667 /**
23668  * @class Roo.bootstrap.Table.AbstractSelectionModel
23669  * @extends Roo.util.Observable
23670  * Abstract base class for grid SelectionModels.  It provides the interface that should be
23671  * implemented by descendant classes.  This class should not be directly instantiated.
23672  * @constructor
23673  */
23674 Roo.bootstrap.Table.AbstractSelectionModel = function(){
23675     this.locked = false;
23676     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
23677 };
23678
23679
23680 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
23681     /** @ignore Called by the grid automatically. Do not call directly. */
23682     init : function(grid){
23683         this.grid = grid;
23684         this.initEvents();
23685     },
23686
23687     /**
23688      * Locks the selections.
23689      */
23690     lock : function(){
23691         this.locked = true;
23692     },
23693
23694     /**
23695      * Unlocks the selections.
23696      */
23697     unlock : function(){
23698         this.locked = false;
23699     },
23700
23701     /**
23702      * Returns true if the selections are locked.
23703      * @return {Boolean}
23704      */
23705     isLocked : function(){
23706         return this.locked;
23707     }
23708 });
23709 /**
23710  * @extends Roo.bootstrap.Table.AbstractSelectionModel
23711  * @class Roo.bootstrap.Table.RowSelectionModel
23712  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
23713  * It supports multiple selections and keyboard selection/navigation. 
23714  * @constructor
23715  * @param {Object} config
23716  */
23717
23718 Roo.bootstrap.Table.RowSelectionModel = function(config){
23719     Roo.apply(this, config);
23720     this.selections = new Roo.util.MixedCollection(false, function(o){
23721         return o.id;
23722     });
23723
23724     this.last = false;
23725     this.lastActive = false;
23726
23727     this.addEvents({
23728         /**
23729              * @event selectionchange
23730              * Fires when the selection changes
23731              * @param {SelectionModel} this
23732              */
23733             "selectionchange" : true,
23734         /**
23735              * @event afterselectionchange
23736              * Fires after the selection changes (eg. by key press or clicking)
23737              * @param {SelectionModel} this
23738              */
23739             "afterselectionchange" : true,
23740         /**
23741              * @event beforerowselect
23742              * Fires when a row is selected being selected, return false to cancel.
23743              * @param {SelectionModel} this
23744              * @param {Number} rowIndex The selected index
23745              * @param {Boolean} keepExisting False if other selections will be cleared
23746              */
23747             "beforerowselect" : true,
23748         /**
23749              * @event rowselect
23750              * Fires when a row is selected.
23751              * @param {SelectionModel} this
23752              * @param {Number} rowIndex The selected index
23753              * @param {Roo.data.Record} r The record
23754              */
23755             "rowselect" : true,
23756         /**
23757              * @event rowdeselect
23758              * Fires when a row is deselected.
23759              * @param {SelectionModel} this
23760              * @param {Number} rowIndex The selected index
23761              */
23762         "rowdeselect" : true
23763     });
23764     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
23765     this.locked = false;
23766  };
23767
23768 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
23769     /**
23770      * @cfg {Boolean} singleSelect
23771      * True to allow selection of only one row at a time (defaults to false)
23772      */
23773     singleSelect : false,
23774
23775     // private
23776     initEvents : function()
23777     {
23778
23779         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
23780         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
23781         //}else{ // allow click to work like normal
23782          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
23783         //}
23784         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
23785         this.grid.on("rowclick", this.handleMouseDown, this);
23786         
23787         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
23788             "up" : function(e){
23789                 if(!e.shiftKey){
23790                     this.selectPrevious(e.shiftKey);
23791                 }else if(this.last !== false && this.lastActive !== false){
23792                     var last = this.last;
23793                     this.selectRange(this.last,  this.lastActive-1);
23794                     this.grid.getView().focusRow(this.lastActive);
23795                     if(last !== false){
23796                         this.last = last;
23797                     }
23798                 }else{
23799                     this.selectFirstRow();
23800                 }
23801                 this.fireEvent("afterselectionchange", this);
23802             },
23803             "down" : function(e){
23804                 if(!e.shiftKey){
23805                     this.selectNext(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             scope: this
23819         });
23820         this.grid.store.on('load', function(){
23821             this.selections.clear();
23822         },this);
23823         /*
23824         var view = this.grid.view;
23825         view.on("refresh", this.onRefresh, this);
23826         view.on("rowupdated", this.onRowUpdated, this);
23827         view.on("rowremoved", this.onRemove, this);
23828         */
23829     },
23830
23831     // private
23832     onRefresh : function()
23833     {
23834         var ds = this.grid.store, i, v = this.grid.view;
23835         var s = this.selections;
23836         s.each(function(r){
23837             if((i = ds.indexOfId(r.id)) != -1){
23838                 v.onRowSelect(i);
23839             }else{
23840                 s.remove(r);
23841             }
23842         });
23843     },
23844
23845     // private
23846     onRemove : function(v, index, r){
23847         this.selections.remove(r);
23848     },
23849
23850     // private
23851     onRowUpdated : function(v, index, r){
23852         if(this.isSelected(r)){
23853             v.onRowSelect(index);
23854         }
23855     },
23856
23857     /**
23858      * Select records.
23859      * @param {Array} records The records to select
23860      * @param {Boolean} keepExisting (optional) True to keep existing selections
23861      */
23862     selectRecords : function(records, keepExisting)
23863     {
23864         if(!keepExisting){
23865             this.clearSelections();
23866         }
23867             var ds = this.grid.store;
23868         for(var i = 0, len = records.length; i < len; i++){
23869             this.selectRow(ds.indexOf(records[i]), true);
23870         }
23871     },
23872
23873     /**
23874      * Gets the number of selected rows.
23875      * @return {Number}
23876      */
23877     getCount : function(){
23878         return this.selections.length;
23879     },
23880
23881     /**
23882      * Selects the first row in the grid.
23883      */
23884     selectFirstRow : function(){
23885         this.selectRow(0);
23886     },
23887
23888     /**
23889      * Select the last row.
23890      * @param {Boolean} keepExisting (optional) True to keep existing selections
23891      */
23892     selectLastRow : function(keepExisting){
23893         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
23894         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
23895     },
23896
23897     /**
23898      * Selects the row immediately following the last selected row.
23899      * @param {Boolean} keepExisting (optional) True to keep existing selections
23900      */
23901     selectNext : function(keepExisting)
23902     {
23903             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
23904             this.selectRow(this.last+1, keepExisting);
23905             this.grid.getView().focusRow(this.last);
23906         }
23907     },
23908
23909     /**
23910      * Selects the row that precedes the last selected row.
23911      * @param {Boolean} keepExisting (optional) True to keep existing selections
23912      */
23913     selectPrevious : function(keepExisting){
23914         if(this.last){
23915             this.selectRow(this.last-1, keepExisting);
23916             this.grid.getView().focusRow(this.last);
23917         }
23918     },
23919
23920     /**
23921      * Returns the selected records
23922      * @return {Array} Array of selected records
23923      */
23924     getSelections : function(){
23925         return [].concat(this.selections.items);
23926     },
23927
23928     /**
23929      * Returns the first selected record.
23930      * @return {Record}
23931      */
23932     getSelected : function(){
23933         return this.selections.itemAt(0);
23934     },
23935
23936
23937     /**
23938      * Clears all selections.
23939      */
23940     clearSelections : function(fast)
23941     {
23942         if(this.locked) {
23943             return;
23944         }
23945         if(fast !== true){
23946                 var ds = this.grid.store;
23947             var s = this.selections;
23948             s.each(function(r){
23949                 this.deselectRow(ds.indexOfId(r.id));
23950             }, this);
23951             s.clear();
23952         }else{
23953             this.selections.clear();
23954         }
23955         this.last = false;
23956     },
23957
23958
23959     /**
23960      * Selects all rows.
23961      */
23962     selectAll : function(){
23963         if(this.locked) {
23964             return;
23965         }
23966         this.selections.clear();
23967         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
23968             this.selectRow(i, true);
23969         }
23970     },
23971
23972     /**
23973      * Returns True if there is a selection.
23974      * @return {Boolean}
23975      */
23976     hasSelection : function(){
23977         return this.selections.length > 0;
23978     },
23979
23980     /**
23981      * Returns True if the specified row is selected.
23982      * @param {Number/Record} record The record or index of the record to check
23983      * @return {Boolean}
23984      */
23985     isSelected : function(index){
23986             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
23987         return (r && this.selections.key(r.id) ? true : false);
23988     },
23989
23990     /**
23991      * Returns True if the specified record id is selected.
23992      * @param {String} id The id of record to check
23993      * @return {Boolean}
23994      */
23995     isIdSelected : function(id){
23996         return (this.selections.key(id) ? true : false);
23997     },
23998
23999
24000     // private
24001     handleMouseDBClick : function(e, t){
24002         
24003     },
24004     // private
24005     handleMouseDown : function(e, t)
24006     {
24007             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
24008         if(this.isLocked() || rowIndex < 0 ){
24009             return;
24010         };
24011         if(e.shiftKey && this.last !== false){
24012             var last = this.last;
24013             this.selectRange(last, rowIndex, e.ctrlKey);
24014             this.last = last; // reset the last
24015             t.focus();
24016     
24017         }else{
24018             var isSelected = this.isSelected(rowIndex);
24019             //Roo.log("select row:" + rowIndex);
24020             if(isSelected){
24021                 this.deselectRow(rowIndex);
24022             } else {
24023                         this.selectRow(rowIndex, true);
24024             }
24025     
24026             /*
24027                 if(e.button !== 0 && isSelected){
24028                 alert('rowIndex 2: ' + rowIndex);
24029                     view.focusRow(rowIndex);
24030                 }else if(e.ctrlKey && isSelected){
24031                     this.deselectRow(rowIndex);
24032                 }else if(!isSelected){
24033                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
24034                     view.focusRow(rowIndex);
24035                 }
24036             */
24037         }
24038         this.fireEvent("afterselectionchange", this);
24039     },
24040     // private
24041     handleDragableRowClick :  function(grid, rowIndex, e) 
24042     {
24043         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
24044             this.selectRow(rowIndex, false);
24045             grid.view.focusRow(rowIndex);
24046              this.fireEvent("afterselectionchange", this);
24047         }
24048     },
24049     
24050     /**
24051      * Selects multiple rows.
24052      * @param {Array} rows Array of the indexes of the row to select
24053      * @param {Boolean} keepExisting (optional) True to keep existing selections
24054      */
24055     selectRows : function(rows, keepExisting){
24056         if(!keepExisting){
24057             this.clearSelections();
24058         }
24059         for(var i = 0, len = rows.length; i < len; i++){
24060             this.selectRow(rows[i], true);
24061         }
24062     },
24063
24064     /**
24065      * Selects a range of rows. All rows in between startRow and endRow are also selected.
24066      * @param {Number} startRow The index of the first row in the range
24067      * @param {Number} endRow The index of the last row in the range
24068      * @param {Boolean} keepExisting (optional) True to retain existing selections
24069      */
24070     selectRange : function(startRow, endRow, keepExisting){
24071         if(this.locked) {
24072             return;
24073         }
24074         if(!keepExisting){
24075             this.clearSelections();
24076         }
24077         if(startRow <= endRow){
24078             for(var i = startRow; i <= endRow; i++){
24079                 this.selectRow(i, true);
24080             }
24081         }else{
24082             for(var i = startRow; i >= endRow; i--){
24083                 this.selectRow(i, true);
24084             }
24085         }
24086     },
24087
24088     /**
24089      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
24090      * @param {Number} startRow The index of the first row in the range
24091      * @param {Number} endRow The index of the last row in the range
24092      */
24093     deselectRange : function(startRow, endRow, preventViewNotify){
24094         if(this.locked) {
24095             return;
24096         }
24097         for(var i = startRow; i <= endRow; i++){
24098             this.deselectRow(i, preventViewNotify);
24099         }
24100     },
24101
24102     /**
24103      * Selects a row.
24104      * @param {Number} row The index of the row to select
24105      * @param {Boolean} keepExisting (optional) True to keep existing selections
24106      */
24107     selectRow : function(index, keepExisting, preventViewNotify)
24108     {
24109             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
24110             return;
24111         }
24112         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
24113             if(!keepExisting || this.singleSelect){
24114                 this.clearSelections();
24115             }
24116             
24117             var r = this.grid.store.getAt(index);
24118             //console.log('selectRow - record id :' + r.id);
24119             
24120             this.selections.add(r);
24121             this.last = this.lastActive = index;
24122             if(!preventViewNotify){
24123                 var proxy = new Roo.Element(
24124                                 this.grid.getRowDom(index)
24125                 );
24126                 proxy.addClass('bg-info info');
24127             }
24128             this.fireEvent("rowselect", this, index, r);
24129             this.fireEvent("selectionchange", this);
24130         }
24131     },
24132
24133     /**
24134      * Deselects a row.
24135      * @param {Number} row The index of the row to deselect
24136      */
24137     deselectRow : function(index, preventViewNotify)
24138     {
24139         if(this.locked) {
24140             return;
24141         }
24142         if(this.last == index){
24143             this.last = false;
24144         }
24145         if(this.lastActive == index){
24146             this.lastActive = false;
24147         }
24148         
24149         var r = this.grid.store.getAt(index);
24150         if (!r) {
24151             return;
24152         }
24153         
24154         this.selections.remove(r);
24155         //.console.log('deselectRow - record id :' + r.id);
24156         if(!preventViewNotify){
24157         
24158             var proxy = new Roo.Element(
24159                 this.grid.getRowDom(index)
24160             );
24161             proxy.removeClass('bg-info info');
24162         }
24163         this.fireEvent("rowdeselect", this, index);
24164         this.fireEvent("selectionchange", this);
24165     },
24166
24167     // private
24168     restoreLast : function(){
24169         if(this._last){
24170             this.last = this._last;
24171         }
24172     },
24173
24174     // private
24175     acceptsNav : function(row, col, cm){
24176         return !cm.isHidden(col) && cm.isCellEditable(col, row);
24177     },
24178
24179     // private
24180     onEditorKey : function(field, e){
24181         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
24182         if(k == e.TAB){
24183             e.stopEvent();
24184             ed.completeEdit();
24185             if(e.shiftKey){
24186                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
24187             }else{
24188                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
24189             }
24190         }else if(k == e.ENTER && !e.ctrlKey){
24191             e.stopEvent();
24192             ed.completeEdit();
24193             if(e.shiftKey){
24194                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
24195             }else{
24196                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
24197             }
24198         }else if(k == e.ESC){
24199             ed.cancelEdit();
24200         }
24201         if(newCell){
24202             g.startEditing(newCell[0], newCell[1]);
24203         }
24204     }
24205 });
24206 /*
24207  * Based on:
24208  * Ext JS Library 1.1.1
24209  * Copyright(c) 2006-2007, Ext JS, LLC.
24210  *
24211  * Originally Released Under LGPL - original licence link has changed is not relivant.
24212  *
24213  * Fork - LGPL
24214  * <script type="text/javascript">
24215  */
24216  
24217 /**
24218  * @class Roo.bootstrap.PagingToolbar
24219  * @extends Roo.bootstrap.NavSimplebar
24220  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
24221  * @constructor
24222  * Create a new PagingToolbar
24223  * @param {Object} config The config object
24224  * @param {Roo.data.Store} store
24225  */
24226 Roo.bootstrap.PagingToolbar = function(config)
24227 {
24228     // old args format still supported... - xtype is prefered..
24229         // created from xtype...
24230     
24231     this.ds = config.dataSource;
24232     
24233     if (config.store && !this.ds) {
24234         this.store= Roo.factory(config.store, Roo.data);
24235         this.ds = this.store;
24236         this.ds.xmodule = this.xmodule || false;
24237     }
24238     
24239     this.toolbarItems = [];
24240     if (config.items) {
24241         this.toolbarItems = config.items;
24242     }
24243     
24244     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
24245     
24246     this.cursor = 0;
24247     
24248     if (this.ds) { 
24249         this.bind(this.ds);
24250     }
24251     
24252     this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
24253     
24254 };
24255
24256 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
24257     /**
24258      * @cfg {Roo.data.Store} dataSource
24259      * The underlying data store providing the paged data
24260      */
24261     /**
24262      * @cfg {String/HTMLElement/Element} container
24263      * container The id or element that will contain the toolbar
24264      */
24265     /**
24266      * @cfg {Boolean} displayInfo
24267      * True to display the displayMsg (defaults to false)
24268      */
24269     /**
24270      * @cfg {Number} pageSize
24271      * The number of records to display per page (defaults to 20)
24272      */
24273     pageSize: 20,
24274     /**
24275      * @cfg {String} displayMsg
24276      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
24277      */
24278     displayMsg : 'Displaying {0} - {1} of {2}',
24279     /**
24280      * @cfg {String} emptyMsg
24281      * The message to display when no records are found (defaults to "No data to display")
24282      */
24283     emptyMsg : 'No data to display',
24284     /**
24285      * Customizable piece of the default paging text (defaults to "Page")
24286      * @type String
24287      */
24288     beforePageText : "Page",
24289     /**
24290      * Customizable piece of the default paging text (defaults to "of %0")
24291      * @type String
24292      */
24293     afterPageText : "of {0}",
24294     /**
24295      * Customizable piece of the default paging text (defaults to "First Page")
24296      * @type String
24297      */
24298     firstText : "First Page",
24299     /**
24300      * Customizable piece of the default paging text (defaults to "Previous Page")
24301      * @type String
24302      */
24303     prevText : "Previous Page",
24304     /**
24305      * Customizable piece of the default paging text (defaults to "Next Page")
24306      * @type String
24307      */
24308     nextText : "Next Page",
24309     /**
24310      * Customizable piece of the default paging text (defaults to "Last Page")
24311      * @type String
24312      */
24313     lastText : "Last Page",
24314     /**
24315      * Customizable piece of the default paging text (defaults to "Refresh")
24316      * @type String
24317      */
24318     refreshText : "Refresh",
24319
24320     buttons : false,
24321     // private
24322     onRender : function(ct, position) 
24323     {
24324         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
24325         this.navgroup.parentId = this.id;
24326         this.navgroup.onRender(this.el, null);
24327         // add the buttons to the navgroup
24328         
24329         if(this.displayInfo){
24330             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
24331             this.displayEl = this.el.select('.x-paging-info', true).first();
24332 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
24333 //            this.displayEl = navel.el.select('span',true).first();
24334         }
24335         
24336         var _this = this;
24337         
24338         if(this.buttons){
24339             Roo.each(_this.buttons, function(e){ // this might need to use render????
24340                Roo.factory(e).onRender(_this.el, null);
24341             });
24342         }
24343             
24344         Roo.each(_this.toolbarItems, function(e) {
24345             _this.navgroup.addItem(e);
24346         });
24347         
24348         
24349         this.first = this.navgroup.addItem({
24350             tooltip: this.firstText,
24351             cls: "prev",
24352             icon : 'fa fa-backward',
24353             disabled: true,
24354             preventDefault: true,
24355             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
24356         });
24357         
24358         this.prev =  this.navgroup.addItem({
24359             tooltip: this.prevText,
24360             cls: "prev",
24361             icon : 'fa fa-step-backward',
24362             disabled: true,
24363             preventDefault: true,
24364             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
24365         });
24366     //this.addSeparator();
24367         
24368         
24369         var field = this.navgroup.addItem( {
24370             tagtype : 'span',
24371             cls : 'x-paging-position',
24372             
24373             html : this.beforePageText  +
24374                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
24375                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
24376          } ); //?? escaped?
24377         
24378         this.field = field.el.select('input', true).first();
24379         this.field.on("keydown", this.onPagingKeydown, this);
24380         this.field.on("focus", function(){this.dom.select();});
24381     
24382     
24383         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
24384         //this.field.setHeight(18);
24385         //this.addSeparator();
24386         this.next = this.navgroup.addItem({
24387             tooltip: this.nextText,
24388             cls: "next",
24389             html : ' <i class="fa fa-step-forward">',
24390             disabled: true,
24391             preventDefault: true,
24392             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
24393         });
24394         this.last = this.navgroup.addItem({
24395             tooltip: this.lastText,
24396             icon : 'fa fa-forward',
24397             cls: "next",
24398             disabled: true,
24399             preventDefault: true,
24400             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
24401         });
24402     //this.addSeparator();
24403         this.loading = this.navgroup.addItem({
24404             tooltip: this.refreshText,
24405             icon: 'fa fa-refresh',
24406             preventDefault: true,
24407             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
24408         });
24409         
24410     },
24411
24412     // private
24413     updateInfo : function(){
24414         if(this.displayEl){
24415             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
24416             var msg = count == 0 ?
24417                 this.emptyMsg :
24418                 String.format(
24419                     this.displayMsg,
24420                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
24421                 );
24422             this.displayEl.update(msg);
24423         }
24424     },
24425
24426     // private
24427     onLoad : function(ds, r, o)
24428     {
24429         this.cursor = o.params ? o.params.start : 0;
24430         var d = this.getPageData(),
24431             ap = d.activePage,
24432             ps = d.pages;
24433         
24434         
24435         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
24436         this.field.dom.value = ap;
24437         this.first.setDisabled(ap == 1);
24438         this.prev.setDisabled(ap == 1);
24439         this.next.setDisabled(ap == ps);
24440         this.last.setDisabled(ap == ps);
24441         this.loading.enable();
24442         this.updateInfo();
24443     },
24444
24445     // private
24446     getPageData : function(){
24447         var total = this.ds.getTotalCount();
24448         return {
24449             total : total,
24450             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
24451             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
24452         };
24453     },
24454
24455     // private
24456     onLoadError : function(){
24457         this.loading.enable();
24458     },
24459
24460     // private
24461     onPagingKeydown : function(e){
24462         var k = e.getKey();
24463         var d = this.getPageData();
24464         if(k == e.RETURN){
24465             var v = this.field.dom.value, pageNum;
24466             if(!v || isNaN(pageNum = parseInt(v, 10))){
24467                 this.field.dom.value = d.activePage;
24468                 return;
24469             }
24470             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
24471             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24472             e.stopEvent();
24473         }
24474         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))
24475         {
24476           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
24477           this.field.dom.value = pageNum;
24478           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
24479           e.stopEvent();
24480         }
24481         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
24482         {
24483           var v = this.field.dom.value, pageNum; 
24484           var increment = (e.shiftKey) ? 10 : 1;
24485           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
24486                 increment *= -1;
24487           }
24488           if(!v || isNaN(pageNum = parseInt(v, 10))) {
24489             this.field.dom.value = d.activePage;
24490             return;
24491           }
24492           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
24493           {
24494             this.field.dom.value = parseInt(v, 10) + increment;
24495             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
24496             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
24497           }
24498           e.stopEvent();
24499         }
24500     },
24501
24502     // private
24503     beforeLoad : function(){
24504         if(this.loading){
24505             this.loading.disable();
24506         }
24507     },
24508
24509     // private
24510     onClick : function(which){
24511         
24512         var ds = this.ds;
24513         if (!ds) {
24514             return;
24515         }
24516         
24517         switch(which){
24518             case "first":
24519                 ds.load({params:{start: 0, limit: this.pageSize}});
24520             break;
24521             case "prev":
24522                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
24523             break;
24524             case "next":
24525                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
24526             break;
24527             case "last":
24528                 var total = ds.getTotalCount();
24529                 var extra = total % this.pageSize;
24530                 var lastStart = extra ? (total - extra) : total-this.pageSize;
24531                 ds.load({params:{start: lastStart, limit: this.pageSize}});
24532             break;
24533             case "refresh":
24534                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
24535             break;
24536         }
24537     },
24538
24539     /**
24540      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
24541      * @param {Roo.data.Store} store The data store to unbind
24542      */
24543     unbind : function(ds){
24544         ds.un("beforeload", this.beforeLoad, this);
24545         ds.un("load", this.onLoad, this);
24546         ds.un("loadexception", this.onLoadError, this);
24547         ds.un("remove", this.updateInfo, this);
24548         ds.un("add", this.updateInfo, this);
24549         this.ds = undefined;
24550     },
24551
24552     /**
24553      * Binds the paging toolbar to the specified {@link Roo.data.Store}
24554      * @param {Roo.data.Store} store The data store to bind
24555      */
24556     bind : function(ds){
24557         ds.on("beforeload", this.beforeLoad, this);
24558         ds.on("load", this.onLoad, this);
24559         ds.on("loadexception", this.onLoadError, this);
24560         ds.on("remove", this.updateInfo, this);
24561         ds.on("add", this.updateInfo, this);
24562         this.ds = ds;
24563     }
24564 });/*
24565  * - LGPL
24566  *
24567  * element
24568  * 
24569  */
24570
24571 /**
24572  * @class Roo.bootstrap.MessageBar
24573  * @extends Roo.bootstrap.Component
24574  * Bootstrap MessageBar class
24575  * @cfg {String} html contents of the MessageBar
24576  * @cfg {String} weight (info | success | warning | danger) default info
24577  * @cfg {String} beforeClass insert the bar before the given class
24578  * @cfg {Boolean} closable (true | false) default false
24579  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
24580  * 
24581  * @constructor
24582  * Create a new Element
24583  * @param {Object} config The config object
24584  */
24585
24586 Roo.bootstrap.MessageBar = function(config){
24587     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
24588 };
24589
24590 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
24591     
24592     html: '',
24593     weight: 'info',
24594     closable: false,
24595     fixed: false,
24596     beforeClass: 'bootstrap-sticky-wrap',
24597     
24598     getAutoCreate : function(){
24599         
24600         var cfg = {
24601             tag: 'div',
24602             cls: 'alert alert-dismissable alert-' + this.weight,
24603             cn: [
24604                 {
24605                     tag: 'span',
24606                     cls: 'message',
24607                     html: this.html || ''
24608                 }
24609             ]
24610         };
24611         
24612         if(this.fixed){
24613             cfg.cls += ' alert-messages-fixed';
24614         }
24615         
24616         if(this.closable){
24617             cfg.cn.push({
24618                 tag: 'button',
24619                 cls: 'close',
24620                 html: 'x'
24621             });
24622         }
24623         
24624         return cfg;
24625     },
24626     
24627     onRender : function(ct, position)
24628     {
24629         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
24630         
24631         if(!this.el){
24632             var cfg = Roo.apply({},  this.getAutoCreate());
24633             cfg.id = Roo.id();
24634             
24635             if (this.cls) {
24636                 cfg.cls += ' ' + this.cls;
24637             }
24638             if (this.style) {
24639                 cfg.style = this.style;
24640             }
24641             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
24642             
24643             this.el.setVisibilityMode(Roo.Element.DISPLAY);
24644         }
24645         
24646         this.el.select('>button.close').on('click', this.hide, this);
24647         
24648     },
24649     
24650     show : function()
24651     {
24652         if (!this.rendered) {
24653             this.render();
24654         }
24655         
24656         this.el.show();
24657         
24658         this.fireEvent('show', this);
24659         
24660     },
24661     
24662     hide : function()
24663     {
24664         if (!this.rendered) {
24665             this.render();
24666         }
24667         
24668         this.el.hide();
24669         
24670         this.fireEvent('hide', this);
24671     },
24672     
24673     update : function()
24674     {
24675 //        var e = this.el.dom.firstChild;
24676 //        
24677 //        if(this.closable){
24678 //            e = e.nextSibling;
24679 //        }
24680 //        
24681 //        e.data = this.html || '';
24682
24683         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
24684     }
24685    
24686 });
24687
24688  
24689
24690      /*
24691  * - LGPL
24692  *
24693  * Graph
24694  * 
24695  */
24696
24697
24698 /**
24699  * @class Roo.bootstrap.Graph
24700  * @extends Roo.bootstrap.Component
24701  * Bootstrap Graph class
24702 > Prameters
24703  -sm {number} sm 4
24704  -md {number} md 5
24705  @cfg {String} graphtype  bar | vbar | pie
24706  @cfg {number} g_x coodinator | centre x (pie)
24707  @cfg {number} g_y coodinator | centre y (pie)
24708  @cfg {number} g_r radius (pie)
24709  @cfg {number} g_height height of the chart (respected by all elements in the set)
24710  @cfg {number} g_width width of the chart (respected by all elements in the set)
24711  @cfg {Object} title The title of the chart
24712     
24713  -{Array}  values
24714  -opts (object) options for the chart 
24715      o {
24716      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
24717      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
24718      o vgutter (number)
24719      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.
24720      o stacked (boolean) whether or not to tread values as in a stacked bar chart
24721      o to
24722      o stretch (boolean)
24723      o }
24724  -opts (object) options for the pie
24725      o{
24726      o cut
24727      o startAngle (number)
24728      o endAngle (number)
24729      } 
24730  *
24731  * @constructor
24732  * Create a new Input
24733  * @param {Object} config The config object
24734  */
24735
24736 Roo.bootstrap.Graph = function(config){
24737     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
24738     
24739     this.addEvents({
24740         // img events
24741         /**
24742          * @event click
24743          * The img click event for the img.
24744          * @param {Roo.EventObject} e
24745          */
24746         "click" : true
24747     });
24748 };
24749
24750 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
24751     
24752     sm: 4,
24753     md: 5,
24754     graphtype: 'bar',
24755     g_height: 250,
24756     g_width: 400,
24757     g_x: 50,
24758     g_y: 50,
24759     g_r: 30,
24760     opts:{
24761         //g_colors: this.colors,
24762         g_type: 'soft',
24763         g_gutter: '20%'
24764
24765     },
24766     title : false,
24767
24768     getAutoCreate : function(){
24769         
24770         var cfg = {
24771             tag: 'div',
24772             html : null
24773         };
24774         
24775         
24776         return  cfg;
24777     },
24778
24779     onRender : function(ct,position){
24780         
24781         
24782         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
24783         
24784         if (typeof(Raphael) == 'undefined') {
24785             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
24786             return;
24787         }
24788         
24789         this.raphael = Raphael(this.el.dom);
24790         
24791                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24792                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24793                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
24794                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
24795                 /*
24796                 r.text(160, 10, "Single Series Chart").attr(txtattr);
24797                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
24798                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
24799                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
24800                 
24801                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
24802                 r.barchart(330, 10, 300, 220, data1);
24803                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
24804                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
24805                 */
24806                 
24807                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24808                 // r.barchart(30, 30, 560, 250,  xdata, {
24809                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
24810                 //     axis : "0 0 1 1",
24811                 //     axisxlabels :  xdata
24812                 //     //yvalues : cols,
24813                    
24814                 // });
24815 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
24816 //        
24817 //        this.load(null,xdata,{
24818 //                axis : "0 0 1 1",
24819 //                axisxlabels :  xdata
24820 //                });
24821
24822     },
24823
24824     load : function(graphtype,xdata,opts)
24825     {
24826         this.raphael.clear();
24827         if(!graphtype) {
24828             graphtype = this.graphtype;
24829         }
24830         if(!opts){
24831             opts = this.opts;
24832         }
24833         var r = this.raphael,
24834             fin = function () {
24835                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
24836             },
24837             fout = function () {
24838                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
24839             },
24840             pfin = function() {
24841                 this.sector.stop();
24842                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
24843
24844                 if (this.label) {
24845                     this.label[0].stop();
24846                     this.label[0].attr({ r: 7.5 });
24847                     this.label[1].attr({ "font-weight": 800 });
24848                 }
24849             },
24850             pfout = function() {
24851                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
24852
24853                 if (this.label) {
24854                     this.label[0].animate({ r: 5 }, 500, "bounce");
24855                     this.label[1].attr({ "font-weight": 400 });
24856                 }
24857             };
24858
24859         switch(graphtype){
24860             case 'bar':
24861                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24862                 break;
24863             case 'hbar':
24864                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
24865                 break;
24866             case 'pie':
24867 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
24868 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
24869 //            
24870                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
24871                 
24872                 break;
24873
24874         }
24875         
24876         if(this.title){
24877             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
24878         }
24879         
24880     },
24881     
24882     setTitle: function(o)
24883     {
24884         this.title = o;
24885     },
24886     
24887     initEvents: function() {
24888         
24889         if(!this.href){
24890             this.el.on('click', this.onClick, this);
24891         }
24892     },
24893     
24894     onClick : function(e)
24895     {
24896         Roo.log('img onclick');
24897         this.fireEvent('click', this, e);
24898     }
24899    
24900 });
24901
24902  
24903 /*
24904  * - LGPL
24905  *
24906  * numberBox
24907  * 
24908  */
24909 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
24910
24911 /**
24912  * @class Roo.bootstrap.dash.NumberBox
24913  * @extends Roo.bootstrap.Component
24914  * Bootstrap NumberBox class
24915  * @cfg {String} headline Box headline
24916  * @cfg {String} content Box content
24917  * @cfg {String} icon Box icon
24918  * @cfg {String} footer Footer text
24919  * @cfg {String} fhref Footer href
24920  * 
24921  * @constructor
24922  * Create a new NumberBox
24923  * @param {Object} config The config object
24924  */
24925
24926
24927 Roo.bootstrap.dash.NumberBox = function(config){
24928     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
24929     
24930 };
24931
24932 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
24933     
24934     headline : '',
24935     content : '',
24936     icon : '',
24937     footer : '',
24938     fhref : '',
24939     ficon : '',
24940     
24941     getAutoCreate : function(){
24942         
24943         var cfg = {
24944             tag : 'div',
24945             cls : 'small-box ',
24946             cn : [
24947                 {
24948                     tag : 'div',
24949                     cls : 'inner',
24950                     cn :[
24951                         {
24952                             tag : 'h3',
24953                             cls : 'roo-headline',
24954                             html : this.headline
24955                         },
24956                         {
24957                             tag : 'p',
24958                             cls : 'roo-content',
24959                             html : this.content
24960                         }
24961                     ]
24962                 }
24963             ]
24964         };
24965         
24966         if(this.icon){
24967             cfg.cn.push({
24968                 tag : 'div',
24969                 cls : 'icon',
24970                 cn :[
24971                     {
24972                         tag : 'i',
24973                         cls : 'ion ' + this.icon
24974                     }
24975                 ]
24976             });
24977         }
24978         
24979         if(this.footer){
24980             var footer = {
24981                 tag : 'a',
24982                 cls : 'small-box-footer',
24983                 href : this.fhref || '#',
24984                 html : this.footer
24985             };
24986             
24987             cfg.cn.push(footer);
24988             
24989         }
24990         
24991         return  cfg;
24992     },
24993
24994     onRender : function(ct,position){
24995         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
24996
24997
24998        
24999                 
25000     },
25001
25002     setHeadline: function (value)
25003     {
25004         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
25005     },
25006     
25007     setFooter: function (value, href)
25008     {
25009         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
25010         
25011         if(href){
25012             this.el.select('a.small-box-footer',true).first().attr('href', href);
25013         }
25014         
25015     },
25016
25017     setContent: function (value)
25018     {
25019         this.el.select('.roo-content',true).first().dom.innerHTML = value;
25020     },
25021
25022     initEvents: function() 
25023     {   
25024         
25025     }
25026     
25027 });
25028
25029  
25030 /*
25031  * - LGPL
25032  *
25033  * TabBox
25034  * 
25035  */
25036 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25037
25038 /**
25039  * @class Roo.bootstrap.dash.TabBox
25040  * @extends Roo.bootstrap.Component
25041  * Bootstrap TabBox class
25042  * @cfg {String} title Title of the TabBox
25043  * @cfg {String} icon Icon of the TabBox
25044  * @cfg {Boolean} showtabs (true|false) show the tabs default true
25045  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
25046  * 
25047  * @constructor
25048  * Create a new TabBox
25049  * @param {Object} config The config object
25050  */
25051
25052
25053 Roo.bootstrap.dash.TabBox = function(config){
25054     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
25055     this.addEvents({
25056         // raw events
25057         /**
25058          * @event addpane
25059          * When a pane is added
25060          * @param {Roo.bootstrap.dash.TabPane} pane
25061          */
25062         "addpane" : true,
25063         /**
25064          * @event activatepane
25065          * When a pane is activated
25066          * @param {Roo.bootstrap.dash.TabPane} pane
25067          */
25068         "activatepane" : true
25069         
25070          
25071     });
25072     
25073     this.panes = [];
25074 };
25075
25076 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
25077
25078     title : '',
25079     icon : false,
25080     showtabs : true,
25081     tabScrollable : false,
25082     
25083     getChildContainer : function()
25084     {
25085         return this.el.select('.tab-content', true).first();
25086     },
25087     
25088     getAutoCreate : function(){
25089         
25090         var header = {
25091             tag: 'li',
25092             cls: 'pull-left header',
25093             html: this.title,
25094             cn : []
25095         };
25096         
25097         if(this.icon){
25098             header.cn.push({
25099                 tag: 'i',
25100                 cls: 'fa ' + this.icon
25101             });
25102         }
25103         
25104         var h = {
25105             tag: 'ul',
25106             cls: 'nav nav-tabs pull-right',
25107             cn: [
25108                 header
25109             ]
25110         };
25111         
25112         if(this.tabScrollable){
25113             h = {
25114                 tag: 'div',
25115                 cls: 'tab-header',
25116                 cn: [
25117                     {
25118                         tag: 'ul',
25119                         cls: 'nav nav-tabs pull-right',
25120                         cn: [
25121                             header
25122                         ]
25123                     }
25124                 ]
25125             };
25126         }
25127         
25128         var cfg = {
25129             tag: 'div',
25130             cls: 'nav-tabs-custom',
25131             cn: [
25132                 h,
25133                 {
25134                     tag: 'div',
25135                     cls: 'tab-content no-padding',
25136                     cn: []
25137                 }
25138             ]
25139         };
25140
25141         return  cfg;
25142     },
25143     initEvents : function()
25144     {
25145         //Roo.log('add add pane handler');
25146         this.on('addpane', this.onAddPane, this);
25147     },
25148      /**
25149      * Updates the box title
25150      * @param {String} html to set the title to.
25151      */
25152     setTitle : function(value)
25153     {
25154         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
25155     },
25156     onAddPane : function(pane)
25157     {
25158         this.panes.push(pane);
25159         //Roo.log('addpane');
25160         //Roo.log(pane);
25161         // tabs are rendere left to right..
25162         if(!this.showtabs){
25163             return;
25164         }
25165         
25166         var ctr = this.el.select('.nav-tabs', true).first();
25167          
25168          
25169         var existing = ctr.select('.nav-tab',true);
25170         var qty = existing.getCount();;
25171         
25172         
25173         var tab = ctr.createChild({
25174             tag : 'li',
25175             cls : 'nav-tab' + (qty ? '' : ' active'),
25176             cn : [
25177                 {
25178                     tag : 'a',
25179                     href:'#',
25180                     html : pane.title
25181                 }
25182             ]
25183         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
25184         pane.tab = tab;
25185         
25186         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
25187         if (!qty) {
25188             pane.el.addClass('active');
25189         }
25190         
25191                 
25192     },
25193     onTabClick : function(ev,un,ob,pane)
25194     {
25195         //Roo.log('tab - prev default');
25196         ev.preventDefault();
25197         
25198         
25199         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
25200         pane.tab.addClass('active');
25201         //Roo.log(pane.title);
25202         this.getChildContainer().select('.tab-pane',true).removeClass('active');
25203         // technically we should have a deactivate event.. but maybe add later.
25204         // and it should not de-activate the selected tab...
25205         this.fireEvent('activatepane', pane);
25206         pane.el.addClass('active');
25207         pane.fireEvent('activate');
25208         
25209         
25210     },
25211     
25212     getActivePane : function()
25213     {
25214         var r = false;
25215         Roo.each(this.panes, function(p) {
25216             if(p.el.hasClass('active')){
25217                 r = p;
25218                 return false;
25219             }
25220             
25221             return;
25222         });
25223         
25224         return r;
25225     }
25226     
25227     
25228 });
25229
25230  
25231 /*
25232  * - LGPL
25233  *
25234  * Tab pane
25235  * 
25236  */
25237 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
25238 /**
25239  * @class Roo.bootstrap.TabPane
25240  * @extends Roo.bootstrap.Component
25241  * Bootstrap TabPane class
25242  * @cfg {Boolean} active (false | true) Default false
25243  * @cfg {String} title title of panel
25244
25245  * 
25246  * @constructor
25247  * Create a new TabPane
25248  * @param {Object} config The config object
25249  */
25250
25251 Roo.bootstrap.dash.TabPane = function(config){
25252     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
25253     
25254     this.addEvents({
25255         // raw events
25256         /**
25257          * @event activate
25258          * When a pane is activated
25259          * @param {Roo.bootstrap.dash.TabPane} pane
25260          */
25261         "activate" : true
25262          
25263     });
25264 };
25265
25266 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
25267     
25268     active : false,
25269     title : '',
25270     
25271     // the tabBox that this is attached to.
25272     tab : false,
25273      
25274     getAutoCreate : function() 
25275     {
25276         var cfg = {
25277             tag: 'div',
25278             cls: 'tab-pane'
25279         };
25280         
25281         if(this.active){
25282             cfg.cls += ' active';
25283         }
25284         
25285         return cfg;
25286     },
25287     initEvents  : function()
25288     {
25289         //Roo.log('trigger add pane handler');
25290         this.parent().fireEvent('addpane', this)
25291     },
25292     
25293      /**
25294      * Updates the tab title 
25295      * @param {String} html to set the title to.
25296      */
25297     setTitle: function(str)
25298     {
25299         if (!this.tab) {
25300             return;
25301         }
25302         this.title = str;
25303         this.tab.select('a', true).first().dom.innerHTML = str;
25304         
25305     }
25306     
25307     
25308     
25309 });
25310
25311  
25312
25313
25314  /*
25315  * - LGPL
25316  *
25317  * menu
25318  * 
25319  */
25320 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25321
25322 /**
25323  * @class Roo.bootstrap.menu.Menu
25324  * @extends Roo.bootstrap.Component
25325  * Bootstrap Menu class - container for Menu
25326  * @cfg {String} html Text of the menu
25327  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
25328  * @cfg {String} icon Font awesome icon
25329  * @cfg {String} pos Menu align to (top | bottom) default bottom
25330  * 
25331  * 
25332  * @constructor
25333  * Create a new Menu
25334  * @param {Object} config The config object
25335  */
25336
25337
25338 Roo.bootstrap.menu.Menu = function(config){
25339     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
25340     
25341     this.addEvents({
25342         /**
25343          * @event beforeshow
25344          * Fires before this menu is displayed
25345          * @param {Roo.bootstrap.menu.Menu} this
25346          */
25347         beforeshow : true,
25348         /**
25349          * @event beforehide
25350          * Fires before this menu is hidden
25351          * @param {Roo.bootstrap.menu.Menu} this
25352          */
25353         beforehide : true,
25354         /**
25355          * @event show
25356          * Fires after this menu is displayed
25357          * @param {Roo.bootstrap.menu.Menu} this
25358          */
25359         show : true,
25360         /**
25361          * @event hide
25362          * Fires after this menu is hidden
25363          * @param {Roo.bootstrap.menu.Menu} this
25364          */
25365         hide : true,
25366         /**
25367          * @event click
25368          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
25369          * @param {Roo.bootstrap.menu.Menu} this
25370          * @param {Roo.EventObject} e
25371          */
25372         click : true
25373     });
25374     
25375 };
25376
25377 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
25378     
25379     submenu : false,
25380     html : '',
25381     weight : 'default',
25382     icon : false,
25383     pos : 'bottom',
25384     
25385     
25386     getChildContainer : function() {
25387         if(this.isSubMenu){
25388             return this.el;
25389         }
25390         
25391         return this.el.select('ul.dropdown-menu', true).first();  
25392     },
25393     
25394     getAutoCreate : function()
25395     {
25396         var text = [
25397             {
25398                 tag : 'span',
25399                 cls : 'roo-menu-text',
25400                 html : this.html
25401             }
25402         ];
25403         
25404         if(this.icon){
25405             text.unshift({
25406                 tag : 'i',
25407                 cls : 'fa ' + this.icon
25408             })
25409         }
25410         
25411         
25412         var cfg = {
25413             tag : 'div',
25414             cls : 'btn-group',
25415             cn : [
25416                 {
25417                     tag : 'button',
25418                     cls : 'dropdown-button btn btn-' + this.weight,
25419                     cn : text
25420                 },
25421                 {
25422                     tag : 'button',
25423                     cls : 'dropdown-toggle btn btn-' + this.weight,
25424                     cn : [
25425                         {
25426                             tag : 'span',
25427                             cls : 'caret'
25428                         }
25429                     ]
25430                 },
25431                 {
25432                     tag : 'ul',
25433                     cls : 'dropdown-menu'
25434                 }
25435             ]
25436             
25437         };
25438         
25439         if(this.pos == 'top'){
25440             cfg.cls += ' dropup';
25441         }
25442         
25443         if(this.isSubMenu){
25444             cfg = {
25445                 tag : 'ul',
25446                 cls : 'dropdown-menu'
25447             }
25448         }
25449         
25450         return cfg;
25451     },
25452     
25453     onRender : function(ct, position)
25454     {
25455         this.isSubMenu = ct.hasClass('dropdown-submenu');
25456         
25457         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
25458     },
25459     
25460     initEvents : function() 
25461     {
25462         if(this.isSubMenu){
25463             return;
25464         }
25465         
25466         this.hidden = true;
25467         
25468         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
25469         this.triggerEl.on('click', this.onTriggerPress, this);
25470         
25471         this.buttonEl = this.el.select('button.dropdown-button', true).first();
25472         this.buttonEl.on('click', this.onClick, this);
25473         
25474     },
25475     
25476     list : function()
25477     {
25478         if(this.isSubMenu){
25479             return this.el;
25480         }
25481         
25482         return this.el.select('ul.dropdown-menu', true).first();
25483     },
25484     
25485     onClick : function(e)
25486     {
25487         this.fireEvent("click", this, e);
25488     },
25489     
25490     onTriggerPress  : function(e)
25491     {   
25492         if (this.isVisible()) {
25493             this.hide();
25494         } else {
25495             this.show();
25496         }
25497     },
25498     
25499     isVisible : function(){
25500         return !this.hidden;
25501     },
25502     
25503     show : function()
25504     {
25505         this.fireEvent("beforeshow", this);
25506         
25507         this.hidden = false;
25508         this.el.addClass('open');
25509         
25510         Roo.get(document).on("mouseup", this.onMouseUp, this);
25511         
25512         this.fireEvent("show", this);
25513         
25514         
25515     },
25516     
25517     hide : function()
25518     {
25519         this.fireEvent("beforehide", this);
25520         
25521         this.hidden = true;
25522         this.el.removeClass('open');
25523         
25524         Roo.get(document).un("mouseup", this.onMouseUp);
25525         
25526         this.fireEvent("hide", this);
25527     },
25528     
25529     onMouseUp : function()
25530     {
25531         this.hide();
25532     }
25533     
25534 });
25535
25536  
25537  /*
25538  * - LGPL
25539  *
25540  * menu item
25541  * 
25542  */
25543 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25544
25545 /**
25546  * @class Roo.bootstrap.menu.Item
25547  * @extends Roo.bootstrap.Component
25548  * Bootstrap MenuItem class
25549  * @cfg {Boolean} submenu (true | false) default false
25550  * @cfg {String} html text of the item
25551  * @cfg {String} href the link
25552  * @cfg {Boolean} disable (true | false) default false
25553  * @cfg {Boolean} preventDefault (true | false) default true
25554  * @cfg {String} icon Font awesome icon
25555  * @cfg {String} pos Submenu align to (left | right) default right 
25556  * 
25557  * 
25558  * @constructor
25559  * Create a new Item
25560  * @param {Object} config The config object
25561  */
25562
25563
25564 Roo.bootstrap.menu.Item = function(config){
25565     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
25566     this.addEvents({
25567         /**
25568          * @event mouseover
25569          * Fires when the mouse is hovering over this menu
25570          * @param {Roo.bootstrap.menu.Item} this
25571          * @param {Roo.EventObject} e
25572          */
25573         mouseover : true,
25574         /**
25575          * @event mouseout
25576          * Fires when the mouse exits this menu
25577          * @param {Roo.bootstrap.menu.Item} this
25578          * @param {Roo.EventObject} e
25579          */
25580         mouseout : true,
25581         // raw events
25582         /**
25583          * @event click
25584          * The raw click event for the entire grid.
25585          * @param {Roo.EventObject} e
25586          */
25587         click : true
25588     });
25589 };
25590
25591 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
25592     
25593     submenu : false,
25594     href : '',
25595     html : '',
25596     preventDefault: true,
25597     disable : false,
25598     icon : false,
25599     pos : 'right',
25600     
25601     getAutoCreate : function()
25602     {
25603         var text = [
25604             {
25605                 tag : 'span',
25606                 cls : 'roo-menu-item-text',
25607                 html : this.html
25608             }
25609         ];
25610         
25611         if(this.icon){
25612             text.unshift({
25613                 tag : 'i',
25614                 cls : 'fa ' + this.icon
25615             })
25616         }
25617         
25618         var cfg = {
25619             tag : 'li',
25620             cn : [
25621                 {
25622                     tag : 'a',
25623                     href : this.href || '#',
25624                     cn : text
25625                 }
25626             ]
25627         };
25628         
25629         if(this.disable){
25630             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
25631         }
25632         
25633         if(this.submenu){
25634             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
25635             
25636             if(this.pos == 'left'){
25637                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
25638             }
25639         }
25640         
25641         return cfg;
25642     },
25643     
25644     initEvents : function() 
25645     {
25646         this.el.on('mouseover', this.onMouseOver, this);
25647         this.el.on('mouseout', this.onMouseOut, this);
25648         
25649         this.el.select('a', true).first().on('click', this.onClick, this);
25650         
25651     },
25652     
25653     onClick : function(e)
25654     {
25655         if(this.preventDefault){
25656             e.preventDefault();
25657         }
25658         
25659         this.fireEvent("click", this, e);
25660     },
25661     
25662     onMouseOver : function(e)
25663     {
25664         if(this.submenu && this.pos == 'left'){
25665             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
25666         }
25667         
25668         this.fireEvent("mouseover", this, e);
25669     },
25670     
25671     onMouseOut : function(e)
25672     {
25673         this.fireEvent("mouseout", this, e);
25674     }
25675 });
25676
25677  
25678
25679  /*
25680  * - LGPL
25681  *
25682  * menu separator
25683  * 
25684  */
25685 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
25686
25687 /**
25688  * @class Roo.bootstrap.menu.Separator
25689  * @extends Roo.bootstrap.Component
25690  * Bootstrap Separator class
25691  * 
25692  * @constructor
25693  * Create a new Separator
25694  * @param {Object} config The config object
25695  */
25696
25697
25698 Roo.bootstrap.menu.Separator = function(config){
25699     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
25700 };
25701
25702 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
25703     
25704     getAutoCreate : function(){
25705         var cfg = {
25706             tag : 'li',
25707             cls: 'divider'
25708         };
25709         
25710         return cfg;
25711     }
25712    
25713 });
25714
25715  
25716
25717  /*
25718  * - LGPL
25719  *
25720  * Tooltip
25721  * 
25722  */
25723
25724 /**
25725  * @class Roo.bootstrap.Tooltip
25726  * Bootstrap Tooltip class
25727  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
25728  * to determine which dom element triggers the tooltip.
25729  * 
25730  * It needs to add support for additional attributes like tooltip-position
25731  * 
25732  * @constructor
25733  * Create a new Toolti
25734  * @param {Object} config The config object
25735  */
25736
25737 Roo.bootstrap.Tooltip = function(config){
25738     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
25739     
25740     this.alignment = Roo.bootstrap.Tooltip.alignment;
25741     
25742     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
25743         this.alignment = config.alignment;
25744     }
25745     
25746 };
25747
25748 Roo.apply(Roo.bootstrap.Tooltip, {
25749     /**
25750      * @function init initialize tooltip monitoring.
25751      * @static
25752      */
25753     currentEl : false,
25754     currentTip : false,
25755     currentRegion : false,
25756     
25757     //  init : delay?
25758     
25759     init : function()
25760     {
25761         Roo.get(document).on('mouseover', this.enter ,this);
25762         Roo.get(document).on('mouseout', this.leave, this);
25763          
25764         
25765         this.currentTip = new Roo.bootstrap.Tooltip();
25766     },
25767     
25768     enter : function(ev)
25769     {
25770         var dom = ev.getTarget();
25771         
25772         //Roo.log(['enter',dom]);
25773         var el = Roo.fly(dom);
25774         if (this.currentEl) {
25775             //Roo.log(dom);
25776             //Roo.log(this.currentEl);
25777             //Roo.log(this.currentEl.contains(dom));
25778             if (this.currentEl == el) {
25779                 return;
25780             }
25781             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
25782                 return;
25783             }
25784
25785         }
25786         
25787         if (this.currentTip.el) {
25788             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
25789         }    
25790         //Roo.log(ev);
25791         
25792         if(!el || el.dom == document){
25793             return;
25794         }
25795         
25796         var bindEl = el;
25797         
25798         // you can not look for children, as if el is the body.. then everythign is the child..
25799         if (!el.attr('tooltip')) { //
25800             if (!el.select("[tooltip]").elements.length) {
25801                 return;
25802             }
25803             // is the mouse over this child...?
25804             bindEl = el.select("[tooltip]").first();
25805             var xy = ev.getXY();
25806             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
25807                 //Roo.log("not in region.");
25808                 return;
25809             }
25810             //Roo.log("child element over..");
25811             
25812         }
25813         this.currentEl = bindEl;
25814         this.currentTip.bind(bindEl);
25815         this.currentRegion = Roo.lib.Region.getRegion(dom);
25816         this.currentTip.enter();
25817         
25818     },
25819     leave : function(ev)
25820     {
25821         var dom = ev.getTarget();
25822         //Roo.log(['leave',dom]);
25823         if (!this.currentEl) {
25824             return;
25825         }
25826         
25827         
25828         if (dom != this.currentEl.dom) {
25829             return;
25830         }
25831         var xy = ev.getXY();
25832         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
25833             return;
25834         }
25835         // only activate leave if mouse cursor is outside... bounding box..
25836         
25837         
25838         
25839         
25840         if (this.currentTip) {
25841             this.currentTip.leave();
25842         }
25843         //Roo.log('clear currentEl');
25844         this.currentEl = false;
25845         
25846         
25847     },
25848     alignment : {
25849         'left' : ['r-l', [-2,0], 'right'],
25850         'right' : ['l-r', [2,0], 'left'],
25851         'bottom' : ['t-b', [0,2], 'top'],
25852         'top' : [ 'b-t', [0,-2], 'bottom']
25853     }
25854     
25855 });
25856
25857
25858 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
25859     
25860     
25861     bindEl : false,
25862     
25863     delay : null, // can be { show : 300 , hide: 500}
25864     
25865     timeout : null,
25866     
25867     hoverState : null, //???
25868     
25869     placement : 'bottom', 
25870     
25871     alignment : false,
25872     
25873     getAutoCreate : function(){
25874     
25875         var cfg = {
25876            cls : 'tooltip',
25877            role : 'tooltip',
25878            cn : [
25879                 {
25880                     cls : 'tooltip-arrow'
25881                 },
25882                 {
25883                     cls : 'tooltip-inner'
25884                 }
25885            ]
25886         };
25887         
25888         return cfg;
25889     },
25890     bind : function(el)
25891     {
25892         this.bindEl = el;
25893     },
25894       
25895     
25896     enter : function () {
25897        
25898         if (this.timeout != null) {
25899             clearTimeout(this.timeout);
25900         }
25901         
25902         this.hoverState = 'in';
25903          //Roo.log("enter - show");
25904         if (!this.delay || !this.delay.show) {
25905             this.show();
25906             return;
25907         }
25908         var _t = this;
25909         this.timeout = setTimeout(function () {
25910             if (_t.hoverState == 'in') {
25911                 _t.show();
25912             }
25913         }, this.delay.show);
25914     },
25915     leave : function()
25916     {
25917         clearTimeout(this.timeout);
25918     
25919         this.hoverState = 'out';
25920          if (!this.delay || !this.delay.hide) {
25921             this.hide();
25922             return;
25923         }
25924        
25925         var _t = this;
25926         this.timeout = setTimeout(function () {
25927             //Roo.log("leave - timeout");
25928             
25929             if (_t.hoverState == 'out') {
25930                 _t.hide();
25931                 Roo.bootstrap.Tooltip.currentEl = false;
25932             }
25933         }, delay);
25934     },
25935     
25936     show : function (msg)
25937     {
25938         if (!this.el) {
25939             this.render(document.body);
25940         }
25941         // set content.
25942         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
25943         
25944         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
25945         
25946         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
25947         
25948         this.el.removeClass(['fade','top','bottom', 'left', 'right','in']);
25949         
25950         var placement = typeof this.placement == 'function' ?
25951             this.placement.call(this, this.el, on_el) :
25952             this.placement;
25953             
25954         var autoToken = /\s?auto?\s?/i;
25955         var autoPlace = autoToken.test(placement);
25956         if (autoPlace) {
25957             placement = placement.replace(autoToken, '') || 'top';
25958         }
25959         
25960         //this.el.detach()
25961         //this.el.setXY([0,0]);
25962         this.el.show();
25963         //this.el.dom.style.display='block';
25964         
25965         //this.el.appendTo(on_el);
25966         
25967         var p = this.getPosition();
25968         var box = this.el.getBox();
25969         
25970         if (autoPlace) {
25971             // fixme..
25972         }
25973         
25974         var align = this.alignment[placement];
25975         
25976         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
25977         
25978         if(placement == 'top' || placement == 'bottom'){
25979             if(xy[0] < 0){
25980                 placement = 'right';
25981             }
25982             
25983             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
25984                 placement = 'left';
25985             }
25986             
25987             var scroll = Roo.select('body', true).first().getScroll();
25988             
25989             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
25990                 placement = 'top';
25991             }
25992             
25993         }
25994         
25995         this.el.alignTo(this.bindEl, align[0],align[1]);
25996         //var arrow = this.el.select('.arrow',true).first();
25997         //arrow.set(align[2], 
25998         
25999         this.el.addClass(placement);
26000         
26001         this.el.addClass('in fade');
26002         
26003         this.hoverState = null;
26004         
26005         if (this.el.hasClass('fade')) {
26006             // fade it?
26007         }
26008         
26009     },
26010     hide : function()
26011     {
26012          
26013         if (!this.el) {
26014             return;
26015         }
26016         //this.el.setXY([0,0]);
26017         this.el.removeClass('in');
26018         //this.el.hide();
26019         
26020     }
26021     
26022 });
26023  
26024
26025  /*
26026  * - LGPL
26027  *
26028  * Location Picker
26029  * 
26030  */
26031
26032 /**
26033  * @class Roo.bootstrap.LocationPicker
26034  * @extends Roo.bootstrap.Component
26035  * Bootstrap LocationPicker class
26036  * @cfg {Number} latitude Position when init default 0
26037  * @cfg {Number} longitude Position when init default 0
26038  * @cfg {Number} zoom default 15
26039  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
26040  * @cfg {Boolean} mapTypeControl default false
26041  * @cfg {Boolean} disableDoubleClickZoom default false
26042  * @cfg {Boolean} scrollwheel default true
26043  * @cfg {Boolean} streetViewControl default false
26044  * @cfg {Number} radius default 0
26045  * @cfg {String} locationName
26046  * @cfg {Boolean} draggable default true
26047  * @cfg {Boolean} enableAutocomplete default false
26048  * @cfg {Boolean} enableReverseGeocode default true
26049  * @cfg {String} markerTitle
26050  * 
26051  * @constructor
26052  * Create a new LocationPicker
26053  * @param {Object} config The config object
26054  */
26055
26056
26057 Roo.bootstrap.LocationPicker = function(config){
26058     
26059     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
26060     
26061     this.addEvents({
26062         /**
26063          * @event initial
26064          * Fires when the picker initialized.
26065          * @param {Roo.bootstrap.LocationPicker} this
26066          * @param {Google Location} location
26067          */
26068         initial : true,
26069         /**
26070          * @event positionchanged
26071          * Fires when the picker position changed.
26072          * @param {Roo.bootstrap.LocationPicker} this
26073          * @param {Google Location} location
26074          */
26075         positionchanged : true,
26076         /**
26077          * @event resize
26078          * Fires when the map resize.
26079          * @param {Roo.bootstrap.LocationPicker} this
26080          */
26081         resize : true,
26082         /**
26083          * @event show
26084          * Fires when the map show.
26085          * @param {Roo.bootstrap.LocationPicker} this
26086          */
26087         show : true,
26088         /**
26089          * @event hide
26090          * Fires when the map hide.
26091          * @param {Roo.bootstrap.LocationPicker} this
26092          */
26093         hide : true,
26094         /**
26095          * @event mapClick
26096          * Fires when click the map.
26097          * @param {Roo.bootstrap.LocationPicker} this
26098          * @param {Map event} e
26099          */
26100         mapClick : true,
26101         /**
26102          * @event mapRightClick
26103          * Fires when right click the map.
26104          * @param {Roo.bootstrap.LocationPicker} this
26105          * @param {Map event} e
26106          */
26107         mapRightClick : true,
26108         /**
26109          * @event markerClick
26110          * Fires when click the marker.
26111          * @param {Roo.bootstrap.LocationPicker} this
26112          * @param {Map event} e
26113          */
26114         markerClick : true,
26115         /**
26116          * @event markerRightClick
26117          * Fires when right click the marker.
26118          * @param {Roo.bootstrap.LocationPicker} this
26119          * @param {Map event} e
26120          */
26121         markerRightClick : true,
26122         /**
26123          * @event OverlayViewDraw
26124          * Fires when OverlayView Draw
26125          * @param {Roo.bootstrap.LocationPicker} this
26126          */
26127         OverlayViewDraw : true,
26128         /**
26129          * @event OverlayViewOnAdd
26130          * Fires when OverlayView Draw
26131          * @param {Roo.bootstrap.LocationPicker} this
26132          */
26133         OverlayViewOnAdd : true,
26134         /**
26135          * @event OverlayViewOnRemove
26136          * Fires when OverlayView Draw
26137          * @param {Roo.bootstrap.LocationPicker} this
26138          */
26139         OverlayViewOnRemove : true,
26140         /**
26141          * @event OverlayViewShow
26142          * Fires when OverlayView Draw
26143          * @param {Roo.bootstrap.LocationPicker} this
26144          * @param {Pixel} cpx
26145          */
26146         OverlayViewShow : true,
26147         /**
26148          * @event OverlayViewHide
26149          * Fires when OverlayView Draw
26150          * @param {Roo.bootstrap.LocationPicker} this
26151          */
26152         OverlayViewHide : true,
26153         /**
26154          * @event loadexception
26155          * Fires when load google lib failed.
26156          * @param {Roo.bootstrap.LocationPicker} this
26157          */
26158         loadexception : true
26159     });
26160         
26161 };
26162
26163 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
26164     
26165     gMapContext: false,
26166     
26167     latitude: 0,
26168     longitude: 0,
26169     zoom: 15,
26170     mapTypeId: false,
26171     mapTypeControl: false,
26172     disableDoubleClickZoom: false,
26173     scrollwheel: true,
26174     streetViewControl: false,
26175     radius: 0,
26176     locationName: '',
26177     draggable: true,
26178     enableAutocomplete: false,
26179     enableReverseGeocode: true,
26180     markerTitle: '',
26181     
26182     getAutoCreate: function()
26183     {
26184
26185         var cfg = {
26186             tag: 'div',
26187             cls: 'roo-location-picker'
26188         };
26189         
26190         return cfg
26191     },
26192     
26193     initEvents: function(ct, position)
26194     {       
26195         if(!this.el.getWidth() || this.isApplied()){
26196             return;
26197         }
26198         
26199         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26200         
26201         this.initial();
26202     },
26203     
26204     initial: function()
26205     {
26206         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
26207             this.fireEvent('loadexception', this);
26208             return;
26209         }
26210         
26211         if(!this.mapTypeId){
26212             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
26213         }
26214         
26215         this.gMapContext = this.GMapContext();
26216         
26217         this.initOverlayView();
26218         
26219         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
26220         
26221         var _this = this;
26222                 
26223         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
26224             _this.setPosition(_this.gMapContext.marker.position);
26225         });
26226         
26227         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
26228             _this.fireEvent('mapClick', this, event);
26229             
26230         });
26231
26232         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
26233             _this.fireEvent('mapRightClick', this, event);
26234             
26235         });
26236         
26237         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
26238             _this.fireEvent('markerClick', this, event);
26239             
26240         });
26241
26242         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
26243             _this.fireEvent('markerRightClick', this, event);
26244             
26245         });
26246         
26247         this.setPosition(this.gMapContext.location);
26248         
26249         this.fireEvent('initial', this, this.gMapContext.location);
26250     },
26251     
26252     initOverlayView: function()
26253     {
26254         var _this = this;
26255         
26256         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
26257             
26258             draw: function()
26259             {
26260                 _this.fireEvent('OverlayViewDraw', _this);
26261             },
26262             
26263             onAdd: function()
26264             {
26265                 _this.fireEvent('OverlayViewOnAdd', _this);
26266             },
26267             
26268             onRemove: function()
26269             {
26270                 _this.fireEvent('OverlayViewOnRemove', _this);
26271             },
26272             
26273             show: function(cpx)
26274             {
26275                 _this.fireEvent('OverlayViewShow', _this, cpx);
26276             },
26277             
26278             hide: function()
26279             {
26280                 _this.fireEvent('OverlayViewHide', _this);
26281             }
26282             
26283         });
26284     },
26285     
26286     fromLatLngToContainerPixel: function(event)
26287     {
26288         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
26289     },
26290     
26291     isApplied: function() 
26292     {
26293         return this.getGmapContext() == false ? false : true;
26294     },
26295     
26296     getGmapContext: function() 
26297     {
26298         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
26299     },
26300     
26301     GMapContext: function() 
26302     {
26303         var position = new google.maps.LatLng(this.latitude, this.longitude);
26304         
26305         var _map = new google.maps.Map(this.el.dom, {
26306             center: position,
26307             zoom: this.zoom,
26308             mapTypeId: this.mapTypeId,
26309             mapTypeControl: this.mapTypeControl,
26310             disableDoubleClickZoom: this.disableDoubleClickZoom,
26311             scrollwheel: this.scrollwheel,
26312             streetViewControl: this.streetViewControl,
26313             locationName: this.locationName,
26314             draggable: this.draggable,
26315             enableAutocomplete: this.enableAutocomplete,
26316             enableReverseGeocode: this.enableReverseGeocode
26317         });
26318         
26319         var _marker = new google.maps.Marker({
26320             position: position,
26321             map: _map,
26322             title: this.markerTitle,
26323             draggable: this.draggable
26324         });
26325         
26326         return {
26327             map: _map,
26328             marker: _marker,
26329             circle: null,
26330             location: position,
26331             radius: this.radius,
26332             locationName: this.locationName,
26333             addressComponents: {
26334                 formatted_address: null,
26335                 addressLine1: null,
26336                 addressLine2: null,
26337                 streetName: null,
26338                 streetNumber: null,
26339                 city: null,
26340                 district: null,
26341                 state: null,
26342                 stateOrProvince: null
26343             },
26344             settings: this,
26345             domContainer: this.el.dom,
26346             geodecoder: new google.maps.Geocoder()
26347         };
26348     },
26349     
26350     drawCircle: function(center, radius, options) 
26351     {
26352         if (this.gMapContext.circle != null) {
26353             this.gMapContext.circle.setMap(null);
26354         }
26355         if (radius > 0) {
26356             radius *= 1;
26357             options = Roo.apply({}, options, {
26358                 strokeColor: "#0000FF",
26359                 strokeOpacity: .35,
26360                 strokeWeight: 2,
26361                 fillColor: "#0000FF",
26362                 fillOpacity: .2
26363             });
26364             
26365             options.map = this.gMapContext.map;
26366             options.radius = radius;
26367             options.center = center;
26368             this.gMapContext.circle = new google.maps.Circle(options);
26369             return this.gMapContext.circle;
26370         }
26371         
26372         return null;
26373     },
26374     
26375     setPosition: function(location) 
26376     {
26377         this.gMapContext.location = location;
26378         this.gMapContext.marker.setPosition(location);
26379         this.gMapContext.map.panTo(location);
26380         this.drawCircle(location, this.gMapContext.radius, {});
26381         
26382         var _this = this;
26383         
26384         if (this.gMapContext.settings.enableReverseGeocode) {
26385             this.gMapContext.geodecoder.geocode({
26386                 latLng: this.gMapContext.location
26387             }, function(results, status) {
26388                 
26389                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
26390                     _this.gMapContext.locationName = results[0].formatted_address;
26391                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
26392                     
26393                     _this.fireEvent('positionchanged', this, location);
26394                 }
26395             });
26396             
26397             return;
26398         }
26399         
26400         this.fireEvent('positionchanged', this, location);
26401     },
26402     
26403     resize: function()
26404     {
26405         google.maps.event.trigger(this.gMapContext.map, "resize");
26406         
26407         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
26408         
26409         this.fireEvent('resize', this);
26410     },
26411     
26412     setPositionByLatLng: function(latitude, longitude)
26413     {
26414         this.setPosition(new google.maps.LatLng(latitude, longitude));
26415     },
26416     
26417     getCurrentPosition: function() 
26418     {
26419         return {
26420             latitude: this.gMapContext.location.lat(),
26421             longitude: this.gMapContext.location.lng()
26422         };
26423     },
26424     
26425     getAddressName: function() 
26426     {
26427         return this.gMapContext.locationName;
26428     },
26429     
26430     getAddressComponents: function() 
26431     {
26432         return this.gMapContext.addressComponents;
26433     },
26434     
26435     address_component_from_google_geocode: function(address_components) 
26436     {
26437         var result = {};
26438         
26439         for (var i = 0; i < address_components.length; i++) {
26440             var component = address_components[i];
26441             if (component.types.indexOf("postal_code") >= 0) {
26442                 result.postalCode = component.short_name;
26443             } else if (component.types.indexOf("street_number") >= 0) {
26444                 result.streetNumber = component.short_name;
26445             } else if (component.types.indexOf("route") >= 0) {
26446                 result.streetName = component.short_name;
26447             } else if (component.types.indexOf("neighborhood") >= 0) {
26448                 result.city = component.short_name;
26449             } else if (component.types.indexOf("locality") >= 0) {
26450                 result.city = component.short_name;
26451             } else if (component.types.indexOf("sublocality") >= 0) {
26452                 result.district = component.short_name;
26453             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
26454                 result.stateOrProvince = component.short_name;
26455             } else if (component.types.indexOf("country") >= 0) {
26456                 result.country = component.short_name;
26457             }
26458         }
26459         
26460         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
26461         result.addressLine2 = "";
26462         return result;
26463     },
26464     
26465     setZoomLevel: function(zoom)
26466     {
26467         this.gMapContext.map.setZoom(zoom);
26468     },
26469     
26470     show: function()
26471     {
26472         if(!this.el){
26473             return;
26474         }
26475         
26476         this.el.show();
26477         
26478         this.resize();
26479         
26480         this.fireEvent('show', this);
26481     },
26482     
26483     hide: function()
26484     {
26485         if(!this.el){
26486             return;
26487         }
26488         
26489         this.el.hide();
26490         
26491         this.fireEvent('hide', this);
26492     }
26493     
26494 });
26495
26496 Roo.apply(Roo.bootstrap.LocationPicker, {
26497     
26498     OverlayView : function(map, options)
26499     {
26500         options = options || {};
26501         
26502         this.setMap(map);
26503     }
26504     
26505     
26506 });/*
26507  * - LGPL
26508  *
26509  * Alert
26510  * 
26511  */
26512
26513 /**
26514  * @class Roo.bootstrap.Alert
26515  * @extends Roo.bootstrap.Component
26516  * Bootstrap Alert class
26517  * @cfg {String} title The title of alert
26518  * @cfg {String} html The content of alert
26519  * @cfg {String} weight (  success | info | warning | danger )
26520  * @cfg {String} faicon font-awesomeicon
26521  * 
26522  * @constructor
26523  * Create a new alert
26524  * @param {Object} config The config object
26525  */
26526
26527
26528 Roo.bootstrap.Alert = function(config){
26529     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
26530     
26531 };
26532
26533 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
26534     
26535     title: '',
26536     html: '',
26537     weight: false,
26538     faicon: false,
26539     
26540     getAutoCreate : function()
26541     {
26542         
26543         var cfg = {
26544             tag : 'div',
26545             cls : 'alert',
26546             cn : [
26547                 {
26548                     tag : 'i',
26549                     cls : 'roo-alert-icon'
26550                     
26551                 },
26552                 {
26553                     tag : 'b',
26554                     cls : 'roo-alert-title',
26555                     html : this.title
26556                 },
26557                 {
26558                     tag : 'span',
26559                     cls : 'roo-alert-text',
26560                     html : this.html
26561                 }
26562             ]
26563         };
26564         
26565         if(this.faicon){
26566             cfg.cn[0].cls += ' fa ' + this.faicon;
26567         }
26568         
26569         if(this.weight){
26570             cfg.cls += ' alert-' + this.weight;
26571         }
26572         
26573         return cfg;
26574     },
26575     
26576     initEvents: function() 
26577     {
26578         this.el.setVisibilityMode(Roo.Element.DISPLAY);
26579     },
26580     
26581     setTitle : function(str)
26582     {
26583         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
26584     },
26585     
26586     setText : function(str)
26587     {
26588         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
26589     },
26590     
26591     setWeight : function(weight)
26592     {
26593         if(this.weight){
26594             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
26595         }
26596         
26597         this.weight = weight;
26598         
26599         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
26600     },
26601     
26602     setIcon : function(icon)
26603     {
26604         if(this.faicon){
26605             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
26606         }
26607         
26608         this.faicon = icon;
26609         
26610         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
26611     },
26612     
26613     hide: function() 
26614     {
26615         this.el.hide();   
26616     },
26617     
26618     show: function() 
26619     {  
26620         this.el.show();   
26621     }
26622     
26623 });
26624
26625  
26626 /*
26627 * Licence: LGPL
26628 */
26629
26630 /**
26631  * @class Roo.bootstrap.UploadCropbox
26632  * @extends Roo.bootstrap.Component
26633  * Bootstrap UploadCropbox class
26634  * @cfg {String} emptyText show when image has been loaded
26635  * @cfg {String} rotateNotify show when image too small to rotate
26636  * @cfg {Number} errorTimeout default 3000
26637  * @cfg {Number} minWidth default 300
26638  * @cfg {Number} minHeight default 300
26639  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
26640  * @cfg {Boolean} isDocument (true|false) default false
26641  * @cfg {String} url action url
26642  * @cfg {String} paramName default 'imageUpload'
26643  * @cfg {String} method default POST
26644  * @cfg {Boolean} loadMask (true|false) default true
26645  * @cfg {Boolean} loadingText default 'Loading...'
26646  * 
26647  * @constructor
26648  * Create a new UploadCropbox
26649  * @param {Object} config The config object
26650  */
26651
26652 Roo.bootstrap.UploadCropbox = function(config){
26653     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
26654     
26655     this.addEvents({
26656         /**
26657          * @event beforeselectfile
26658          * Fire before select file
26659          * @param {Roo.bootstrap.UploadCropbox} this
26660          */
26661         "beforeselectfile" : true,
26662         /**
26663          * @event initial
26664          * Fire after initEvent
26665          * @param {Roo.bootstrap.UploadCropbox} this
26666          */
26667         "initial" : true,
26668         /**
26669          * @event crop
26670          * Fire after initEvent
26671          * @param {Roo.bootstrap.UploadCropbox} this
26672          * @param {String} data
26673          */
26674         "crop" : true,
26675         /**
26676          * @event prepare
26677          * Fire when preparing the file data
26678          * @param {Roo.bootstrap.UploadCropbox} this
26679          * @param {Object} file
26680          */
26681         "prepare" : true,
26682         /**
26683          * @event exception
26684          * Fire when get exception
26685          * @param {Roo.bootstrap.UploadCropbox} this
26686          * @param {XMLHttpRequest} xhr
26687          */
26688         "exception" : true,
26689         /**
26690          * @event beforeloadcanvas
26691          * Fire before load the canvas
26692          * @param {Roo.bootstrap.UploadCropbox} this
26693          * @param {String} src
26694          */
26695         "beforeloadcanvas" : true,
26696         /**
26697          * @event trash
26698          * Fire when trash image
26699          * @param {Roo.bootstrap.UploadCropbox} this
26700          */
26701         "trash" : true,
26702         /**
26703          * @event download
26704          * Fire when download the image
26705          * @param {Roo.bootstrap.UploadCropbox} this
26706          */
26707         "download" : true,
26708         /**
26709          * @event footerbuttonclick
26710          * Fire when footerbuttonclick
26711          * @param {Roo.bootstrap.UploadCropbox} this
26712          * @param {String} type
26713          */
26714         "footerbuttonclick" : true,
26715         /**
26716          * @event resize
26717          * Fire when resize
26718          * @param {Roo.bootstrap.UploadCropbox} this
26719          */
26720         "resize" : true,
26721         /**
26722          * @event rotate
26723          * Fire when rotate the image
26724          * @param {Roo.bootstrap.UploadCropbox} this
26725          * @param {String} pos
26726          */
26727         "rotate" : true,
26728         /**
26729          * @event inspect
26730          * Fire when inspect the file
26731          * @param {Roo.bootstrap.UploadCropbox} this
26732          * @param {Object} file
26733          */
26734         "inspect" : true,
26735         /**
26736          * @event upload
26737          * Fire when xhr upload the file
26738          * @param {Roo.bootstrap.UploadCropbox} this
26739          * @param {Object} data
26740          */
26741         "upload" : true,
26742         /**
26743          * @event arrange
26744          * Fire when arrange the file data
26745          * @param {Roo.bootstrap.UploadCropbox} this
26746          * @param {Object} formData
26747          */
26748         "arrange" : true
26749     });
26750     
26751     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
26752 };
26753
26754 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
26755     
26756     emptyText : 'Click to upload image',
26757     rotateNotify : 'Image is too small to rotate',
26758     errorTimeout : 3000,
26759     scale : 0,
26760     baseScale : 1,
26761     rotate : 0,
26762     dragable : false,
26763     pinching : false,
26764     mouseX : 0,
26765     mouseY : 0,
26766     cropData : false,
26767     minWidth : 300,
26768     minHeight : 300,
26769     file : false,
26770     exif : {},
26771     baseRotate : 1,
26772     cropType : 'image/jpeg',
26773     buttons : false,
26774     canvasLoaded : false,
26775     isDocument : false,
26776     method : 'POST',
26777     paramName : 'imageUpload',
26778     loadMask : true,
26779     loadingText : 'Loading...',
26780     maskEl : false,
26781     
26782     getAutoCreate : function()
26783     {
26784         var cfg = {
26785             tag : 'div',
26786             cls : 'roo-upload-cropbox',
26787             cn : [
26788                 {
26789                     tag : 'input',
26790                     cls : 'roo-upload-cropbox-selector',
26791                     type : 'file'
26792                 },
26793                 {
26794                     tag : 'div',
26795                     cls : 'roo-upload-cropbox-body',
26796                     style : 'cursor:pointer',
26797                     cn : [
26798                         {
26799                             tag : 'div',
26800                             cls : 'roo-upload-cropbox-preview'
26801                         },
26802                         {
26803                             tag : 'div',
26804                             cls : 'roo-upload-cropbox-thumb'
26805                         },
26806                         {
26807                             tag : 'div',
26808                             cls : 'roo-upload-cropbox-empty-notify',
26809                             html : this.emptyText
26810                         },
26811                         {
26812                             tag : 'div',
26813                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
26814                             html : this.rotateNotify
26815                         }
26816                     ]
26817                 },
26818                 {
26819                     tag : 'div',
26820                     cls : 'roo-upload-cropbox-footer',
26821                     cn : {
26822                         tag : 'div',
26823                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
26824                         cn : []
26825                     }
26826                 }
26827             ]
26828         };
26829         
26830         return cfg;
26831     },
26832     
26833     onRender : function(ct, position)
26834     {
26835         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
26836         
26837         if (this.buttons.length) {
26838             
26839             Roo.each(this.buttons, function(bb) {
26840                 
26841                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
26842                 
26843                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
26844                 
26845             }, this);
26846         }
26847         
26848         if(this.loadMask){
26849             this.maskEl = this.el;
26850         }
26851     },
26852     
26853     initEvents : function()
26854     {
26855         this.urlAPI = (window.createObjectURL && window) || 
26856                                 (window.URL && URL.revokeObjectURL && URL) || 
26857                                 (window.webkitURL && webkitURL);
26858                         
26859         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
26860         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26861         
26862         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
26863         this.selectorEl.hide();
26864         
26865         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
26866         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26867         
26868         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
26869         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26870         this.thumbEl.hide();
26871         
26872         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
26873         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26874         
26875         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
26876         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26877         this.errorEl.hide();
26878         
26879         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
26880         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
26881         this.footerEl.hide();
26882         
26883         this.setThumbBoxSize();
26884         
26885         this.bind();
26886         
26887         this.resize();
26888         
26889         this.fireEvent('initial', this);
26890     },
26891
26892     bind : function()
26893     {
26894         var _this = this;
26895         
26896         window.addEventListener("resize", function() { _this.resize(); } );
26897         
26898         this.bodyEl.on('click', this.beforeSelectFile, this);
26899         
26900         if(Roo.isTouch){
26901             this.bodyEl.on('touchstart', this.onTouchStart, this);
26902             this.bodyEl.on('touchmove', this.onTouchMove, this);
26903             this.bodyEl.on('touchend', this.onTouchEnd, this);
26904         }
26905         
26906         if(!Roo.isTouch){
26907             this.bodyEl.on('mousedown', this.onMouseDown, this);
26908             this.bodyEl.on('mousemove', this.onMouseMove, this);
26909             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
26910             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
26911             Roo.get(document).on('mouseup', this.onMouseUp, this);
26912         }
26913         
26914         this.selectorEl.on('change', this.onFileSelected, this);
26915     },
26916     
26917     reset : function()
26918     {    
26919         this.scale = 0;
26920         this.baseScale = 1;
26921         this.rotate = 0;
26922         this.baseRotate = 1;
26923         this.dragable = false;
26924         this.pinching = false;
26925         this.mouseX = 0;
26926         this.mouseY = 0;
26927         this.cropData = false;
26928         this.notifyEl.dom.innerHTML = this.emptyText;
26929         
26930         this.selectorEl.dom.value = '';
26931         
26932     },
26933     
26934     resize : function()
26935     {
26936         if(this.fireEvent('resize', this) != false){
26937             this.setThumbBoxPosition();
26938             this.setCanvasPosition();
26939         }
26940     },
26941     
26942     onFooterButtonClick : function(e, el, o, type)
26943     {
26944         switch (type) {
26945             case 'rotate-left' :
26946                 this.onRotateLeft(e);
26947                 break;
26948             case 'rotate-right' :
26949                 this.onRotateRight(e);
26950                 break;
26951             case 'picture' :
26952                 this.beforeSelectFile(e);
26953                 break;
26954             case 'trash' :
26955                 this.trash(e);
26956                 break;
26957             case 'crop' :
26958                 this.crop(e);
26959                 break;
26960             case 'download' :
26961                 this.download(e);
26962                 break;
26963             default :
26964                 break;
26965         }
26966         
26967         this.fireEvent('footerbuttonclick', this, type);
26968     },
26969     
26970     beforeSelectFile : function(e)
26971     {
26972         e.preventDefault();
26973         
26974         if(this.fireEvent('beforeselectfile', this) != false){
26975             this.selectorEl.dom.click();
26976         }
26977     },
26978     
26979     onFileSelected : function(e)
26980     {
26981         e.preventDefault();
26982         
26983         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
26984             return;
26985         }
26986         
26987         var file = this.selectorEl.dom.files[0];
26988         
26989         if(this.fireEvent('inspect', this, file) != false){
26990             this.prepare(file);
26991         }
26992         
26993     },
26994     
26995     trash : function(e)
26996     {
26997         this.fireEvent('trash', this);
26998     },
26999     
27000     download : function(e)
27001     {
27002         this.fireEvent('download', this);
27003     },
27004     
27005     loadCanvas : function(src)
27006     {   
27007         if(this.fireEvent('beforeloadcanvas', this, src) != false){
27008             
27009             this.reset();
27010             
27011             this.imageEl = document.createElement('img');
27012             
27013             var _this = this;
27014             
27015             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
27016             
27017             this.imageEl.src = src;
27018         }
27019     },
27020     
27021     onLoadCanvas : function()
27022     {   
27023         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
27024         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
27025         
27026         this.bodyEl.un('click', this.beforeSelectFile, this);
27027         
27028         this.notifyEl.hide();
27029         this.thumbEl.show();
27030         this.footerEl.show();
27031         
27032         this.baseRotateLevel();
27033         
27034         if(this.isDocument){
27035             this.setThumbBoxSize();
27036         }
27037         
27038         this.setThumbBoxPosition();
27039         
27040         this.baseScaleLevel();
27041         
27042         this.draw();
27043         
27044         this.resize();
27045         
27046         this.canvasLoaded = true;
27047         
27048         if(this.loadMask){
27049             this.maskEl.unmask();
27050         }
27051         
27052     },
27053     
27054     setCanvasPosition : function()
27055     {   
27056         if(!this.canvasEl){
27057             return;
27058         }
27059         
27060         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
27061         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
27062         
27063         this.previewEl.setLeft(pw);
27064         this.previewEl.setTop(ph);
27065         
27066     },
27067     
27068     onMouseDown : function(e)
27069     {   
27070         e.stopEvent();
27071         
27072         this.dragable = true;
27073         this.pinching = false;
27074         
27075         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
27076             this.dragable = false;
27077             return;
27078         }
27079         
27080         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27081         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27082         
27083     },
27084     
27085     onMouseMove : function(e)
27086     {   
27087         e.stopEvent();
27088         
27089         if(!this.canvasLoaded){
27090             return;
27091         }
27092         
27093         if (!this.dragable){
27094             return;
27095         }
27096         
27097         var minX = Math.ceil(this.thumbEl.getLeft(true));
27098         var minY = Math.ceil(this.thumbEl.getTop(true));
27099         
27100         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
27101         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
27102         
27103         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27104         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27105         
27106         x = x - this.mouseX;
27107         y = y - this.mouseY;
27108         
27109         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
27110         var bgY = Math.ceil(y + this.previewEl.getTop(true));
27111         
27112         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
27113         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
27114         
27115         this.previewEl.setLeft(bgX);
27116         this.previewEl.setTop(bgY);
27117         
27118         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
27119         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
27120     },
27121     
27122     onMouseUp : function(e)
27123     {   
27124         e.stopEvent();
27125         
27126         this.dragable = false;
27127     },
27128     
27129     onMouseWheel : function(e)
27130     {   
27131         e.stopEvent();
27132         
27133         this.startScale = this.scale;
27134         
27135         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
27136         
27137         if(!this.zoomable()){
27138             this.scale = this.startScale;
27139             return;
27140         }
27141         
27142         this.draw();
27143         
27144         return;
27145     },
27146     
27147     zoomable : function()
27148     {
27149         var minScale = this.thumbEl.getWidth() / this.minWidth;
27150         
27151         if(this.minWidth < this.minHeight){
27152             minScale = this.thumbEl.getHeight() / this.minHeight;
27153         }
27154         
27155         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
27156         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
27157         
27158         if(
27159                 this.isDocument &&
27160                 (this.rotate == 0 || this.rotate == 180) && 
27161                 (
27162                     width > this.imageEl.OriginWidth || 
27163                     height > this.imageEl.OriginHeight ||
27164                     (width < this.minWidth && height < this.minHeight)
27165                 )
27166         ){
27167             return false;
27168         }
27169         
27170         if(
27171                 this.isDocument &&
27172                 (this.rotate == 90 || this.rotate == 270) && 
27173                 (
27174                     width > this.imageEl.OriginWidth || 
27175                     height > this.imageEl.OriginHeight ||
27176                     (width < this.minHeight && height < this.minWidth)
27177                 )
27178         ){
27179             return false;
27180         }
27181         
27182         if(
27183                 !this.isDocument &&
27184                 (this.rotate == 0 || this.rotate == 180) && 
27185                 (
27186                     width < this.minWidth || 
27187                     width > this.imageEl.OriginWidth || 
27188                     height < this.minHeight || 
27189                     height > this.imageEl.OriginHeight
27190                 )
27191         ){
27192             return false;
27193         }
27194         
27195         if(
27196                 !this.isDocument &&
27197                 (this.rotate == 90 || this.rotate == 270) && 
27198                 (
27199                     width < this.minHeight || 
27200                     width > this.imageEl.OriginWidth || 
27201                     height < this.minWidth || 
27202                     height > this.imageEl.OriginHeight
27203                 )
27204         ){
27205             return false;
27206         }
27207         
27208         return true;
27209         
27210     },
27211     
27212     onRotateLeft : function(e)
27213     {   
27214         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27215             
27216             var minScale = this.thumbEl.getWidth() / this.minWidth;
27217             
27218             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27219             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27220             
27221             this.startScale = this.scale;
27222             
27223             while (this.getScaleLevel() < minScale){
27224             
27225                 this.scale = this.scale + 1;
27226                 
27227                 if(!this.zoomable()){
27228                     break;
27229                 }
27230                 
27231                 if(
27232                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27233                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27234                 ){
27235                     continue;
27236                 }
27237                 
27238                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27239
27240                 this.draw();
27241                 
27242                 return;
27243             }
27244             
27245             this.scale = this.startScale;
27246             
27247             this.onRotateFail();
27248             
27249             return false;
27250         }
27251         
27252         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
27253
27254         if(this.isDocument){
27255             this.setThumbBoxSize();
27256             this.setThumbBoxPosition();
27257             this.setCanvasPosition();
27258         }
27259         
27260         this.draw();
27261         
27262         this.fireEvent('rotate', this, 'left');
27263         
27264     },
27265     
27266     onRotateRight : function(e)
27267     {
27268         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
27269             
27270             var minScale = this.thumbEl.getWidth() / this.minWidth;
27271         
27272             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
27273             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
27274             
27275             this.startScale = this.scale;
27276             
27277             while (this.getScaleLevel() < minScale){
27278             
27279                 this.scale = this.scale + 1;
27280                 
27281                 if(!this.zoomable()){
27282                     break;
27283                 }
27284                 
27285                 if(
27286                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
27287                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
27288                 ){
27289                     continue;
27290                 }
27291                 
27292                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27293
27294                 this.draw();
27295                 
27296                 return;
27297             }
27298             
27299             this.scale = this.startScale;
27300             
27301             this.onRotateFail();
27302             
27303             return false;
27304         }
27305         
27306         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
27307
27308         if(this.isDocument){
27309             this.setThumbBoxSize();
27310             this.setThumbBoxPosition();
27311             this.setCanvasPosition();
27312         }
27313         
27314         this.draw();
27315         
27316         this.fireEvent('rotate', this, 'right');
27317     },
27318     
27319     onRotateFail : function()
27320     {
27321         this.errorEl.show(true);
27322         
27323         var _this = this;
27324         
27325         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
27326     },
27327     
27328     draw : function()
27329     {
27330         this.previewEl.dom.innerHTML = '';
27331         
27332         var canvasEl = document.createElement("canvas");
27333         
27334         var contextEl = canvasEl.getContext("2d");
27335         
27336         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27337         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27338         var center = this.imageEl.OriginWidth / 2;
27339         
27340         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
27341             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27342             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27343             center = this.imageEl.OriginHeight / 2;
27344         }
27345         
27346         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
27347         
27348         contextEl.translate(center, center);
27349         contextEl.rotate(this.rotate * Math.PI / 180);
27350
27351         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27352         
27353         this.canvasEl = document.createElement("canvas");
27354         
27355         this.contextEl = this.canvasEl.getContext("2d");
27356         
27357         switch (this.rotate) {
27358             case 0 :
27359                 
27360                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27361                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27362                 
27363                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27364                 
27365                 break;
27366             case 90 : 
27367                 
27368                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27369                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27370                 
27371                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27372                     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);
27373                     break;
27374                 }
27375                 
27376                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27377                 
27378                 break;
27379             case 180 :
27380                 
27381                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
27382                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
27383                 
27384                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27385                     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);
27386                     break;
27387                 }
27388                 
27389                 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);
27390                 
27391                 break;
27392             case 270 :
27393                 
27394                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
27395                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
27396         
27397                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27398                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
27399                     break;
27400                 }
27401                 
27402                 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);
27403                 
27404                 break;
27405             default : 
27406                 break;
27407         }
27408         
27409         this.previewEl.appendChild(this.canvasEl);
27410         
27411         this.setCanvasPosition();
27412     },
27413     
27414     crop : function()
27415     {
27416         if(!this.canvasLoaded){
27417             return;
27418         }
27419         
27420         var imageCanvas = document.createElement("canvas");
27421         
27422         var imageContext = imageCanvas.getContext("2d");
27423         
27424         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27425         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
27426         
27427         var center = imageCanvas.width / 2;
27428         
27429         imageContext.translate(center, center);
27430         
27431         imageContext.rotate(this.rotate * Math.PI / 180);
27432         
27433         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
27434         
27435         var canvas = document.createElement("canvas");
27436         
27437         var context = canvas.getContext("2d");
27438                 
27439         canvas.width = this.minWidth;
27440         canvas.height = this.minHeight;
27441
27442         switch (this.rotate) {
27443             case 0 :
27444                 
27445                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27446                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27447                 
27448                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27449                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27450                 
27451                 var targetWidth = this.minWidth - 2 * x;
27452                 var targetHeight = this.minHeight - 2 * y;
27453                 
27454                 var scale = 1;
27455                 
27456                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27457                     scale = targetWidth / width;
27458                 }
27459                 
27460                 if(x > 0 && y == 0){
27461                     scale = targetHeight / height;
27462                 }
27463                 
27464                 if(x > 0 && y > 0){
27465                     scale = targetWidth / width;
27466                     
27467                     if(width < height){
27468                         scale = targetHeight / height;
27469                     }
27470                 }
27471                 
27472                 context.scale(scale, scale);
27473                 
27474                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27475                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27476
27477                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27478                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27479
27480                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27481                 
27482                 break;
27483             case 90 : 
27484                 
27485                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27486                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27487                 
27488                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27489                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27490                 
27491                 var targetWidth = this.minWidth - 2 * x;
27492                 var targetHeight = this.minHeight - 2 * y;
27493                 
27494                 var scale = 1;
27495                 
27496                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27497                     scale = targetWidth / width;
27498                 }
27499                 
27500                 if(x > 0 && y == 0){
27501                     scale = targetHeight / height;
27502                 }
27503                 
27504                 if(x > 0 && y > 0){
27505                     scale = targetWidth / width;
27506                     
27507                     if(width < height){
27508                         scale = targetHeight / height;
27509                     }
27510                 }
27511                 
27512                 context.scale(scale, scale);
27513                 
27514                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27515                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27516
27517                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27518                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27519                 
27520                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27521                 
27522                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27523                 
27524                 break;
27525             case 180 :
27526                 
27527                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
27528                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
27529                 
27530                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27531                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27532                 
27533                 var targetWidth = this.minWidth - 2 * x;
27534                 var targetHeight = this.minHeight - 2 * y;
27535                 
27536                 var scale = 1;
27537                 
27538                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27539                     scale = targetWidth / width;
27540                 }
27541                 
27542                 if(x > 0 && y == 0){
27543                     scale = targetHeight / height;
27544                 }
27545                 
27546                 if(x > 0 && y > 0){
27547                     scale = targetWidth / width;
27548                     
27549                     if(width < height){
27550                         scale = targetHeight / height;
27551                     }
27552                 }
27553                 
27554                 context.scale(scale, scale);
27555                 
27556                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27557                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27558
27559                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27560                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27561
27562                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27563                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
27564                 
27565                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27566                 
27567                 break;
27568             case 270 :
27569                 
27570                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
27571                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
27572                 
27573                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
27574                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
27575                 
27576                 var targetWidth = this.minWidth - 2 * x;
27577                 var targetHeight = this.minHeight - 2 * y;
27578                 
27579                 var scale = 1;
27580                 
27581                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
27582                     scale = targetWidth / width;
27583                 }
27584                 
27585                 if(x > 0 && y == 0){
27586                     scale = targetHeight / height;
27587                 }
27588                 
27589                 if(x > 0 && y > 0){
27590                     scale = targetWidth / width;
27591                     
27592                     if(width < height){
27593                         scale = targetHeight / height;
27594                     }
27595                 }
27596                 
27597                 context.scale(scale, scale);
27598                 
27599                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
27600                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
27601
27602                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
27603                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
27604                 
27605                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
27606                 
27607                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
27608                 
27609                 break;
27610             default : 
27611                 break;
27612         }
27613         
27614         this.cropData = canvas.toDataURL(this.cropType);
27615         
27616         if(this.fireEvent('crop', this, this.cropData) !== false){
27617             this.process(this.file, this.cropData);
27618         }
27619         
27620         return;
27621         
27622     },
27623     
27624     setThumbBoxSize : function()
27625     {
27626         var width, height;
27627         
27628         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
27629             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
27630             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
27631             
27632             this.minWidth = width;
27633             this.minHeight = height;
27634             
27635             if(this.rotate == 90 || this.rotate == 270){
27636                 this.minWidth = height;
27637                 this.minHeight = width;
27638             }
27639         }
27640         
27641         height = 300;
27642         width = Math.ceil(this.minWidth * height / this.minHeight);
27643         
27644         if(this.minWidth > this.minHeight){
27645             width = 300;
27646             height = Math.ceil(this.minHeight * width / this.minWidth);
27647         }
27648         
27649         this.thumbEl.setStyle({
27650             width : width + 'px',
27651             height : height + 'px'
27652         });
27653
27654         return;
27655             
27656     },
27657     
27658     setThumbBoxPosition : function()
27659     {
27660         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
27661         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
27662         
27663         this.thumbEl.setLeft(x);
27664         this.thumbEl.setTop(y);
27665         
27666     },
27667     
27668     baseRotateLevel : function()
27669     {
27670         this.baseRotate = 1;
27671         
27672         if(
27673                 typeof(this.exif) != 'undefined' &&
27674                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
27675                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
27676         ){
27677             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
27678         }
27679         
27680         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
27681         
27682     },
27683     
27684     baseScaleLevel : function()
27685     {
27686         var width, height;
27687         
27688         if(this.isDocument){
27689             
27690             if(this.baseRotate == 6 || this.baseRotate == 8){
27691             
27692                 height = this.thumbEl.getHeight();
27693                 this.baseScale = height / this.imageEl.OriginWidth;
27694
27695                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
27696                     width = this.thumbEl.getWidth();
27697                     this.baseScale = width / this.imageEl.OriginHeight;
27698                 }
27699
27700                 return;
27701             }
27702
27703             height = this.thumbEl.getHeight();
27704             this.baseScale = height / this.imageEl.OriginHeight;
27705
27706             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
27707                 width = this.thumbEl.getWidth();
27708                 this.baseScale = width / this.imageEl.OriginWidth;
27709             }
27710
27711             return;
27712         }
27713         
27714         if(this.baseRotate == 6 || this.baseRotate == 8){
27715             
27716             width = this.thumbEl.getHeight();
27717             this.baseScale = width / this.imageEl.OriginHeight;
27718             
27719             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
27720                 height = this.thumbEl.getWidth();
27721                 this.baseScale = height / this.imageEl.OriginHeight;
27722             }
27723             
27724             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27725                 height = this.thumbEl.getWidth();
27726                 this.baseScale = height / this.imageEl.OriginHeight;
27727                 
27728                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
27729                     width = this.thumbEl.getHeight();
27730                     this.baseScale = width / this.imageEl.OriginWidth;
27731                 }
27732             }
27733             
27734             return;
27735         }
27736         
27737         width = this.thumbEl.getWidth();
27738         this.baseScale = width / this.imageEl.OriginWidth;
27739         
27740         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
27741             height = this.thumbEl.getHeight();
27742             this.baseScale = height / this.imageEl.OriginHeight;
27743         }
27744         
27745         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
27746             
27747             height = this.thumbEl.getHeight();
27748             this.baseScale = height / this.imageEl.OriginHeight;
27749             
27750             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
27751                 width = this.thumbEl.getWidth();
27752                 this.baseScale = width / this.imageEl.OriginWidth;
27753             }
27754             
27755         }
27756         
27757         return;
27758     },
27759     
27760     getScaleLevel : function()
27761     {
27762         return this.baseScale * Math.pow(1.1, this.scale);
27763     },
27764     
27765     onTouchStart : function(e)
27766     {
27767         if(!this.canvasLoaded){
27768             this.beforeSelectFile(e);
27769             return;
27770         }
27771         
27772         var touches = e.browserEvent.touches;
27773         
27774         if(!touches){
27775             return;
27776         }
27777         
27778         if(touches.length == 1){
27779             this.onMouseDown(e);
27780             return;
27781         }
27782         
27783         if(touches.length != 2){
27784             return;
27785         }
27786         
27787         var coords = [];
27788         
27789         for(var i = 0, finger; finger = touches[i]; i++){
27790             coords.push(finger.pageX, finger.pageY);
27791         }
27792         
27793         var x = Math.pow(coords[0] - coords[2], 2);
27794         var y = Math.pow(coords[1] - coords[3], 2);
27795         
27796         this.startDistance = Math.sqrt(x + y);
27797         
27798         this.startScale = this.scale;
27799         
27800         this.pinching = true;
27801         this.dragable = false;
27802         
27803     },
27804     
27805     onTouchMove : function(e)
27806     {
27807         if(!this.pinching && !this.dragable){
27808             return;
27809         }
27810         
27811         var touches = e.browserEvent.touches;
27812         
27813         if(!touches){
27814             return;
27815         }
27816         
27817         if(this.dragable){
27818             this.onMouseMove(e);
27819             return;
27820         }
27821         
27822         var coords = [];
27823         
27824         for(var i = 0, finger; finger = touches[i]; i++){
27825             coords.push(finger.pageX, finger.pageY);
27826         }
27827         
27828         var x = Math.pow(coords[0] - coords[2], 2);
27829         var y = Math.pow(coords[1] - coords[3], 2);
27830         
27831         this.endDistance = Math.sqrt(x + y);
27832         
27833         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
27834         
27835         if(!this.zoomable()){
27836             this.scale = this.startScale;
27837             return;
27838         }
27839         
27840         this.draw();
27841         
27842     },
27843     
27844     onTouchEnd : function(e)
27845     {
27846         this.pinching = false;
27847         this.dragable = false;
27848         
27849     },
27850     
27851     process : function(file, crop)
27852     {
27853         if(this.loadMask){
27854             this.maskEl.mask(this.loadingText);
27855         }
27856         
27857         this.xhr = new XMLHttpRequest();
27858         
27859         file.xhr = this.xhr;
27860
27861         this.xhr.open(this.method, this.url, true);
27862         
27863         var headers = {
27864             "Accept": "application/json",
27865             "Cache-Control": "no-cache",
27866             "X-Requested-With": "XMLHttpRequest"
27867         };
27868         
27869         for (var headerName in headers) {
27870             var headerValue = headers[headerName];
27871             if (headerValue) {
27872                 this.xhr.setRequestHeader(headerName, headerValue);
27873             }
27874         }
27875         
27876         var _this = this;
27877         
27878         this.xhr.onload = function()
27879         {
27880             _this.xhrOnLoad(_this.xhr);
27881         }
27882         
27883         this.xhr.onerror = function()
27884         {
27885             _this.xhrOnError(_this.xhr);
27886         }
27887         
27888         var formData = new FormData();
27889
27890         formData.append('returnHTML', 'NO');
27891         
27892         if(crop){
27893             formData.append('crop', crop);
27894         }
27895         
27896         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
27897             formData.append(this.paramName, file, file.name);
27898         }
27899         
27900         if(typeof(file.filename) != 'undefined'){
27901             formData.append('filename', file.filename);
27902         }
27903         
27904         if(typeof(file.mimetype) != 'undefined'){
27905             formData.append('mimetype', file.mimetype);
27906         }
27907         
27908         if(this.fireEvent('arrange', this, formData) != false){
27909             this.xhr.send(formData);
27910         };
27911     },
27912     
27913     xhrOnLoad : function(xhr)
27914     {
27915         if(this.loadMask){
27916             this.maskEl.unmask();
27917         }
27918         
27919         if (xhr.readyState !== 4) {
27920             this.fireEvent('exception', this, xhr);
27921             return;
27922         }
27923
27924         var response = Roo.decode(xhr.responseText);
27925         
27926         if(!response.success){
27927             this.fireEvent('exception', this, xhr);
27928             return;
27929         }
27930         
27931         var response = Roo.decode(xhr.responseText);
27932         
27933         this.fireEvent('upload', this, response);
27934         
27935     },
27936     
27937     xhrOnError : function()
27938     {
27939         if(this.loadMask){
27940             this.maskEl.unmask();
27941         }
27942         
27943         Roo.log('xhr on error');
27944         
27945         var response = Roo.decode(xhr.responseText);
27946           
27947         Roo.log(response);
27948         
27949     },
27950     
27951     prepare : function(file)
27952     {   
27953         if(this.loadMask){
27954             this.maskEl.mask(this.loadingText);
27955         }
27956         
27957         this.file = false;
27958         this.exif = {};
27959         
27960         if(typeof(file) === 'string'){
27961             this.loadCanvas(file);
27962             return;
27963         }
27964         
27965         if(!file || !this.urlAPI){
27966             return;
27967         }
27968         
27969         this.file = file;
27970         this.cropType = file.type;
27971         
27972         var _this = this;
27973         
27974         if(this.fireEvent('prepare', this, this.file) != false){
27975             
27976             var reader = new FileReader();
27977             
27978             reader.onload = function (e) {
27979                 if (e.target.error) {
27980                     Roo.log(e.target.error);
27981                     return;
27982                 }
27983                 
27984                 var buffer = e.target.result,
27985                     dataView = new DataView(buffer),
27986                     offset = 2,
27987                     maxOffset = dataView.byteLength - 4,
27988                     markerBytes,
27989                     markerLength;
27990                 
27991                 if (dataView.getUint16(0) === 0xffd8) {
27992                     while (offset < maxOffset) {
27993                         markerBytes = dataView.getUint16(offset);
27994                         
27995                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
27996                             markerLength = dataView.getUint16(offset + 2) + 2;
27997                             if (offset + markerLength > dataView.byteLength) {
27998                                 Roo.log('Invalid meta data: Invalid segment size.');
27999                                 break;
28000                             }
28001                             
28002                             if(markerBytes == 0xffe1){
28003                                 _this.parseExifData(
28004                                     dataView,
28005                                     offset,
28006                                     markerLength
28007                                 );
28008                             }
28009                             
28010                             offset += markerLength;
28011                             
28012                             continue;
28013                         }
28014                         
28015                         break;
28016                     }
28017                     
28018                 }
28019                 
28020                 var url = _this.urlAPI.createObjectURL(_this.file);
28021                 
28022                 _this.loadCanvas(url);
28023                 
28024                 return;
28025             }
28026             
28027             reader.readAsArrayBuffer(this.file);
28028             
28029         }
28030         
28031     },
28032     
28033     parseExifData : function(dataView, offset, length)
28034     {
28035         var tiffOffset = offset + 10,
28036             littleEndian,
28037             dirOffset;
28038     
28039         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28040             // No Exif data, might be XMP data instead
28041             return;
28042         }
28043         
28044         // Check for the ASCII code for "Exif" (0x45786966):
28045         if (dataView.getUint32(offset + 4) !== 0x45786966) {
28046             // No Exif data, might be XMP data instead
28047             return;
28048         }
28049         if (tiffOffset + 8 > dataView.byteLength) {
28050             Roo.log('Invalid Exif data: Invalid segment size.');
28051             return;
28052         }
28053         // Check for the two null bytes:
28054         if (dataView.getUint16(offset + 8) !== 0x0000) {
28055             Roo.log('Invalid Exif data: Missing byte alignment offset.');
28056             return;
28057         }
28058         // Check the byte alignment:
28059         switch (dataView.getUint16(tiffOffset)) {
28060         case 0x4949:
28061             littleEndian = true;
28062             break;
28063         case 0x4D4D:
28064             littleEndian = false;
28065             break;
28066         default:
28067             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
28068             return;
28069         }
28070         // Check for the TIFF tag marker (0x002A):
28071         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
28072             Roo.log('Invalid Exif data: Missing TIFF marker.');
28073             return;
28074         }
28075         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
28076         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
28077         
28078         this.parseExifTags(
28079             dataView,
28080             tiffOffset,
28081             tiffOffset + dirOffset,
28082             littleEndian
28083         );
28084     },
28085     
28086     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
28087     {
28088         var tagsNumber,
28089             dirEndOffset,
28090             i;
28091         if (dirOffset + 6 > dataView.byteLength) {
28092             Roo.log('Invalid Exif data: Invalid directory offset.');
28093             return;
28094         }
28095         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
28096         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
28097         if (dirEndOffset + 4 > dataView.byteLength) {
28098             Roo.log('Invalid Exif data: Invalid directory size.');
28099             return;
28100         }
28101         for (i = 0; i < tagsNumber; i += 1) {
28102             this.parseExifTag(
28103                 dataView,
28104                 tiffOffset,
28105                 dirOffset + 2 + 12 * i, // tag offset
28106                 littleEndian
28107             );
28108         }
28109         // Return the offset to the next directory:
28110         return dataView.getUint32(dirEndOffset, littleEndian);
28111     },
28112     
28113     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
28114     {
28115         var tag = dataView.getUint16(offset, littleEndian);
28116         
28117         this.exif[tag] = this.getExifValue(
28118             dataView,
28119             tiffOffset,
28120             offset,
28121             dataView.getUint16(offset + 2, littleEndian), // tag type
28122             dataView.getUint32(offset + 4, littleEndian), // tag length
28123             littleEndian
28124         );
28125     },
28126     
28127     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
28128     {
28129         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
28130             tagSize,
28131             dataOffset,
28132             values,
28133             i,
28134             str,
28135             c;
28136     
28137         if (!tagType) {
28138             Roo.log('Invalid Exif data: Invalid tag type.');
28139             return;
28140         }
28141         
28142         tagSize = tagType.size * length;
28143         // Determine if the value is contained in the dataOffset bytes,
28144         // or if the value at the dataOffset is a pointer to the actual data:
28145         dataOffset = tagSize > 4 ?
28146                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
28147         if (dataOffset + tagSize > dataView.byteLength) {
28148             Roo.log('Invalid Exif data: Invalid data offset.');
28149             return;
28150         }
28151         if (length === 1) {
28152             return tagType.getValue(dataView, dataOffset, littleEndian);
28153         }
28154         values = [];
28155         for (i = 0; i < length; i += 1) {
28156             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
28157         }
28158         
28159         if (tagType.ascii) {
28160             str = '';
28161             // Concatenate the chars:
28162             for (i = 0; i < values.length; i += 1) {
28163                 c = values[i];
28164                 // Ignore the terminating NULL byte(s):
28165                 if (c === '\u0000') {
28166                     break;
28167                 }
28168                 str += c;
28169             }
28170             return str;
28171         }
28172         return values;
28173     }
28174     
28175 });
28176
28177 Roo.apply(Roo.bootstrap.UploadCropbox, {
28178     tags : {
28179         'Orientation': 0x0112
28180     },
28181     
28182     Orientation: {
28183             1: 0, //'top-left',
28184 //            2: 'top-right',
28185             3: 180, //'bottom-right',
28186 //            4: 'bottom-left',
28187 //            5: 'left-top',
28188             6: 90, //'right-top',
28189 //            7: 'right-bottom',
28190             8: 270 //'left-bottom'
28191     },
28192     
28193     exifTagTypes : {
28194         // byte, 8-bit unsigned int:
28195         1: {
28196             getValue: function (dataView, dataOffset) {
28197                 return dataView.getUint8(dataOffset);
28198             },
28199             size: 1
28200         },
28201         // ascii, 8-bit byte:
28202         2: {
28203             getValue: function (dataView, dataOffset) {
28204                 return String.fromCharCode(dataView.getUint8(dataOffset));
28205             },
28206             size: 1,
28207             ascii: true
28208         },
28209         // short, 16 bit int:
28210         3: {
28211             getValue: function (dataView, dataOffset, littleEndian) {
28212                 return dataView.getUint16(dataOffset, littleEndian);
28213             },
28214             size: 2
28215         },
28216         // long, 32 bit int:
28217         4: {
28218             getValue: function (dataView, dataOffset, littleEndian) {
28219                 return dataView.getUint32(dataOffset, littleEndian);
28220             },
28221             size: 4
28222         },
28223         // rational = two long values, first is numerator, second is denominator:
28224         5: {
28225             getValue: function (dataView, dataOffset, littleEndian) {
28226                 return dataView.getUint32(dataOffset, littleEndian) /
28227                     dataView.getUint32(dataOffset + 4, littleEndian);
28228             },
28229             size: 8
28230         },
28231         // slong, 32 bit signed int:
28232         9: {
28233             getValue: function (dataView, dataOffset, littleEndian) {
28234                 return dataView.getInt32(dataOffset, littleEndian);
28235             },
28236             size: 4
28237         },
28238         // srational, two slongs, first is numerator, second is denominator:
28239         10: {
28240             getValue: function (dataView, dataOffset, littleEndian) {
28241                 return dataView.getInt32(dataOffset, littleEndian) /
28242                     dataView.getInt32(dataOffset + 4, littleEndian);
28243             },
28244             size: 8
28245         }
28246     },
28247     
28248     footer : {
28249         STANDARD : [
28250             {
28251                 tag : 'div',
28252                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28253                 action : 'rotate-left',
28254                 cn : [
28255                     {
28256                         tag : 'button',
28257                         cls : 'btn btn-default',
28258                         html : '<i class="fa fa-undo"></i>'
28259                     }
28260                 ]
28261             },
28262             {
28263                 tag : 'div',
28264                 cls : 'btn-group roo-upload-cropbox-picture',
28265                 action : 'picture',
28266                 cn : [
28267                     {
28268                         tag : 'button',
28269                         cls : 'btn btn-default',
28270                         html : '<i class="fa fa-picture-o"></i>'
28271                     }
28272                 ]
28273             },
28274             {
28275                 tag : 'div',
28276                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28277                 action : 'rotate-right',
28278                 cn : [
28279                     {
28280                         tag : 'button',
28281                         cls : 'btn btn-default',
28282                         html : '<i class="fa fa-repeat"></i>'
28283                     }
28284                 ]
28285             }
28286         ],
28287         DOCUMENT : [
28288             {
28289                 tag : 'div',
28290                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28291                 action : 'rotate-left',
28292                 cn : [
28293                     {
28294                         tag : 'button',
28295                         cls : 'btn btn-default',
28296                         html : '<i class="fa fa-undo"></i>'
28297                     }
28298                 ]
28299             },
28300             {
28301                 tag : 'div',
28302                 cls : 'btn-group roo-upload-cropbox-download',
28303                 action : 'download',
28304                 cn : [
28305                     {
28306                         tag : 'button',
28307                         cls : 'btn btn-default',
28308                         html : '<i class="fa fa-download"></i>'
28309                     }
28310                 ]
28311             },
28312             {
28313                 tag : 'div',
28314                 cls : 'btn-group roo-upload-cropbox-crop',
28315                 action : 'crop',
28316                 cn : [
28317                     {
28318                         tag : 'button',
28319                         cls : 'btn btn-default',
28320                         html : '<i class="fa fa-crop"></i>'
28321                     }
28322                 ]
28323             },
28324             {
28325                 tag : 'div',
28326                 cls : 'btn-group roo-upload-cropbox-trash',
28327                 action : 'trash',
28328                 cn : [
28329                     {
28330                         tag : 'button',
28331                         cls : 'btn btn-default',
28332                         html : '<i class="fa fa-trash"></i>'
28333                     }
28334                 ]
28335             },
28336             {
28337                 tag : 'div',
28338                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28339                 action : 'rotate-right',
28340                 cn : [
28341                     {
28342                         tag : 'button',
28343                         cls : 'btn btn-default',
28344                         html : '<i class="fa fa-repeat"></i>'
28345                     }
28346                 ]
28347             }
28348         ],
28349         ROTATOR : [
28350             {
28351                 tag : 'div',
28352                 cls : 'btn-group roo-upload-cropbox-rotate-left',
28353                 action : 'rotate-left',
28354                 cn : [
28355                     {
28356                         tag : 'button',
28357                         cls : 'btn btn-default',
28358                         html : '<i class="fa fa-undo"></i>'
28359                     }
28360                 ]
28361             },
28362             {
28363                 tag : 'div',
28364                 cls : 'btn-group roo-upload-cropbox-rotate-right',
28365                 action : 'rotate-right',
28366                 cn : [
28367                     {
28368                         tag : 'button',
28369                         cls : 'btn btn-default',
28370                         html : '<i class="fa fa-repeat"></i>'
28371                     }
28372                 ]
28373             }
28374         ]
28375     }
28376 });
28377
28378 /*
28379 * Licence: LGPL
28380 */
28381
28382 /**
28383  * @class Roo.bootstrap.DocumentManager
28384  * @extends Roo.bootstrap.Component
28385  * Bootstrap DocumentManager class
28386  * @cfg {String} paramName default 'imageUpload'
28387  * @cfg {String} toolTipName default 'filename'
28388  * @cfg {String} method default POST
28389  * @cfg {String} url action url
28390  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
28391  * @cfg {Boolean} multiple multiple upload default true
28392  * @cfg {Number} thumbSize default 300
28393  * @cfg {String} fieldLabel
28394  * @cfg {Number} labelWidth default 4
28395  * @cfg {String} labelAlign (left|top) default left
28396  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
28397 * @cfg {Number} labellg set the width of label (1-12)
28398  * @cfg {Number} labelmd set the width of label (1-12)
28399  * @cfg {Number} labelsm set the width of label (1-12)
28400  * @cfg {Number} labelxs set the width of label (1-12)
28401  * 
28402  * @constructor
28403  * Create a new DocumentManager
28404  * @param {Object} config The config object
28405  */
28406
28407 Roo.bootstrap.DocumentManager = function(config){
28408     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
28409     
28410     this.files = [];
28411     this.delegates = [];
28412     
28413     this.addEvents({
28414         /**
28415          * @event initial
28416          * Fire when initial the DocumentManager
28417          * @param {Roo.bootstrap.DocumentManager} this
28418          */
28419         "initial" : true,
28420         /**
28421          * @event inspect
28422          * inspect selected file
28423          * @param {Roo.bootstrap.DocumentManager} this
28424          * @param {File} file
28425          */
28426         "inspect" : true,
28427         /**
28428          * @event exception
28429          * Fire when xhr load exception
28430          * @param {Roo.bootstrap.DocumentManager} this
28431          * @param {XMLHttpRequest} xhr
28432          */
28433         "exception" : true,
28434         /**
28435          * @event afterupload
28436          * Fire when xhr load exception
28437          * @param {Roo.bootstrap.DocumentManager} this
28438          * @param {XMLHttpRequest} xhr
28439          */
28440         "afterupload" : true,
28441         /**
28442          * @event prepare
28443          * prepare the form data
28444          * @param {Roo.bootstrap.DocumentManager} this
28445          * @param {Object} formData
28446          */
28447         "prepare" : true,
28448         /**
28449          * @event remove
28450          * Fire when remove the file
28451          * @param {Roo.bootstrap.DocumentManager} this
28452          * @param {Object} file
28453          */
28454         "remove" : true,
28455         /**
28456          * @event refresh
28457          * Fire after refresh the file
28458          * @param {Roo.bootstrap.DocumentManager} this
28459          */
28460         "refresh" : true,
28461         /**
28462          * @event click
28463          * Fire after click the image
28464          * @param {Roo.bootstrap.DocumentManager} this
28465          * @param {Object} file
28466          */
28467         "click" : true,
28468         /**
28469          * @event edit
28470          * Fire when upload a image and editable set to true
28471          * @param {Roo.bootstrap.DocumentManager} this
28472          * @param {Object} file
28473          */
28474         "edit" : true,
28475         /**
28476          * @event beforeselectfile
28477          * Fire before select file
28478          * @param {Roo.bootstrap.DocumentManager} this
28479          */
28480         "beforeselectfile" : true,
28481         /**
28482          * @event process
28483          * Fire before process file
28484          * @param {Roo.bootstrap.DocumentManager} this
28485          * @param {Object} file
28486          */
28487         "process" : true,
28488         /**
28489          * @event previewrendered
28490          * Fire when preview rendered
28491          * @param {Roo.bootstrap.DocumentManager} this
28492          * @param {Object} file
28493          */
28494         "previewrendered" : true
28495         
28496     });
28497 };
28498
28499 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
28500     
28501     boxes : 0,
28502     inputName : '',
28503     thumbSize : 300,
28504     multiple : true,
28505     files : false,
28506     method : 'POST',
28507     url : '',
28508     paramName : 'imageUpload',
28509     toolTipName : 'filename',
28510     fieldLabel : '',
28511     labelWidth : 4,
28512     labelAlign : 'left',
28513     editable : true,
28514     delegates : false,
28515     xhr : false, 
28516     
28517     labellg : 0,
28518     labelmd : 0,
28519     labelsm : 0,
28520     labelxs : 0,
28521     
28522     getAutoCreate : function()
28523     {   
28524         var managerWidget = {
28525             tag : 'div',
28526             cls : 'roo-document-manager',
28527             cn : [
28528                 {
28529                     tag : 'input',
28530                     cls : 'roo-document-manager-selector',
28531                     type : 'file'
28532                 },
28533                 {
28534                     tag : 'div',
28535                     cls : 'roo-document-manager-uploader',
28536                     cn : [
28537                         {
28538                             tag : 'div',
28539                             cls : 'roo-document-manager-upload-btn',
28540                             html : '<i class="fa fa-plus"></i>'
28541                         }
28542                     ]
28543                     
28544                 }
28545             ]
28546         };
28547         
28548         var content = [
28549             {
28550                 tag : 'div',
28551                 cls : 'column col-md-12',
28552                 cn : managerWidget
28553             }
28554         ];
28555         
28556         if(this.fieldLabel.length){
28557             
28558             content = [
28559                 {
28560                     tag : 'div',
28561                     cls : 'column col-md-12',
28562                     html : this.fieldLabel
28563                 },
28564                 {
28565                     tag : 'div',
28566                     cls : 'column col-md-12',
28567                     cn : managerWidget
28568                 }
28569             ];
28570
28571             if(this.labelAlign == 'left'){
28572                 content = [
28573                     {
28574                         tag : 'div',
28575                         cls : 'column',
28576                         html : this.fieldLabel
28577                     },
28578                     {
28579                         tag : 'div',
28580                         cls : 'column',
28581                         cn : managerWidget
28582                     }
28583                 ];
28584                 
28585                 if(this.labelWidth > 12){
28586                     content[0].style = "width: " + this.labelWidth + 'px';
28587                 }
28588
28589                 if(this.labelWidth < 13 && this.labelmd == 0){
28590                     this.labelmd = this.labelWidth;
28591                 }
28592
28593                 if(this.labellg > 0){
28594                     content[0].cls += ' col-lg-' + this.labellg;
28595                     content[1].cls += ' col-lg-' + (12 - this.labellg);
28596                 }
28597
28598                 if(this.labelmd > 0){
28599                     content[0].cls += ' col-md-' + this.labelmd;
28600                     content[1].cls += ' col-md-' + (12 - this.labelmd);
28601                 }
28602
28603                 if(this.labelsm > 0){
28604                     content[0].cls += ' col-sm-' + this.labelsm;
28605                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
28606                 }
28607
28608                 if(this.labelxs > 0){
28609                     content[0].cls += ' col-xs-' + this.labelxs;
28610                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
28611                 }
28612                 
28613             }
28614         }
28615         
28616         var cfg = {
28617             tag : 'div',
28618             cls : 'row clearfix',
28619             cn : content
28620         };
28621         
28622         return cfg;
28623         
28624     },
28625     
28626     initEvents : function()
28627     {
28628         this.managerEl = this.el.select('.roo-document-manager', true).first();
28629         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28630         
28631         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
28632         this.selectorEl.hide();
28633         
28634         if(this.multiple){
28635             this.selectorEl.attr('multiple', 'multiple');
28636         }
28637         
28638         this.selectorEl.on('change', this.onFileSelected, this);
28639         
28640         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
28641         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28642         
28643         this.uploader.on('click', this.onUploaderClick, this);
28644         
28645         this.renderProgressDialog();
28646         
28647         var _this = this;
28648         
28649         window.addEventListener("resize", function() { _this.refresh(); } );
28650         
28651         this.fireEvent('initial', this);
28652     },
28653     
28654     renderProgressDialog : function()
28655     {
28656         var _this = this;
28657         
28658         this.progressDialog = new Roo.bootstrap.Modal({
28659             cls : 'roo-document-manager-progress-dialog',
28660             allow_close : false,
28661             title : '',
28662             buttons : [
28663                 {
28664                     name  :'cancel',
28665                     weight : 'danger',
28666                     html : 'Cancel'
28667                 }
28668             ], 
28669             listeners : { 
28670                 btnclick : function() {
28671                     _this.uploadCancel();
28672                     this.hide();
28673                 }
28674             }
28675         });
28676          
28677         this.progressDialog.render(Roo.get(document.body));
28678          
28679         this.progress = new Roo.bootstrap.Progress({
28680             cls : 'roo-document-manager-progress',
28681             active : true,
28682             striped : true
28683         });
28684         
28685         this.progress.render(this.progressDialog.getChildContainer());
28686         
28687         this.progressBar = new Roo.bootstrap.ProgressBar({
28688             cls : 'roo-document-manager-progress-bar',
28689             aria_valuenow : 0,
28690             aria_valuemin : 0,
28691             aria_valuemax : 12,
28692             panel : 'success'
28693         });
28694         
28695         this.progressBar.render(this.progress.getChildContainer());
28696     },
28697     
28698     onUploaderClick : function(e)
28699     {
28700         e.preventDefault();
28701      
28702         if(this.fireEvent('beforeselectfile', this) != false){
28703             this.selectorEl.dom.click();
28704         }
28705         
28706     },
28707     
28708     onFileSelected : function(e)
28709     {
28710         e.preventDefault();
28711         
28712         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
28713             return;
28714         }
28715         
28716         Roo.each(this.selectorEl.dom.files, function(file){
28717             if(this.fireEvent('inspect', this, file) != false){
28718                 this.files.push(file);
28719             }
28720         }, this);
28721         
28722         this.queue();
28723         
28724     },
28725     
28726     queue : function()
28727     {
28728         this.selectorEl.dom.value = '';
28729         
28730         if(!this.files || !this.files.length){
28731             return;
28732         }
28733         
28734         if(this.boxes > 0 && this.files.length > this.boxes){
28735             this.files = this.files.slice(0, this.boxes);
28736         }
28737         
28738         this.uploader.show();
28739         
28740         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28741             this.uploader.hide();
28742         }
28743         
28744         var _this = this;
28745         
28746         var files = [];
28747         
28748         var docs = [];
28749         
28750         Roo.each(this.files, function(file){
28751             
28752             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28753                 var f = this.renderPreview(file);
28754                 files.push(f);
28755                 return;
28756             }
28757             
28758             if(file.type.indexOf('image') != -1){
28759                 this.delegates.push(
28760                     (function(){
28761                         _this.process(file);
28762                     }).createDelegate(this)
28763                 );
28764         
28765                 return;
28766             }
28767             
28768             docs.push(
28769                 (function(){
28770                     _this.process(file);
28771                 }).createDelegate(this)
28772             );
28773             
28774         }, this);
28775         
28776         this.files = files;
28777         
28778         this.delegates = this.delegates.concat(docs);
28779         
28780         if(!this.delegates.length){
28781             this.refresh();
28782             return;
28783         }
28784         
28785         this.progressBar.aria_valuemax = this.delegates.length;
28786         
28787         this.arrange();
28788         
28789         return;
28790     },
28791     
28792     arrange : function()
28793     {
28794         if(!this.delegates.length){
28795             this.progressDialog.hide();
28796             this.refresh();
28797             return;
28798         }
28799         
28800         var delegate = this.delegates.shift();
28801         
28802         this.progressDialog.show();
28803         
28804         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
28805         
28806         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
28807         
28808         delegate();
28809     },
28810     
28811     refresh : function()
28812     {
28813         this.uploader.show();
28814         
28815         if(this.boxes > 0 && this.files.length > this.boxes - 1){
28816             this.uploader.hide();
28817         }
28818         
28819         Roo.isTouch ? this.closable(false) : this.closable(true);
28820         
28821         this.fireEvent('refresh', this);
28822     },
28823     
28824     onRemove : function(e, el, o)
28825     {
28826         e.preventDefault();
28827         
28828         this.fireEvent('remove', this, o);
28829         
28830     },
28831     
28832     remove : function(o)
28833     {
28834         var files = [];
28835         
28836         Roo.each(this.files, function(file){
28837             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
28838                 files.push(file);
28839                 return;
28840             }
28841
28842             o.target.remove();
28843
28844         }, this);
28845         
28846         this.files = files;
28847         
28848         this.refresh();
28849     },
28850     
28851     clear : function()
28852     {
28853         Roo.each(this.files, function(file){
28854             if(!file.target){
28855                 return;
28856             }
28857             
28858             file.target.remove();
28859
28860         }, this);
28861         
28862         this.files = [];
28863         
28864         this.refresh();
28865     },
28866     
28867     onClick : function(e, el, o)
28868     {
28869         e.preventDefault();
28870         
28871         this.fireEvent('click', this, o);
28872         
28873     },
28874     
28875     closable : function(closable)
28876     {
28877         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
28878             
28879             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
28880             
28881             if(closable){
28882                 el.show();
28883                 return;
28884             }
28885             
28886             el.hide();
28887             
28888         }, this);
28889     },
28890     
28891     xhrOnLoad : function(xhr)
28892     {
28893         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
28894             el.remove();
28895         }, this);
28896         
28897         if (xhr.readyState !== 4) {
28898             this.arrange();
28899             this.fireEvent('exception', this, xhr);
28900             return;
28901         }
28902
28903         var response = Roo.decode(xhr.responseText);
28904         
28905         if(!response.success){
28906             this.arrange();
28907             this.fireEvent('exception', this, xhr);
28908             return;
28909         }
28910         
28911         var file = this.renderPreview(response.data);
28912         
28913         this.files.push(file);
28914         
28915         this.arrange();
28916         
28917         this.fireEvent('afterupload', this, xhr);
28918         
28919     },
28920     
28921     xhrOnError : function(xhr)
28922     {
28923         Roo.log('xhr on error');
28924         
28925         var response = Roo.decode(xhr.responseText);
28926           
28927         Roo.log(response);
28928         
28929         this.arrange();
28930     },
28931     
28932     process : function(file)
28933     {
28934         if(this.fireEvent('process', this, file) !== false){
28935             if(this.editable && file.type.indexOf('image') != -1){
28936                 this.fireEvent('edit', this, file);
28937                 return;
28938             }
28939
28940             this.uploadStart(file, false);
28941
28942             return;
28943         }
28944         
28945     },
28946     
28947     uploadStart : function(file, crop)
28948     {
28949         this.xhr = new XMLHttpRequest();
28950         
28951         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
28952             this.arrange();
28953             return;
28954         }
28955         
28956         file.xhr = this.xhr;
28957             
28958         this.managerEl.createChild({
28959             tag : 'div',
28960             cls : 'roo-document-manager-loading',
28961             cn : [
28962                 {
28963                     tag : 'div',
28964                     tooltip : file.name,
28965                     cls : 'roo-document-manager-thumb',
28966                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
28967                 }
28968             ]
28969
28970         });
28971
28972         this.xhr.open(this.method, this.url, true);
28973         
28974         var headers = {
28975             "Accept": "application/json",
28976             "Cache-Control": "no-cache",
28977             "X-Requested-With": "XMLHttpRequest"
28978         };
28979         
28980         for (var headerName in headers) {
28981             var headerValue = headers[headerName];
28982             if (headerValue) {
28983                 this.xhr.setRequestHeader(headerName, headerValue);
28984             }
28985         }
28986         
28987         var _this = this;
28988         
28989         this.xhr.onload = function()
28990         {
28991             _this.xhrOnLoad(_this.xhr);
28992         }
28993         
28994         this.xhr.onerror = function()
28995         {
28996             _this.xhrOnError(_this.xhr);
28997         }
28998         
28999         var formData = new FormData();
29000
29001         formData.append('returnHTML', 'NO');
29002         
29003         if(crop){
29004             formData.append('crop', crop);
29005         }
29006         
29007         formData.append(this.paramName, file, file.name);
29008         
29009         var options = {
29010             file : file, 
29011             manually : false
29012         };
29013         
29014         if(this.fireEvent('prepare', this, formData, options) != false){
29015             
29016             if(options.manually){
29017                 return;
29018             }
29019             
29020             this.xhr.send(formData);
29021             return;
29022         };
29023         
29024         this.uploadCancel();
29025     },
29026     
29027     uploadCancel : function()
29028     {
29029         if (this.xhr) {
29030             this.xhr.abort();
29031         }
29032         
29033         this.delegates = [];
29034         
29035         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
29036             el.remove();
29037         }, this);
29038         
29039         this.arrange();
29040     },
29041     
29042     renderPreview : function(file)
29043     {
29044         if(typeof(file.target) != 'undefined' && file.target){
29045             return file;
29046         }
29047         
29048         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
29049         
29050         var previewEl = this.managerEl.createChild({
29051             tag : 'div',
29052             cls : 'roo-document-manager-preview',
29053             cn : [
29054                 {
29055                     tag : 'div',
29056                     tooltip : file[this.toolTipName],
29057                     cls : 'roo-document-manager-thumb',
29058                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
29059                 },
29060                 {
29061                     tag : 'button',
29062                     cls : 'close',
29063                     html : '<i class="fa fa-times-circle"></i>'
29064                 }
29065             ]
29066         });
29067
29068         var close = previewEl.select('button.close', true).first();
29069
29070         close.on('click', this.onRemove, this, file);
29071
29072         file.target = previewEl;
29073
29074         var image = previewEl.select('img', true).first();
29075         
29076         var _this = this;
29077         
29078         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
29079         
29080         image.on('click', this.onClick, this, file);
29081         
29082         this.fireEvent('previewrendered', this, file);
29083         
29084         return file;
29085         
29086     },
29087     
29088     onPreviewLoad : function(file, image)
29089     {
29090         if(typeof(file.target) == 'undefined' || !file.target){
29091             return;
29092         }
29093         
29094         var width = image.dom.naturalWidth || image.dom.width;
29095         var height = image.dom.naturalHeight || image.dom.height;
29096         
29097         if(width > height){
29098             file.target.addClass('wide');
29099             return;
29100         }
29101         
29102         file.target.addClass('tall');
29103         return;
29104         
29105     },
29106     
29107     uploadFromSource : function(file, crop)
29108     {
29109         this.xhr = new XMLHttpRequest();
29110         
29111         this.managerEl.createChild({
29112             tag : 'div',
29113             cls : 'roo-document-manager-loading',
29114             cn : [
29115                 {
29116                     tag : 'div',
29117                     tooltip : file.name,
29118                     cls : 'roo-document-manager-thumb',
29119                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
29120                 }
29121             ]
29122
29123         });
29124
29125         this.xhr.open(this.method, this.url, true);
29126         
29127         var headers = {
29128             "Accept": "application/json",
29129             "Cache-Control": "no-cache",
29130             "X-Requested-With": "XMLHttpRequest"
29131         };
29132         
29133         for (var headerName in headers) {
29134             var headerValue = headers[headerName];
29135             if (headerValue) {
29136                 this.xhr.setRequestHeader(headerName, headerValue);
29137             }
29138         }
29139         
29140         var _this = this;
29141         
29142         this.xhr.onload = function()
29143         {
29144             _this.xhrOnLoad(_this.xhr);
29145         }
29146         
29147         this.xhr.onerror = function()
29148         {
29149             _this.xhrOnError(_this.xhr);
29150         }
29151         
29152         var formData = new FormData();
29153
29154         formData.append('returnHTML', 'NO');
29155         
29156         formData.append('crop', crop);
29157         
29158         if(typeof(file.filename) != 'undefined'){
29159             formData.append('filename', file.filename);
29160         }
29161         
29162         if(typeof(file.mimetype) != 'undefined'){
29163             formData.append('mimetype', file.mimetype);
29164         }
29165         
29166         Roo.log(formData);
29167         
29168         if(this.fireEvent('prepare', this, formData) != false){
29169             this.xhr.send(formData);
29170         };
29171     }
29172 });
29173
29174 /*
29175 * Licence: LGPL
29176 */
29177
29178 /**
29179  * @class Roo.bootstrap.DocumentViewer
29180  * @extends Roo.bootstrap.Component
29181  * Bootstrap DocumentViewer class
29182  * @cfg {Boolean} showDownload (true|false) show download button (default true)
29183  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
29184  * 
29185  * @constructor
29186  * Create a new DocumentViewer
29187  * @param {Object} config The config object
29188  */
29189
29190 Roo.bootstrap.DocumentViewer = function(config){
29191     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
29192     
29193     this.addEvents({
29194         /**
29195          * @event initial
29196          * Fire after initEvent
29197          * @param {Roo.bootstrap.DocumentViewer} this
29198          */
29199         "initial" : true,
29200         /**
29201          * @event click
29202          * Fire after click
29203          * @param {Roo.bootstrap.DocumentViewer} this
29204          */
29205         "click" : true,
29206         /**
29207          * @event download
29208          * Fire after download button
29209          * @param {Roo.bootstrap.DocumentViewer} this
29210          */
29211         "download" : true,
29212         /**
29213          * @event trash
29214          * Fire after trash button
29215          * @param {Roo.bootstrap.DocumentViewer} this
29216          */
29217         "trash" : true
29218         
29219     });
29220 };
29221
29222 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
29223     
29224     showDownload : true,
29225     
29226     showTrash : true,
29227     
29228     getAutoCreate : function()
29229     {
29230         var cfg = {
29231             tag : 'div',
29232             cls : 'roo-document-viewer',
29233             cn : [
29234                 {
29235                     tag : 'div',
29236                     cls : 'roo-document-viewer-body',
29237                     cn : [
29238                         {
29239                             tag : 'div',
29240                             cls : 'roo-document-viewer-thumb',
29241                             cn : [
29242                                 {
29243                                     tag : 'img',
29244                                     cls : 'roo-document-viewer-image'
29245                                 }
29246                             ]
29247                         }
29248                     ]
29249                 },
29250                 {
29251                     tag : 'div',
29252                     cls : 'roo-document-viewer-footer',
29253                     cn : {
29254                         tag : 'div',
29255                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
29256                         cn : [
29257                             {
29258                                 tag : 'div',
29259                                 cls : 'btn-group roo-document-viewer-download',
29260                                 cn : [
29261                                     {
29262                                         tag : 'button',
29263                                         cls : 'btn btn-default',
29264                                         html : '<i class="fa fa-download"></i>'
29265                                     }
29266                                 ]
29267                             },
29268                             {
29269                                 tag : 'div',
29270                                 cls : 'btn-group roo-document-viewer-trash',
29271                                 cn : [
29272                                     {
29273                                         tag : 'button',
29274                                         cls : 'btn btn-default',
29275                                         html : '<i class="fa fa-trash"></i>'
29276                                     }
29277                                 ]
29278                             }
29279                         ]
29280                     }
29281                 }
29282             ]
29283         };
29284         
29285         return cfg;
29286     },
29287     
29288     initEvents : function()
29289     {
29290         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
29291         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
29292         
29293         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
29294         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
29295         
29296         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
29297         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
29298         
29299         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
29300         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
29301         
29302         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
29303         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
29304         
29305         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
29306         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
29307         
29308         this.bodyEl.on('click', this.onClick, this);
29309         this.downloadBtn.on('click', this.onDownload, this);
29310         this.trashBtn.on('click', this.onTrash, this);
29311         
29312         this.downloadBtn.hide();
29313         this.trashBtn.hide();
29314         
29315         if(this.showDownload){
29316             this.downloadBtn.show();
29317         }
29318         
29319         if(this.showTrash){
29320             this.trashBtn.show();
29321         }
29322         
29323         if(!this.showDownload && !this.showTrash) {
29324             this.footerEl.hide();
29325         }
29326         
29327     },
29328     
29329     initial : function()
29330     {
29331         this.fireEvent('initial', this);
29332         
29333     },
29334     
29335     onClick : function(e)
29336     {
29337         e.preventDefault();
29338         
29339         this.fireEvent('click', this);
29340     },
29341     
29342     onDownload : function(e)
29343     {
29344         e.preventDefault();
29345         
29346         this.fireEvent('download', this);
29347     },
29348     
29349     onTrash : function(e)
29350     {
29351         e.preventDefault();
29352         
29353         this.fireEvent('trash', this);
29354     }
29355     
29356 });
29357 /*
29358  * - LGPL
29359  *
29360  * nav progress bar
29361  * 
29362  */
29363
29364 /**
29365  * @class Roo.bootstrap.NavProgressBar
29366  * @extends Roo.bootstrap.Component
29367  * Bootstrap NavProgressBar class
29368  * 
29369  * @constructor
29370  * Create a new nav progress bar
29371  * @param {Object} config The config object
29372  */
29373
29374 Roo.bootstrap.NavProgressBar = function(config){
29375     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
29376
29377     this.bullets = this.bullets || [];
29378    
29379 //    Roo.bootstrap.NavProgressBar.register(this);
29380      this.addEvents({
29381         /**
29382              * @event changed
29383              * Fires when the active item changes
29384              * @param {Roo.bootstrap.NavProgressBar} this
29385              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
29386              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
29387          */
29388         'changed': true
29389      });
29390     
29391 };
29392
29393 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
29394     
29395     bullets : [],
29396     barItems : [],
29397     
29398     getAutoCreate : function()
29399     {
29400         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
29401         
29402         cfg = {
29403             tag : 'div',
29404             cls : 'roo-navigation-bar-group',
29405             cn : [
29406                 {
29407                     tag : 'div',
29408                     cls : 'roo-navigation-top-bar'
29409                 },
29410                 {
29411                     tag : 'div',
29412                     cls : 'roo-navigation-bullets-bar',
29413                     cn : [
29414                         {
29415                             tag : 'ul',
29416                             cls : 'roo-navigation-bar'
29417                         }
29418                     ]
29419                 },
29420                 
29421                 {
29422                     tag : 'div',
29423                     cls : 'roo-navigation-bottom-bar'
29424                 }
29425             ]
29426             
29427         };
29428         
29429         return cfg;
29430         
29431     },
29432     
29433     initEvents: function() 
29434     {
29435         
29436     },
29437     
29438     onRender : function(ct, position) 
29439     {
29440         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
29441         
29442         if(this.bullets.length){
29443             Roo.each(this.bullets, function(b){
29444                this.addItem(b);
29445             }, this);
29446         }
29447         
29448         this.format();
29449         
29450     },
29451     
29452     addItem : function(cfg)
29453     {
29454         var item = new Roo.bootstrap.NavProgressItem(cfg);
29455         
29456         item.parentId = this.id;
29457         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
29458         
29459         if(cfg.html){
29460             var top = new Roo.bootstrap.Element({
29461                 tag : 'div',
29462                 cls : 'roo-navigation-bar-text'
29463             });
29464             
29465             var bottom = new Roo.bootstrap.Element({
29466                 tag : 'div',
29467                 cls : 'roo-navigation-bar-text'
29468             });
29469             
29470             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
29471             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
29472             
29473             var topText = new Roo.bootstrap.Element({
29474                 tag : 'span',
29475                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
29476             });
29477             
29478             var bottomText = new Roo.bootstrap.Element({
29479                 tag : 'span',
29480                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
29481             });
29482             
29483             topText.onRender(top.el, null);
29484             bottomText.onRender(bottom.el, null);
29485             
29486             item.topEl = top;
29487             item.bottomEl = bottom;
29488         }
29489         
29490         this.barItems.push(item);
29491         
29492         return item;
29493     },
29494     
29495     getActive : function()
29496     {
29497         var active = false;
29498         
29499         Roo.each(this.barItems, function(v){
29500             
29501             if (!v.isActive()) {
29502                 return;
29503             }
29504             
29505             active = v;
29506             return false;
29507             
29508         });
29509         
29510         return active;
29511     },
29512     
29513     setActiveItem : function(item)
29514     {
29515         var prev = false;
29516         
29517         Roo.each(this.barItems, function(v){
29518             if (v.rid == item.rid) {
29519                 return ;
29520             }
29521             
29522             if (v.isActive()) {
29523                 v.setActive(false);
29524                 prev = v;
29525             }
29526         });
29527
29528         item.setActive(true);
29529         
29530         this.fireEvent('changed', this, item, prev);
29531     },
29532     
29533     getBarItem: function(rid)
29534     {
29535         var ret = false;
29536         
29537         Roo.each(this.barItems, function(e) {
29538             if (e.rid != rid) {
29539                 return;
29540             }
29541             
29542             ret =  e;
29543             return false;
29544         });
29545         
29546         return ret;
29547     },
29548     
29549     indexOfItem : function(item)
29550     {
29551         var index = false;
29552         
29553         Roo.each(this.barItems, function(v, i){
29554             
29555             if (v.rid != item.rid) {
29556                 return;
29557             }
29558             
29559             index = i;
29560             return false
29561         });
29562         
29563         return index;
29564     },
29565     
29566     setActiveNext : function()
29567     {
29568         var i = this.indexOfItem(this.getActive());
29569         
29570         if (i > this.barItems.length) {
29571             return;
29572         }
29573         
29574         this.setActiveItem(this.barItems[i+1]);
29575     },
29576     
29577     setActivePrev : function()
29578     {
29579         var i = this.indexOfItem(this.getActive());
29580         
29581         if (i  < 1) {
29582             return;
29583         }
29584         
29585         this.setActiveItem(this.barItems[i-1]);
29586     },
29587     
29588     format : function()
29589     {
29590         if(!this.barItems.length){
29591             return;
29592         }
29593      
29594         var width = 100 / this.barItems.length;
29595         
29596         Roo.each(this.barItems, function(i){
29597             i.el.setStyle('width', width + '%');
29598             i.topEl.el.setStyle('width', width + '%');
29599             i.bottomEl.el.setStyle('width', width + '%');
29600         }, this);
29601         
29602     }
29603     
29604 });
29605 /*
29606  * - LGPL
29607  *
29608  * Nav Progress Item
29609  * 
29610  */
29611
29612 /**
29613  * @class Roo.bootstrap.NavProgressItem
29614  * @extends Roo.bootstrap.Component
29615  * Bootstrap NavProgressItem class
29616  * @cfg {String} rid the reference id
29617  * @cfg {Boolean} active (true|false) Is item active default false
29618  * @cfg {Boolean} disabled (true|false) Is item active default false
29619  * @cfg {String} html
29620  * @cfg {String} position (top|bottom) text position default bottom
29621  * @cfg {String} icon show icon instead of number
29622  * 
29623  * @constructor
29624  * Create a new NavProgressItem
29625  * @param {Object} config The config object
29626  */
29627 Roo.bootstrap.NavProgressItem = function(config){
29628     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
29629     this.addEvents({
29630         // raw events
29631         /**
29632          * @event click
29633          * The raw click event for the entire grid.
29634          * @param {Roo.bootstrap.NavProgressItem} this
29635          * @param {Roo.EventObject} e
29636          */
29637         "click" : true
29638     });
29639    
29640 };
29641
29642 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
29643     
29644     rid : '',
29645     active : false,
29646     disabled : false,
29647     html : '',
29648     position : 'bottom',
29649     icon : false,
29650     
29651     getAutoCreate : function()
29652     {
29653         var iconCls = 'roo-navigation-bar-item-icon';
29654         
29655         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
29656         
29657         var cfg = {
29658             tag: 'li',
29659             cls: 'roo-navigation-bar-item',
29660             cn : [
29661                 {
29662                     tag : 'i',
29663                     cls : iconCls
29664                 }
29665             ]
29666         };
29667         
29668         if(this.active){
29669             cfg.cls += ' active';
29670         }
29671         if(this.disabled){
29672             cfg.cls += ' disabled';
29673         }
29674         
29675         return cfg;
29676     },
29677     
29678     disable : function()
29679     {
29680         this.setDisabled(true);
29681     },
29682     
29683     enable : function()
29684     {
29685         this.setDisabled(false);
29686     },
29687     
29688     initEvents: function() 
29689     {
29690         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
29691         
29692         this.iconEl.on('click', this.onClick, this);
29693     },
29694     
29695     onClick : function(e)
29696     {
29697         e.preventDefault();
29698         
29699         if(this.disabled){
29700             return;
29701         }
29702         
29703         if(this.fireEvent('click', this, e) === false){
29704             return;
29705         };
29706         
29707         this.parent().setActiveItem(this);
29708     },
29709     
29710     isActive: function () 
29711     {
29712         return this.active;
29713     },
29714     
29715     setActive : function(state)
29716     {
29717         if(this.active == state){
29718             return;
29719         }
29720         
29721         this.active = state;
29722         
29723         if (state) {
29724             this.el.addClass('active');
29725             return;
29726         }
29727         
29728         this.el.removeClass('active');
29729         
29730         return;
29731     },
29732     
29733     setDisabled : function(state)
29734     {
29735         if(this.disabled == state){
29736             return;
29737         }
29738         
29739         this.disabled = state;
29740         
29741         if (state) {
29742             this.el.addClass('disabled');
29743             return;
29744         }
29745         
29746         this.el.removeClass('disabled');
29747     },
29748     
29749     tooltipEl : function()
29750     {
29751         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
29752     }
29753 });
29754  
29755
29756  /*
29757  * - LGPL
29758  *
29759  * FieldLabel
29760  * 
29761  */
29762
29763 /**
29764  * @class Roo.bootstrap.FieldLabel
29765  * @extends Roo.bootstrap.Component
29766  * Bootstrap FieldLabel class
29767  * @cfg {String} html contents of the element
29768  * @cfg {String} tag tag of the element default label
29769  * @cfg {String} cls class of the element
29770  * @cfg {String} target label target 
29771  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
29772  * @cfg {String} invalidClass default "text-warning"
29773  * @cfg {String} validClass default "text-success"
29774  * @cfg {String} iconTooltip default "This field is required"
29775  * @cfg {String} indicatorpos (left|right) default left
29776  * 
29777  * @constructor
29778  * Create a new FieldLabel
29779  * @param {Object} config The config object
29780  */
29781
29782 Roo.bootstrap.FieldLabel = function(config){
29783     Roo.bootstrap.Element.superclass.constructor.call(this, config);
29784     
29785     this.addEvents({
29786             /**
29787              * @event invalid
29788              * Fires after the field has been marked as invalid.
29789              * @param {Roo.form.FieldLabel} this
29790              * @param {String} msg The validation message
29791              */
29792             invalid : true,
29793             /**
29794              * @event valid
29795              * Fires after the field has been validated with no errors.
29796              * @param {Roo.form.FieldLabel} this
29797              */
29798             valid : true
29799         });
29800 };
29801
29802 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
29803     
29804     tag: 'label',
29805     cls: '',
29806     html: '',
29807     target: '',
29808     allowBlank : true,
29809     invalidClass : 'has-warning',
29810     validClass : 'has-success',
29811     iconTooltip : 'This field is required',
29812     indicatorpos : 'left',
29813     
29814     getAutoCreate : function(){
29815         
29816         var cfg = {
29817             tag : this.tag,
29818             cls : 'roo-bootstrap-field-label ' + this.cls,
29819             for : this.target,
29820             cn : [
29821                 {
29822                     tag : 'i',
29823                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
29824                     tooltip : this.iconTooltip
29825                 },
29826                 {
29827                     tag : 'span',
29828                     html : this.html
29829                 }
29830             ] 
29831         };
29832         
29833         if(this.indicatorpos == 'right'){
29834             var cfg = {
29835                 tag : this.tag,
29836                 cls : 'roo-bootstrap-field-label ' + this.cls,
29837                 for : this.target,
29838                 cn : [
29839                     {
29840                         tag : 'span',
29841                         html : this.html
29842                     },
29843                     {
29844                         tag : 'i',
29845                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
29846                         tooltip : this.iconTooltip
29847                     }
29848                 ] 
29849             };
29850         }
29851         
29852         return cfg;
29853     },
29854     
29855     initEvents: function() 
29856     {
29857         Roo.bootstrap.Element.superclass.initEvents.call(this);
29858         
29859         this.indicator = this.indicatorEl();
29860         
29861         if(this.indicator){
29862             this.indicator.removeClass('visible');
29863             this.indicator.addClass('invisible');
29864         }
29865         
29866         Roo.bootstrap.FieldLabel.register(this);
29867     },
29868     
29869     indicatorEl : function()
29870     {
29871         var indicator = this.el.select('i.roo-required-indicator',true).first();
29872         
29873         if(!indicator){
29874             return false;
29875         }
29876         
29877         return indicator;
29878         
29879     },
29880     
29881     /**
29882      * Mark this field as valid
29883      */
29884     markValid : function()
29885     {
29886         if(this.indicator){
29887             this.indicator.removeClass('visible');
29888             this.indicator.addClass('invisible');
29889         }
29890         
29891         this.el.removeClass(this.invalidClass);
29892         
29893         this.el.addClass(this.validClass);
29894         
29895         this.fireEvent('valid', this);
29896     },
29897     
29898     /**
29899      * Mark this field as invalid
29900      * @param {String} msg The validation message
29901      */
29902     markInvalid : function(msg)
29903     {
29904         if(this.indicator){
29905             this.indicator.removeClass('invisible');
29906             this.indicator.addClass('visible');
29907         }
29908         
29909         this.el.removeClass(this.validClass);
29910         
29911         this.el.addClass(this.invalidClass);
29912         
29913         this.fireEvent('invalid', this, msg);
29914     }
29915     
29916    
29917 });
29918
29919 Roo.apply(Roo.bootstrap.FieldLabel, {
29920     
29921     groups: {},
29922     
29923      /**
29924     * register a FieldLabel Group
29925     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
29926     */
29927     register : function(label)
29928     {
29929         if(this.groups.hasOwnProperty(label.target)){
29930             return;
29931         }
29932      
29933         this.groups[label.target] = label;
29934         
29935     },
29936     /**
29937     * fetch a FieldLabel Group based on the target
29938     * @param {string} target
29939     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
29940     */
29941     get: function(target) {
29942         if (typeof(this.groups[target]) == 'undefined') {
29943             return false;
29944         }
29945         
29946         return this.groups[target] ;
29947     }
29948 });
29949
29950  
29951
29952  /*
29953  * - LGPL
29954  *
29955  * page DateSplitField.
29956  * 
29957  */
29958
29959
29960 /**
29961  * @class Roo.bootstrap.DateSplitField
29962  * @extends Roo.bootstrap.Component
29963  * Bootstrap DateSplitField class
29964  * @cfg {string} fieldLabel - the label associated
29965  * @cfg {Number} labelWidth set the width of label (0-12)
29966  * @cfg {String} labelAlign (top|left)
29967  * @cfg {Boolean} dayAllowBlank (true|false) default false
29968  * @cfg {Boolean} monthAllowBlank (true|false) default false
29969  * @cfg {Boolean} yearAllowBlank (true|false) default false
29970  * @cfg {string} dayPlaceholder 
29971  * @cfg {string} monthPlaceholder
29972  * @cfg {string} yearPlaceholder
29973  * @cfg {string} dayFormat default 'd'
29974  * @cfg {string} monthFormat default 'm'
29975  * @cfg {string} yearFormat default 'Y'
29976  * @cfg {Number} labellg set the width of label (1-12)
29977  * @cfg {Number} labelmd set the width of label (1-12)
29978  * @cfg {Number} labelsm set the width of label (1-12)
29979  * @cfg {Number} labelxs set the width of label (1-12)
29980
29981  *     
29982  * @constructor
29983  * Create a new DateSplitField
29984  * @param {Object} config The config object
29985  */
29986
29987 Roo.bootstrap.DateSplitField = function(config){
29988     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
29989     
29990     this.addEvents({
29991         // raw events
29992          /**
29993          * @event years
29994          * getting the data of years
29995          * @param {Roo.bootstrap.DateSplitField} this
29996          * @param {Object} years
29997          */
29998         "years" : true,
29999         /**
30000          * @event days
30001          * getting the data of days
30002          * @param {Roo.bootstrap.DateSplitField} this
30003          * @param {Object} days
30004          */
30005         "days" : true,
30006         /**
30007          * @event invalid
30008          * Fires after the field has been marked as invalid.
30009          * @param {Roo.form.Field} this
30010          * @param {String} msg The validation message
30011          */
30012         invalid : true,
30013        /**
30014          * @event valid
30015          * Fires after the field has been validated with no errors.
30016          * @param {Roo.form.Field} this
30017          */
30018         valid : true
30019     });
30020 };
30021
30022 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
30023     
30024     fieldLabel : '',
30025     labelAlign : 'top',
30026     labelWidth : 3,
30027     dayAllowBlank : false,
30028     monthAllowBlank : false,
30029     yearAllowBlank : false,
30030     dayPlaceholder : '',
30031     monthPlaceholder : '',
30032     yearPlaceholder : '',
30033     dayFormat : 'd',
30034     monthFormat : 'm',
30035     yearFormat : 'Y',
30036     isFormField : true,
30037     labellg : 0,
30038     labelmd : 0,
30039     labelsm : 0,
30040     labelxs : 0,
30041     
30042     getAutoCreate : function()
30043     {
30044         var cfg = {
30045             tag : 'div',
30046             cls : 'row roo-date-split-field-group',
30047             cn : [
30048                 {
30049                     tag : 'input',
30050                     type : 'hidden',
30051                     cls : 'form-hidden-field roo-date-split-field-group-value',
30052                     name : this.name
30053                 }
30054             ]
30055         };
30056         
30057         var labelCls = 'col-md-12';
30058         var contentCls = 'col-md-4';
30059         
30060         if(this.fieldLabel){
30061             
30062             var label = {
30063                 tag : 'div',
30064                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
30065                 cn : [
30066                     {
30067                         tag : 'label',
30068                         html : this.fieldLabel
30069                     }
30070                 ]
30071             };
30072             
30073             if(this.labelAlign == 'left'){
30074             
30075                 if(this.labelWidth > 12){
30076                     label.style = "width: " + this.labelWidth + 'px';
30077                 }
30078
30079                 if(this.labelWidth < 13 && this.labelmd == 0){
30080                     this.labelmd = this.labelWidth;
30081                 }
30082
30083                 if(this.labellg > 0){
30084                     labelCls = ' col-lg-' + this.labellg;
30085                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
30086                 }
30087
30088                 if(this.labelmd > 0){
30089                     labelCls = ' col-md-' + this.labelmd;
30090                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
30091                 }
30092
30093                 if(this.labelsm > 0){
30094                     labelCls = ' col-sm-' + this.labelsm;
30095                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
30096                 }
30097
30098                 if(this.labelxs > 0){
30099                     labelCls = ' col-xs-' + this.labelxs;
30100                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
30101                 }
30102             }
30103             
30104             label.cls += ' ' + labelCls;
30105             
30106             cfg.cn.push(label);
30107         }
30108         
30109         Roo.each(['day', 'month', 'year'], function(t){
30110             cfg.cn.push({
30111                 tag : 'div',
30112                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
30113             });
30114         }, this);
30115         
30116         return cfg;
30117     },
30118     
30119     inputEl: function ()
30120     {
30121         return this.el.select('.roo-date-split-field-group-value', true).first();
30122     },
30123     
30124     onRender : function(ct, position) 
30125     {
30126         var _this = this;
30127         
30128         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
30129         
30130         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
30131         
30132         this.dayField = new Roo.bootstrap.ComboBox({
30133             allowBlank : this.dayAllowBlank,
30134             alwaysQuery : true,
30135             displayField : 'value',
30136             editable : false,
30137             fieldLabel : '',
30138             forceSelection : true,
30139             mode : 'local',
30140             placeholder : this.dayPlaceholder,
30141             selectOnFocus : true,
30142             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30143             triggerAction : 'all',
30144             typeAhead : true,
30145             valueField : 'value',
30146             store : new Roo.data.SimpleStore({
30147                 data : (function() {    
30148                     var days = [];
30149                     _this.fireEvent('days', _this, days);
30150                     return days;
30151                 })(),
30152                 fields : [ 'value' ]
30153             }),
30154             listeners : {
30155                 select : function (_self, record, index)
30156                 {
30157                     _this.setValue(_this.getValue());
30158                 }
30159             }
30160         });
30161
30162         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
30163         
30164         this.monthField = new Roo.bootstrap.MonthField({
30165             after : '<i class=\"fa fa-calendar\"></i>',
30166             allowBlank : this.monthAllowBlank,
30167             placeholder : this.monthPlaceholder,
30168             readOnly : true,
30169             listeners : {
30170                 render : function (_self)
30171                 {
30172                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
30173                         e.preventDefault();
30174                         _self.focus();
30175                     });
30176                 },
30177                 select : function (_self, oldvalue, newvalue)
30178                 {
30179                     _this.setValue(_this.getValue());
30180                 }
30181             }
30182         });
30183         
30184         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
30185         
30186         this.yearField = new Roo.bootstrap.ComboBox({
30187             allowBlank : this.yearAllowBlank,
30188             alwaysQuery : true,
30189             displayField : 'value',
30190             editable : false,
30191             fieldLabel : '',
30192             forceSelection : true,
30193             mode : 'local',
30194             placeholder : this.yearPlaceholder,
30195             selectOnFocus : true,
30196             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
30197             triggerAction : 'all',
30198             typeAhead : true,
30199             valueField : 'value',
30200             store : new Roo.data.SimpleStore({
30201                 data : (function() {
30202                     var years = [];
30203                     _this.fireEvent('years', _this, years);
30204                     return years;
30205                 })(),
30206                 fields : [ 'value' ]
30207             }),
30208             listeners : {
30209                 select : function (_self, record, index)
30210                 {
30211                     _this.setValue(_this.getValue());
30212                 }
30213             }
30214         });
30215
30216         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
30217     },
30218     
30219     setValue : function(v, format)
30220     {
30221         this.inputEl.dom.value = v;
30222         
30223         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
30224         
30225         var d = Date.parseDate(v, f);
30226         
30227         if(!d){
30228             this.validate();
30229             return;
30230         }
30231         
30232         this.setDay(d.format(this.dayFormat));
30233         this.setMonth(d.format(this.monthFormat));
30234         this.setYear(d.format(this.yearFormat));
30235         
30236         this.validate();
30237         
30238         return;
30239     },
30240     
30241     setDay : function(v)
30242     {
30243         this.dayField.setValue(v);
30244         this.inputEl.dom.value = this.getValue();
30245         this.validate();
30246         return;
30247     },
30248     
30249     setMonth : function(v)
30250     {
30251         this.monthField.setValue(v, true);
30252         this.inputEl.dom.value = this.getValue();
30253         this.validate();
30254         return;
30255     },
30256     
30257     setYear : function(v)
30258     {
30259         this.yearField.setValue(v);
30260         this.inputEl.dom.value = this.getValue();
30261         this.validate();
30262         return;
30263     },
30264     
30265     getDay : function()
30266     {
30267         return this.dayField.getValue();
30268     },
30269     
30270     getMonth : function()
30271     {
30272         return this.monthField.getValue();
30273     },
30274     
30275     getYear : function()
30276     {
30277         return this.yearField.getValue();
30278     },
30279     
30280     getValue : function()
30281     {
30282         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
30283         
30284         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
30285         
30286         return date;
30287     },
30288     
30289     reset : function()
30290     {
30291         this.setDay('');
30292         this.setMonth('');
30293         this.setYear('');
30294         this.inputEl.dom.value = '';
30295         this.validate();
30296         return;
30297     },
30298     
30299     validate : function()
30300     {
30301         var d = this.dayField.validate();
30302         var m = this.monthField.validate();
30303         var y = this.yearField.validate();
30304         
30305         var valid = true;
30306         
30307         if(
30308                 (!this.dayAllowBlank && !d) ||
30309                 (!this.monthAllowBlank && !m) ||
30310                 (!this.yearAllowBlank && !y)
30311         ){
30312             valid = false;
30313         }
30314         
30315         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
30316             return valid;
30317         }
30318         
30319         if(valid){
30320             this.markValid();
30321             return valid;
30322         }
30323         
30324         this.markInvalid();
30325         
30326         return valid;
30327     },
30328     
30329     markValid : function()
30330     {
30331         
30332         var label = this.el.select('label', true).first();
30333         var icon = this.el.select('i.fa-star', true).first();
30334
30335         if(label && icon){
30336             icon.remove();
30337         }
30338         
30339         this.fireEvent('valid', this);
30340     },
30341     
30342      /**
30343      * Mark this field as invalid
30344      * @param {String} msg The validation message
30345      */
30346     markInvalid : function(msg)
30347     {
30348         
30349         var label = this.el.select('label', true).first();
30350         var icon = this.el.select('i.fa-star', true).first();
30351
30352         if(label && !icon){
30353             this.el.select('.roo-date-split-field-label', true).createChild({
30354                 tag : 'i',
30355                 cls : 'text-danger fa fa-lg fa-star',
30356                 tooltip : 'This field is required',
30357                 style : 'margin-right:5px;'
30358             }, label, true);
30359         }
30360         
30361         this.fireEvent('invalid', this, msg);
30362     },
30363     
30364     clearInvalid : function()
30365     {
30366         var label = this.el.select('label', true).first();
30367         var icon = this.el.select('i.fa-star', true).first();
30368
30369         if(label && icon){
30370             icon.remove();
30371         }
30372         
30373         this.fireEvent('valid', this);
30374     },
30375     
30376     getName: function()
30377     {
30378         return this.name;
30379     }
30380     
30381 });
30382
30383  /**
30384  *
30385  * This is based on 
30386  * http://masonry.desandro.com
30387  *
30388  * The idea is to render all the bricks based on vertical width...
30389  *
30390  * The original code extends 'outlayer' - we might need to use that....
30391  * 
30392  */
30393
30394
30395 /**
30396  * @class Roo.bootstrap.LayoutMasonry
30397  * @extends Roo.bootstrap.Component
30398  * Bootstrap Layout Masonry class
30399  * 
30400  * @constructor
30401  * Create a new Element
30402  * @param {Object} config The config object
30403  */
30404
30405 Roo.bootstrap.LayoutMasonry = function(config){
30406     
30407     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
30408     
30409     this.bricks = [];
30410     
30411     Roo.bootstrap.LayoutMasonry.register(this);
30412     
30413     this.addEvents({
30414         // raw events
30415         /**
30416          * @event layout
30417          * Fire after layout the items
30418          * @param {Roo.bootstrap.LayoutMasonry} this
30419          * @param {Roo.EventObject} e
30420          */
30421         "layout" : true
30422     });
30423     
30424 };
30425
30426 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
30427     
30428     /**
30429      * @cfg {Boolean} isLayoutInstant = no animation?
30430      */   
30431     isLayoutInstant : false, // needed?
30432    
30433     /**
30434      * @cfg {Number} boxWidth  width of the columns
30435      */   
30436     boxWidth : 450,
30437     
30438       /**
30439      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
30440      */   
30441     boxHeight : 0,
30442     
30443     /**
30444      * @cfg {Number} padWidth padding below box..
30445      */   
30446     padWidth : 10, 
30447     
30448     /**
30449      * @cfg {Number} gutter gutter width..
30450      */   
30451     gutter : 10,
30452     
30453      /**
30454      * @cfg {Number} maxCols maximum number of columns
30455      */   
30456     
30457     maxCols: 0,
30458     
30459     /**
30460      * @cfg {Boolean} isAutoInitial defalut true
30461      */   
30462     isAutoInitial : true, 
30463     
30464     containerWidth: 0,
30465     
30466     /**
30467      * @cfg {Boolean} isHorizontal defalut false
30468      */   
30469     isHorizontal : false, 
30470
30471     currentSize : null,
30472     
30473     tag: 'div',
30474     
30475     cls: '',
30476     
30477     bricks: null, //CompositeElement
30478     
30479     cols : 1,
30480     
30481     _isLayoutInited : false,
30482     
30483 //    isAlternative : false, // only use for vertical layout...
30484     
30485     /**
30486      * @cfg {Number} alternativePadWidth padding below box..
30487      */   
30488     alternativePadWidth : 50,
30489     
30490     selectedBrick : [],
30491     
30492     getAutoCreate : function(){
30493         
30494         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
30495         
30496         var cfg = {
30497             tag: this.tag,
30498             cls: 'blog-masonary-wrapper ' + this.cls,
30499             cn : {
30500                 cls : 'mas-boxes masonary'
30501             }
30502         };
30503         
30504         return cfg;
30505     },
30506     
30507     getChildContainer: function( )
30508     {
30509         if (this.boxesEl) {
30510             return this.boxesEl;
30511         }
30512         
30513         this.boxesEl = this.el.select('.mas-boxes').first();
30514         
30515         return this.boxesEl;
30516     },
30517     
30518     
30519     initEvents : function()
30520     {
30521         var _this = this;
30522         
30523         if(this.isAutoInitial){
30524             Roo.log('hook children rendered');
30525             this.on('childrenrendered', function() {
30526                 Roo.log('children rendered');
30527                 _this.initial();
30528             } ,this);
30529         }
30530     },
30531     
30532     initial : function()
30533     {
30534         this.selectedBrick = [];
30535         
30536         this.currentSize = this.el.getBox(true);
30537         
30538         Roo.EventManager.onWindowResize(this.resize, this); 
30539
30540         if(!this.isAutoInitial){
30541             this.layout();
30542             return;
30543         }
30544         
30545         this.layout();
30546         
30547         return;
30548         //this.layout.defer(500,this);
30549         
30550     },
30551     
30552     resize : function()
30553     {
30554         var cs = this.el.getBox(true);
30555         
30556         if (
30557                 this.currentSize.width == cs.width && 
30558                 this.currentSize.x == cs.x && 
30559                 this.currentSize.height == cs.height && 
30560                 this.currentSize.y == cs.y 
30561         ) {
30562             Roo.log("no change in with or X or Y");
30563             return;
30564         }
30565         
30566         this.currentSize = cs;
30567         
30568         this.layout();
30569         
30570     },
30571     
30572     layout : function()
30573     {   
30574         this._resetLayout();
30575         
30576         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
30577         
30578         this.layoutItems( isInstant );
30579       
30580         this._isLayoutInited = true;
30581         
30582         this.fireEvent('layout', this);
30583         
30584     },
30585     
30586     _resetLayout : function()
30587     {
30588         if(this.isHorizontal){
30589             this.horizontalMeasureColumns();
30590             return;
30591         }
30592         
30593         this.verticalMeasureColumns();
30594         
30595     },
30596     
30597     verticalMeasureColumns : function()
30598     {
30599         this.getContainerWidth();
30600         
30601 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30602 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
30603 //            return;
30604 //        }
30605         
30606         var boxWidth = this.boxWidth + this.padWidth;
30607         
30608         if(this.containerWidth < this.boxWidth){
30609             boxWidth = this.containerWidth
30610         }
30611         
30612         var containerWidth = this.containerWidth;
30613         
30614         var cols = Math.floor(containerWidth / boxWidth);
30615         
30616         this.cols = Math.max( cols, 1 );
30617         
30618         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
30619         
30620         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
30621         
30622         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
30623         
30624         this.colWidth = boxWidth + avail - this.padWidth;
30625         
30626         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
30627         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
30628     },
30629     
30630     horizontalMeasureColumns : function()
30631     {
30632         this.getContainerWidth();
30633         
30634         var boxWidth = this.boxWidth;
30635         
30636         if(this.containerWidth < boxWidth){
30637             boxWidth = this.containerWidth;
30638         }
30639         
30640         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
30641         
30642         this.el.setHeight(boxWidth);
30643         
30644     },
30645     
30646     getContainerWidth : function()
30647     {
30648         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
30649     },
30650     
30651     layoutItems : function( isInstant )
30652     {
30653         Roo.log(this.bricks);
30654         
30655         var items = Roo.apply([], this.bricks);
30656         
30657         if(this.isHorizontal){
30658             this._horizontalLayoutItems( items , isInstant );
30659             return;
30660         }
30661         
30662 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
30663 //            this._verticalAlternativeLayoutItems( items , isInstant );
30664 //            return;
30665 //        }
30666         
30667         this._verticalLayoutItems( items , isInstant );
30668         
30669     },
30670     
30671     _verticalLayoutItems : function ( items , isInstant)
30672     {
30673         if ( !items || !items.length ) {
30674             return;
30675         }
30676         
30677         var standard = [
30678             ['xs', 'xs', 'xs', 'tall'],
30679             ['xs', 'xs', 'tall'],
30680             ['xs', 'xs', 'sm'],
30681             ['xs', 'xs', 'xs'],
30682             ['xs', 'tall'],
30683             ['xs', 'sm'],
30684             ['xs', 'xs'],
30685             ['xs'],
30686             
30687             ['sm', 'xs', 'xs'],
30688             ['sm', 'xs'],
30689             ['sm'],
30690             
30691             ['tall', 'xs', 'xs', 'xs'],
30692             ['tall', 'xs', 'xs'],
30693             ['tall', 'xs'],
30694             ['tall']
30695             
30696         ];
30697         
30698         var queue = [];
30699         
30700         var boxes = [];
30701         
30702         var box = [];
30703         
30704         Roo.each(items, function(item, k){
30705             
30706             switch (item.size) {
30707                 // these layouts take up a full box,
30708                 case 'md' :
30709                 case 'md-left' :
30710                 case 'md-right' :
30711                 case 'wide' :
30712                     
30713                     if(box.length){
30714                         boxes.push(box);
30715                         box = [];
30716                     }
30717                     
30718                     boxes.push([item]);
30719                     
30720                     break;
30721                     
30722                 case 'xs' :
30723                 case 'sm' :
30724                 case 'tall' :
30725                     
30726                     box.push(item);
30727                     
30728                     break;
30729                 default :
30730                     break;
30731                     
30732             }
30733             
30734         }, this);
30735         
30736         if(box.length){
30737             boxes.push(box);
30738             box = [];
30739         }
30740         
30741         var filterPattern = function(box, length)
30742         {
30743             if(!box.length){
30744                 return;
30745             }
30746             
30747             var match = false;
30748             
30749             var pattern = box.slice(0, length);
30750             
30751             var format = [];
30752             
30753             Roo.each(pattern, function(i){
30754                 format.push(i.size);
30755             }, this);
30756             
30757             Roo.each(standard, function(s){
30758                 
30759                 if(String(s) != String(format)){
30760                     return;
30761                 }
30762                 
30763                 match = true;
30764                 return false;
30765                 
30766             }, this);
30767             
30768             if(!match && length == 1){
30769                 return;
30770             }
30771             
30772             if(!match){
30773                 filterPattern(box, length - 1);
30774                 return;
30775             }
30776                 
30777             queue.push(pattern);
30778
30779             box = box.slice(length, box.length);
30780
30781             filterPattern(box, 4);
30782
30783             return;
30784             
30785         }
30786         
30787         Roo.each(boxes, function(box, k){
30788             
30789             if(!box.length){
30790                 return;
30791             }
30792             
30793             if(box.length == 1){
30794                 queue.push(box);
30795                 return;
30796             }
30797             
30798             filterPattern(box, 4);
30799             
30800         }, this);
30801         
30802         this._processVerticalLayoutQueue( queue, isInstant );
30803         
30804     },
30805     
30806 //    _verticalAlternativeLayoutItems : function( items , isInstant )
30807 //    {
30808 //        if ( !items || !items.length ) {
30809 //            return;
30810 //        }
30811 //
30812 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
30813 //        
30814 //    },
30815     
30816     _horizontalLayoutItems : function ( items , isInstant)
30817     {
30818         if ( !items || !items.length || items.length < 3) {
30819             return;
30820         }
30821         
30822         items.reverse();
30823         
30824         var eItems = items.slice(0, 3);
30825         
30826         items = items.slice(3, items.length);
30827         
30828         var standard = [
30829             ['xs', 'xs', 'xs', 'wide'],
30830             ['xs', 'xs', 'wide'],
30831             ['xs', 'xs', 'sm'],
30832             ['xs', 'xs', 'xs'],
30833             ['xs', 'wide'],
30834             ['xs', 'sm'],
30835             ['xs', 'xs'],
30836             ['xs'],
30837             
30838             ['sm', 'xs', 'xs'],
30839             ['sm', 'xs'],
30840             ['sm'],
30841             
30842             ['wide', 'xs', 'xs', 'xs'],
30843             ['wide', 'xs', 'xs'],
30844             ['wide', 'xs'],
30845             ['wide'],
30846             
30847             ['wide-thin']
30848         ];
30849         
30850         var queue = [];
30851         
30852         var boxes = [];
30853         
30854         var box = [];
30855         
30856         Roo.each(items, function(item, k){
30857             
30858             switch (item.size) {
30859                 case 'md' :
30860                 case 'md-left' :
30861                 case 'md-right' :
30862                 case 'tall' :
30863                     
30864                     if(box.length){
30865                         boxes.push(box);
30866                         box = [];
30867                     }
30868                     
30869                     boxes.push([item]);
30870                     
30871                     break;
30872                     
30873                 case 'xs' :
30874                 case 'sm' :
30875                 case 'wide' :
30876                 case 'wide-thin' :
30877                     
30878                     box.push(item);
30879                     
30880                     break;
30881                 default :
30882                     break;
30883                     
30884             }
30885             
30886         }, this);
30887         
30888         if(box.length){
30889             boxes.push(box);
30890             box = [];
30891         }
30892         
30893         var filterPattern = function(box, length)
30894         {
30895             if(!box.length){
30896                 return;
30897             }
30898             
30899             var match = false;
30900             
30901             var pattern = box.slice(0, length);
30902             
30903             var format = [];
30904             
30905             Roo.each(pattern, function(i){
30906                 format.push(i.size);
30907             }, this);
30908             
30909             Roo.each(standard, function(s){
30910                 
30911                 if(String(s) != String(format)){
30912                     return;
30913                 }
30914                 
30915                 match = true;
30916                 return false;
30917                 
30918             }, this);
30919             
30920             if(!match && length == 1){
30921                 return;
30922             }
30923             
30924             if(!match){
30925                 filterPattern(box, length - 1);
30926                 return;
30927             }
30928                 
30929             queue.push(pattern);
30930
30931             box = box.slice(length, box.length);
30932
30933             filterPattern(box, 4);
30934
30935             return;
30936             
30937         }
30938         
30939         Roo.each(boxes, function(box, k){
30940             
30941             if(!box.length){
30942                 return;
30943             }
30944             
30945             if(box.length == 1){
30946                 queue.push(box);
30947                 return;
30948             }
30949             
30950             filterPattern(box, 4);
30951             
30952         }, this);
30953         
30954         
30955         var prune = [];
30956         
30957         var pos = this.el.getBox(true);
30958         
30959         var minX = pos.x;
30960         
30961         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
30962         
30963         var hit_end = false;
30964         
30965         Roo.each(queue, function(box){
30966             
30967             if(hit_end){
30968                 
30969                 Roo.each(box, function(b){
30970                 
30971                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30972                     b.el.hide();
30973
30974                 }, this);
30975
30976                 return;
30977             }
30978             
30979             var mx = 0;
30980             
30981             Roo.each(box, function(b){
30982                 
30983                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
30984                 b.el.show();
30985
30986                 mx = Math.max(mx, b.x);
30987                 
30988             }, this);
30989             
30990             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
30991             
30992             if(maxX < minX){
30993                 
30994                 Roo.each(box, function(b){
30995                 
30996                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
30997                     b.el.hide();
30998                     
30999                 }, this);
31000                 
31001                 hit_end = true;
31002                 
31003                 return;
31004             }
31005             
31006             prune.push(box);
31007             
31008         }, this);
31009         
31010         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
31011     },
31012     
31013     /** Sets position of item in DOM
31014     * @param {Element} item
31015     * @param {Number} x - horizontal position
31016     * @param {Number} y - vertical position
31017     * @param {Boolean} isInstant - disables transitions
31018     */
31019     _processVerticalLayoutQueue : function( queue, isInstant )
31020     {
31021         var pos = this.el.getBox(true);
31022         var x = pos.x;
31023         var y = pos.y;
31024         var maxY = [];
31025         
31026         for (var i = 0; i < this.cols; i++){
31027             maxY[i] = pos.y;
31028         }
31029         
31030         Roo.each(queue, function(box, k){
31031             
31032             var col = k % this.cols;
31033             
31034             Roo.each(box, function(b,kk){
31035                 
31036                 b.el.position('absolute');
31037                 
31038                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31039                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31040                 
31041                 if(b.size == 'md-left' || b.size == 'md-right'){
31042                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31043                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31044                 }
31045                 
31046                 b.el.setWidth(width);
31047                 b.el.setHeight(height);
31048                 // iframe?
31049                 b.el.select('iframe',true).setSize(width,height);
31050                 
31051             }, this);
31052             
31053             for (var i = 0; i < this.cols; i++){
31054                 
31055                 if(maxY[i] < maxY[col]){
31056                     col = i;
31057                     continue;
31058                 }
31059                 
31060                 col = Math.min(col, i);
31061                 
31062             }
31063             
31064             x = pos.x + col * (this.colWidth + this.padWidth);
31065             
31066             y = maxY[col];
31067             
31068             var positions = [];
31069             
31070             switch (box.length){
31071                 case 1 :
31072                     positions = this.getVerticalOneBoxColPositions(x, y, box);
31073                     break;
31074                 case 2 :
31075                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
31076                     break;
31077                 case 3 :
31078                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
31079                     break;
31080                 case 4 :
31081                     positions = this.getVerticalFourBoxColPositions(x, y, box);
31082                     break;
31083                 default :
31084                     break;
31085             }
31086             
31087             Roo.each(box, function(b,kk){
31088                 
31089                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31090                 
31091                 var sz = b.el.getSize();
31092                 
31093                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
31094                 
31095             }, this);
31096             
31097         }, this);
31098         
31099         var mY = 0;
31100         
31101         for (var i = 0; i < this.cols; i++){
31102             mY = Math.max(mY, maxY[i]);
31103         }
31104         
31105         this.el.setHeight(mY - pos.y);
31106         
31107     },
31108     
31109 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
31110 //    {
31111 //        var pos = this.el.getBox(true);
31112 //        var x = pos.x;
31113 //        var y = pos.y;
31114 //        var maxX = pos.right;
31115 //        
31116 //        var maxHeight = 0;
31117 //        
31118 //        Roo.each(items, function(item, k){
31119 //            
31120 //            var c = k % 2;
31121 //            
31122 //            item.el.position('absolute');
31123 //                
31124 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
31125 //
31126 //            item.el.setWidth(width);
31127 //
31128 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
31129 //
31130 //            item.el.setHeight(height);
31131 //            
31132 //            if(c == 0){
31133 //                item.el.setXY([x, y], isInstant ? false : true);
31134 //            } else {
31135 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
31136 //            }
31137 //            
31138 //            y = y + height + this.alternativePadWidth;
31139 //            
31140 //            maxHeight = maxHeight + height + this.alternativePadWidth;
31141 //            
31142 //        }, this);
31143 //        
31144 //        this.el.setHeight(maxHeight);
31145 //        
31146 //    },
31147     
31148     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
31149     {
31150         var pos = this.el.getBox(true);
31151         
31152         var minX = pos.x;
31153         var minY = pos.y;
31154         
31155         var maxX = pos.right;
31156         
31157         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
31158         
31159         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
31160         
31161         Roo.each(queue, function(box, k){
31162             
31163             Roo.each(box, function(b, kk){
31164                 
31165                 b.el.position('absolute');
31166                 
31167                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31168                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31169                 
31170                 if(b.size == 'md-left' || b.size == 'md-right'){
31171                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
31172                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
31173                 }
31174                 
31175                 b.el.setWidth(width);
31176                 b.el.setHeight(height);
31177                 
31178             }, this);
31179             
31180             if(!box.length){
31181                 return;
31182             }
31183             
31184             var positions = [];
31185             
31186             switch (box.length){
31187                 case 1 :
31188                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
31189                     break;
31190                 case 2 :
31191                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
31192                     break;
31193                 case 3 :
31194                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
31195                     break;
31196                 case 4 :
31197                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
31198                     break;
31199                 default :
31200                     break;
31201             }
31202             
31203             Roo.each(box, function(b,kk){
31204                 
31205                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
31206                 
31207                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
31208                 
31209             }, this);
31210             
31211         }, this);
31212         
31213     },
31214     
31215     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
31216     {
31217         Roo.each(eItems, function(b,k){
31218             
31219             b.size = (k == 0) ? 'sm' : 'xs';
31220             b.x = (k == 0) ? 2 : 1;
31221             b.y = (k == 0) ? 2 : 1;
31222             
31223             b.el.position('absolute');
31224             
31225             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
31226                 
31227             b.el.setWidth(width);
31228             
31229             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
31230             
31231             b.el.setHeight(height);
31232             
31233         }, this);
31234
31235         var positions = [];
31236         
31237         positions.push({
31238             x : maxX - this.unitWidth * 2 - this.gutter,
31239             y : minY
31240         });
31241         
31242         positions.push({
31243             x : maxX - this.unitWidth,
31244             y : minY + (this.unitWidth + this.gutter) * 2
31245         });
31246         
31247         positions.push({
31248             x : maxX - this.unitWidth * 3 - this.gutter * 2,
31249             y : minY
31250         });
31251         
31252         Roo.each(eItems, function(b,k){
31253             
31254             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
31255
31256         }, this);
31257         
31258     },
31259     
31260     getVerticalOneBoxColPositions : function(x, y, box)
31261     {
31262         var pos = [];
31263         
31264         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
31265         
31266         if(box[0].size == 'md-left'){
31267             rand = 0;
31268         }
31269         
31270         if(box[0].size == 'md-right'){
31271             rand = 1;
31272         }
31273         
31274         pos.push({
31275             x : x + (this.unitWidth + this.gutter) * rand,
31276             y : y
31277         });
31278         
31279         return pos;
31280     },
31281     
31282     getVerticalTwoBoxColPositions : function(x, y, box)
31283     {
31284         var pos = [];
31285         
31286         if(box[0].size == 'xs'){
31287             
31288             pos.push({
31289                 x : x,
31290                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
31291             });
31292
31293             pos.push({
31294                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
31295                 y : y
31296             });
31297             
31298             return pos;
31299             
31300         }
31301         
31302         pos.push({
31303             x : x,
31304             y : y
31305         });
31306
31307         pos.push({
31308             x : x + (this.unitWidth + this.gutter) * 2,
31309             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
31310         });
31311         
31312         return pos;
31313         
31314     },
31315     
31316     getVerticalThreeBoxColPositions : function(x, y, box)
31317     {
31318         var pos = [];
31319         
31320         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31321             
31322             pos.push({
31323                 x : x,
31324                 y : y
31325             });
31326
31327             pos.push({
31328                 x : x + (this.unitWidth + this.gutter) * 1,
31329                 y : y
31330             });
31331             
31332             pos.push({
31333                 x : x + (this.unitWidth + this.gutter) * 2,
31334                 y : y
31335             });
31336             
31337             return pos;
31338             
31339         }
31340         
31341         if(box[0].size == 'xs' && box[1].size == 'xs'){
31342             
31343             pos.push({
31344                 x : x,
31345                 y : y
31346             });
31347
31348             pos.push({
31349                 x : x,
31350                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
31351             });
31352             
31353             pos.push({
31354                 x : x + (this.unitWidth + this.gutter) * 1,
31355                 y : y
31356             });
31357             
31358             return pos;
31359             
31360         }
31361         
31362         pos.push({
31363             x : x,
31364             y : y
31365         });
31366
31367         pos.push({
31368             x : x + (this.unitWidth + this.gutter) * 2,
31369             y : y
31370         });
31371
31372         pos.push({
31373             x : x + (this.unitWidth + this.gutter) * 2,
31374             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
31375         });
31376             
31377         return pos;
31378         
31379     },
31380     
31381     getVerticalFourBoxColPositions : function(x, y, box)
31382     {
31383         var pos = [];
31384         
31385         if(box[0].size == 'xs'){
31386             
31387             pos.push({
31388                 x : x,
31389                 y : y
31390             });
31391
31392             pos.push({
31393                 x : x,
31394                 y : y + (this.unitHeight + this.gutter) * 1
31395             });
31396             
31397             pos.push({
31398                 x : x,
31399                 y : y + (this.unitHeight + this.gutter) * 2
31400             });
31401             
31402             pos.push({
31403                 x : x + (this.unitWidth + this.gutter) * 1,
31404                 y : y
31405             });
31406             
31407             return pos;
31408             
31409         }
31410         
31411         pos.push({
31412             x : x,
31413             y : y
31414         });
31415
31416         pos.push({
31417             x : x + (this.unitWidth + this.gutter) * 2,
31418             y : y
31419         });
31420
31421         pos.push({
31422             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
31423             y : y + (this.unitHeight + this.gutter) * 1
31424         });
31425
31426         pos.push({
31427             x : x + (this.unitWidth + this.gutter) * 2,
31428             y : y + (this.unitWidth + this.gutter) * 2
31429         });
31430
31431         return pos;
31432         
31433     },
31434     
31435     getHorizontalOneBoxColPositions : function(maxX, minY, box)
31436     {
31437         var pos = [];
31438         
31439         if(box[0].size == 'md-left'){
31440             pos.push({
31441                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31442                 y : minY
31443             });
31444             
31445             return pos;
31446         }
31447         
31448         if(box[0].size == 'md-right'){
31449             pos.push({
31450                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
31451                 y : minY + (this.unitWidth + this.gutter) * 1
31452             });
31453             
31454             return pos;
31455         }
31456         
31457         var rand = Math.floor(Math.random() * (4 - box[0].y));
31458         
31459         pos.push({
31460             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31461             y : minY + (this.unitWidth + this.gutter) * rand
31462         });
31463         
31464         return pos;
31465         
31466     },
31467     
31468     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
31469     {
31470         var pos = [];
31471         
31472         if(box[0].size == 'xs'){
31473             
31474             pos.push({
31475                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31476                 y : minY
31477             });
31478
31479             pos.push({
31480                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31481                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
31482             });
31483             
31484             return pos;
31485             
31486         }
31487         
31488         pos.push({
31489             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31490             y : minY
31491         });
31492
31493         pos.push({
31494             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31495             y : minY + (this.unitWidth + this.gutter) * 2
31496         });
31497         
31498         return pos;
31499         
31500     },
31501     
31502     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
31503     {
31504         var pos = [];
31505         
31506         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
31507             
31508             pos.push({
31509                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31510                 y : minY
31511             });
31512
31513             pos.push({
31514                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31515                 y : minY + (this.unitWidth + this.gutter) * 1
31516             });
31517             
31518             pos.push({
31519                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31520                 y : minY + (this.unitWidth + this.gutter) * 2
31521             });
31522             
31523             return pos;
31524             
31525         }
31526         
31527         if(box[0].size == 'xs' && box[1].size == 'xs'){
31528             
31529             pos.push({
31530                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31531                 y : minY
31532             });
31533
31534             pos.push({
31535                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31536                 y : minY
31537             });
31538             
31539             pos.push({
31540                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31541                 y : minY + (this.unitWidth + this.gutter) * 1
31542             });
31543             
31544             return pos;
31545             
31546         }
31547         
31548         pos.push({
31549             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31550             y : minY
31551         });
31552
31553         pos.push({
31554             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31555             y : minY + (this.unitWidth + this.gutter) * 2
31556         });
31557
31558         pos.push({
31559             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31560             y : minY + (this.unitWidth + this.gutter) * 2
31561         });
31562             
31563         return pos;
31564         
31565     },
31566     
31567     getHorizontalFourBoxColPositions : function(maxX, minY, box)
31568     {
31569         var pos = [];
31570         
31571         if(box[0].size == 'xs'){
31572             
31573             pos.push({
31574                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31575                 y : minY
31576             });
31577
31578             pos.push({
31579                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31580                 y : minY
31581             });
31582             
31583             pos.push({
31584                 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),
31585                 y : minY
31586             });
31587             
31588             pos.push({
31589                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
31590                 y : minY + (this.unitWidth + this.gutter) * 1
31591             });
31592             
31593             return pos;
31594             
31595         }
31596         
31597         pos.push({
31598             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
31599             y : minY
31600         });
31601         
31602         pos.push({
31603             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
31604             y : minY + (this.unitWidth + this.gutter) * 2
31605         });
31606         
31607         pos.push({
31608             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
31609             y : minY + (this.unitWidth + this.gutter) * 2
31610         });
31611         
31612         pos.push({
31613             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),
31614             y : minY + (this.unitWidth + this.gutter) * 2
31615         });
31616
31617         return pos;
31618         
31619     },
31620     
31621     /**
31622     * remove a Masonry Brick
31623     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
31624     */
31625     removeBrick : function(brick_id)
31626     {
31627         if (!brick_id) {
31628             return;
31629         }
31630         
31631         for (var i = 0; i<this.bricks.length; i++) {
31632             if (this.bricks[i].id == brick_id) {
31633                 this.bricks.splice(i,1);
31634                 this.el.dom.removeChild(Roo.get(brick_id).dom);
31635                 this.initial();
31636             }
31637         }
31638     },
31639     
31640     /**
31641     * adds a Masonry Brick
31642     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31643     */
31644     addBrick : function(cfg)
31645     {
31646         var cn = new Roo.bootstrap.MasonryBrick(cfg);
31647         //this.register(cn);
31648         cn.parentId = this.id;
31649         cn.onRender(this.el, null);
31650         return cn;
31651     },
31652     
31653     /**
31654     * register a Masonry Brick
31655     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
31656     */
31657     
31658     register : function(brick)
31659     {
31660         this.bricks.push(brick);
31661         brick.masonryId = this.id;
31662     },
31663     
31664     /**
31665     * clear all the Masonry Brick
31666     */
31667     clearAll : function()
31668     {
31669         this.bricks = [];
31670         //this.getChildContainer().dom.innerHTML = "";
31671         this.el.dom.innerHTML = '';
31672     },
31673     
31674     getSelected : function()
31675     {
31676         if (!this.selectedBrick) {
31677             return false;
31678         }
31679         
31680         return this.selectedBrick;
31681     }
31682 });
31683
31684 Roo.apply(Roo.bootstrap.LayoutMasonry, {
31685     
31686     groups: {},
31687      /**
31688     * register a Masonry Layout
31689     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
31690     */
31691     
31692     register : function(layout)
31693     {
31694         this.groups[layout.id] = layout;
31695     },
31696     /**
31697     * fetch a  Masonry Layout based on the masonry layout ID
31698     * @param {string} the masonry layout to add
31699     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
31700     */
31701     
31702     get: function(layout_id) {
31703         if (typeof(this.groups[layout_id]) == 'undefined') {
31704             return false;
31705         }
31706         return this.groups[layout_id] ;
31707     }
31708     
31709     
31710     
31711 });
31712
31713  
31714
31715  /**
31716  *
31717  * This is based on 
31718  * http://masonry.desandro.com
31719  *
31720  * The idea is to render all the bricks based on vertical width...
31721  *
31722  * The original code extends 'outlayer' - we might need to use that....
31723  * 
31724  */
31725
31726
31727 /**
31728  * @class Roo.bootstrap.LayoutMasonryAuto
31729  * @extends Roo.bootstrap.Component
31730  * Bootstrap Layout Masonry class
31731  * 
31732  * @constructor
31733  * Create a new Element
31734  * @param {Object} config The config object
31735  */
31736
31737 Roo.bootstrap.LayoutMasonryAuto = function(config){
31738     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
31739 };
31740
31741 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
31742     
31743       /**
31744      * @cfg {Boolean} isFitWidth  - resize the width..
31745      */   
31746     isFitWidth : false,  // options..
31747     /**
31748      * @cfg {Boolean} isOriginLeft = left align?
31749      */   
31750     isOriginLeft : true,
31751     /**
31752      * @cfg {Boolean} isOriginTop = top align?
31753      */   
31754     isOriginTop : false,
31755     /**
31756      * @cfg {Boolean} isLayoutInstant = no animation?
31757      */   
31758     isLayoutInstant : false, // needed?
31759     /**
31760      * @cfg {Boolean} isResizingContainer = not sure if this is used..
31761      */   
31762     isResizingContainer : true,
31763     /**
31764      * @cfg {Number} columnWidth  width of the columns 
31765      */   
31766     
31767     columnWidth : 0,
31768     
31769     /**
31770      * @cfg {Number} maxCols maximum number of columns
31771      */   
31772     
31773     maxCols: 0,
31774     /**
31775      * @cfg {Number} padHeight padding below box..
31776      */   
31777     
31778     padHeight : 10, 
31779     
31780     /**
31781      * @cfg {Boolean} isAutoInitial defalut true
31782      */   
31783     
31784     isAutoInitial : true, 
31785     
31786     // private?
31787     gutter : 0,
31788     
31789     containerWidth: 0,
31790     initialColumnWidth : 0,
31791     currentSize : null,
31792     
31793     colYs : null, // array.
31794     maxY : 0,
31795     padWidth: 10,
31796     
31797     
31798     tag: 'div',
31799     cls: '',
31800     bricks: null, //CompositeElement
31801     cols : 0, // array?
31802     // element : null, // wrapped now this.el
31803     _isLayoutInited : null, 
31804     
31805     
31806     getAutoCreate : function(){
31807         
31808         var cfg = {
31809             tag: this.tag,
31810             cls: 'blog-masonary-wrapper ' + this.cls,
31811             cn : {
31812                 cls : 'mas-boxes masonary'
31813             }
31814         };
31815         
31816         return cfg;
31817     },
31818     
31819     getChildContainer: function( )
31820     {
31821         if (this.boxesEl) {
31822             return this.boxesEl;
31823         }
31824         
31825         this.boxesEl = this.el.select('.mas-boxes').first();
31826         
31827         return this.boxesEl;
31828     },
31829     
31830     
31831     initEvents : function()
31832     {
31833         var _this = this;
31834         
31835         if(this.isAutoInitial){
31836             Roo.log('hook children rendered');
31837             this.on('childrenrendered', function() {
31838                 Roo.log('children rendered');
31839                 _this.initial();
31840             } ,this);
31841         }
31842         
31843     },
31844     
31845     initial : function()
31846     {
31847         this.reloadItems();
31848
31849         this.currentSize = this.el.getBox(true);
31850
31851         /// was window resize... - let's see if this works..
31852         Roo.EventManager.onWindowResize(this.resize, this); 
31853
31854         if(!this.isAutoInitial){
31855             this.layout();
31856             return;
31857         }
31858         
31859         this.layout.defer(500,this);
31860     },
31861     
31862     reloadItems: function()
31863     {
31864         this.bricks = this.el.select('.masonry-brick', true);
31865         
31866         this.bricks.each(function(b) {
31867             //Roo.log(b.getSize());
31868             if (!b.attr('originalwidth')) {
31869                 b.attr('originalwidth',  b.getSize().width);
31870             }
31871             
31872         });
31873         
31874         Roo.log(this.bricks.elements.length);
31875     },
31876     
31877     resize : function()
31878     {
31879         Roo.log('resize');
31880         var cs = this.el.getBox(true);
31881         
31882         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
31883             Roo.log("no change in with or X");
31884             return;
31885         }
31886         this.currentSize = cs;
31887         this.layout();
31888     },
31889     
31890     layout : function()
31891     {
31892          Roo.log('layout');
31893         this._resetLayout();
31894         //this._manageStamps();
31895       
31896         // don't animate first layout
31897         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
31898         this.layoutItems( isInstant );
31899       
31900         // flag for initalized
31901         this._isLayoutInited = true;
31902     },
31903     
31904     layoutItems : function( isInstant )
31905     {
31906         //var items = this._getItemsForLayout( this.items );
31907         // original code supports filtering layout items.. we just ignore it..
31908         
31909         this._layoutItems( this.bricks , isInstant );
31910       
31911         this._postLayout();
31912     },
31913     _layoutItems : function ( items , isInstant)
31914     {
31915        //this.fireEvent( 'layout', this, items );
31916     
31917
31918         if ( !items || !items.elements.length ) {
31919           // no items, emit event with empty array
31920             return;
31921         }
31922
31923         var queue = [];
31924         items.each(function(item) {
31925             Roo.log("layout item");
31926             Roo.log(item);
31927             // get x/y object from method
31928             var position = this._getItemLayoutPosition( item );
31929             // enqueue
31930             position.item = item;
31931             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
31932             queue.push( position );
31933         }, this);
31934       
31935         this._processLayoutQueue( queue );
31936     },
31937     /** Sets position of item in DOM
31938     * @param {Element} item
31939     * @param {Number} x - horizontal position
31940     * @param {Number} y - vertical position
31941     * @param {Boolean} isInstant - disables transitions
31942     */
31943     _processLayoutQueue : function( queue )
31944     {
31945         for ( var i=0, len = queue.length; i < len; i++ ) {
31946             var obj = queue[i];
31947             obj.item.position('absolute');
31948             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
31949         }
31950     },
31951       
31952     
31953     /**
31954     * Any logic you want to do after each layout,
31955     * i.e. size the container
31956     */
31957     _postLayout : function()
31958     {
31959         this.resizeContainer();
31960     },
31961     
31962     resizeContainer : function()
31963     {
31964         if ( !this.isResizingContainer ) {
31965             return;
31966         }
31967         var size = this._getContainerSize();
31968         if ( size ) {
31969             this.el.setSize(size.width,size.height);
31970             this.boxesEl.setSize(size.width,size.height);
31971         }
31972     },
31973     
31974     
31975     
31976     _resetLayout : function()
31977     {
31978         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
31979         this.colWidth = this.el.getWidth();
31980         //this.gutter = this.el.getWidth(); 
31981         
31982         this.measureColumns();
31983
31984         // reset column Y
31985         var i = this.cols;
31986         this.colYs = [];
31987         while (i--) {
31988             this.colYs.push( 0 );
31989         }
31990     
31991         this.maxY = 0;
31992     },
31993
31994     measureColumns : function()
31995     {
31996         this.getContainerWidth();
31997       // if columnWidth is 0, default to outerWidth of first item
31998         if ( !this.columnWidth ) {
31999             var firstItem = this.bricks.first();
32000             Roo.log(firstItem);
32001             this.columnWidth  = this.containerWidth;
32002             if (firstItem && firstItem.attr('originalwidth') ) {
32003                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
32004             }
32005             // columnWidth fall back to item of first element
32006             Roo.log("set column width?");
32007                         this.initialColumnWidth = this.columnWidth  ;
32008
32009             // if first elem has no width, default to size of container
32010             
32011         }
32012         
32013         
32014         if (this.initialColumnWidth) {
32015             this.columnWidth = this.initialColumnWidth;
32016         }
32017         
32018         
32019             
32020         // column width is fixed at the top - however if container width get's smaller we should
32021         // reduce it...
32022         
32023         // this bit calcs how man columns..
32024             
32025         var columnWidth = this.columnWidth += this.gutter;
32026       
32027         // calculate columns
32028         var containerWidth = this.containerWidth + this.gutter;
32029         
32030         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
32031         // fix rounding errors, typically with gutters
32032         var excess = columnWidth - containerWidth % columnWidth;
32033         
32034         
32035         // if overshoot is less than a pixel, round up, otherwise floor it
32036         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
32037         cols = Math[ mathMethod ]( cols );
32038         this.cols = Math.max( cols, 1 );
32039         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
32040         
32041          // padding positioning..
32042         var totalColWidth = this.cols * this.columnWidth;
32043         var padavail = this.containerWidth - totalColWidth;
32044         // so for 2 columns - we need 3 'pads'
32045         
32046         var padNeeded = (1+this.cols) * this.padWidth;
32047         
32048         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
32049         
32050         this.columnWidth += padExtra
32051         //this.padWidth = Math.floor(padavail /  ( this.cols));
32052         
32053         // adjust colum width so that padding is fixed??
32054         
32055         // we have 3 columns ... total = width * 3
32056         // we have X left over... that should be used by 
32057         
32058         //if (this.expandC) {
32059             
32060         //}
32061         
32062         
32063         
32064     },
32065     
32066     getContainerWidth : function()
32067     {
32068        /* // container is parent if fit width
32069         var container = this.isFitWidth ? this.element.parentNode : this.element;
32070         // check that this.size and size are there
32071         // IE8 triggers resize on body size change, so they might not be
32072         
32073         var size = getSize( container );  //FIXME
32074         this.containerWidth = size && size.innerWidth; //FIXME
32075         */
32076          
32077         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
32078         
32079     },
32080     
32081     _getItemLayoutPosition : function( item )  // what is item?
32082     {
32083         // we resize the item to our columnWidth..
32084       
32085         item.setWidth(this.columnWidth);
32086         item.autoBoxAdjust  = false;
32087         
32088         var sz = item.getSize();
32089  
32090         // how many columns does this brick span
32091         var remainder = this.containerWidth % this.columnWidth;
32092         
32093         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
32094         // round if off by 1 pixel, otherwise use ceil
32095         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
32096         colSpan = Math.min( colSpan, this.cols );
32097         
32098         // normally this should be '1' as we dont' currently allow multi width columns..
32099         
32100         var colGroup = this._getColGroup( colSpan );
32101         // get the minimum Y value from the columns
32102         var minimumY = Math.min.apply( Math, colGroup );
32103         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32104         
32105         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
32106          
32107         // position the brick
32108         var position = {
32109             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
32110             y: this.currentSize.y + minimumY + this.padHeight
32111         };
32112         
32113         Roo.log(position);
32114         // apply setHeight to necessary columns
32115         var setHeight = minimumY + sz.height + this.padHeight;
32116         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
32117         
32118         var setSpan = this.cols + 1 - colGroup.length;
32119         for ( var i = 0; i < setSpan; i++ ) {
32120           this.colYs[ shortColIndex + i ] = setHeight ;
32121         }
32122       
32123         return position;
32124     },
32125     
32126     /**
32127      * @param {Number} colSpan - number of columns the element spans
32128      * @returns {Array} colGroup
32129      */
32130     _getColGroup : function( colSpan )
32131     {
32132         if ( colSpan < 2 ) {
32133           // if brick spans only one column, use all the column Ys
32134           return this.colYs;
32135         }
32136       
32137         var colGroup = [];
32138         // how many different places could this brick fit horizontally
32139         var groupCount = this.cols + 1 - colSpan;
32140         // for each group potential horizontal position
32141         for ( var i = 0; i < groupCount; i++ ) {
32142           // make an array of colY values for that one group
32143           var groupColYs = this.colYs.slice( i, i + colSpan );
32144           // and get the max value of the array
32145           colGroup[i] = Math.max.apply( Math, groupColYs );
32146         }
32147         return colGroup;
32148     },
32149     /*
32150     _manageStamp : function( stamp )
32151     {
32152         var stampSize =  stamp.getSize();
32153         var offset = stamp.getBox();
32154         // get the columns that this stamp affects
32155         var firstX = this.isOriginLeft ? offset.x : offset.right;
32156         var lastX = firstX + stampSize.width;
32157         var firstCol = Math.floor( firstX / this.columnWidth );
32158         firstCol = Math.max( 0, firstCol );
32159         
32160         var lastCol = Math.floor( lastX / this.columnWidth );
32161         // lastCol should not go over if multiple of columnWidth #425
32162         lastCol -= lastX % this.columnWidth ? 0 : 1;
32163         lastCol = Math.min( this.cols - 1, lastCol );
32164         
32165         // set colYs to bottom of the stamp
32166         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
32167             stampSize.height;
32168             
32169         for ( var i = firstCol; i <= lastCol; i++ ) {
32170           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
32171         }
32172     },
32173     */
32174     
32175     _getContainerSize : function()
32176     {
32177         this.maxY = Math.max.apply( Math, this.colYs );
32178         var size = {
32179             height: this.maxY
32180         };
32181       
32182         if ( this.isFitWidth ) {
32183             size.width = this._getContainerFitWidth();
32184         }
32185       
32186         return size;
32187     },
32188     
32189     _getContainerFitWidth : function()
32190     {
32191         var unusedCols = 0;
32192         // count unused columns
32193         var i = this.cols;
32194         while ( --i ) {
32195           if ( this.colYs[i] !== 0 ) {
32196             break;
32197           }
32198           unusedCols++;
32199         }
32200         // fit container to columns that have been used
32201         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
32202     },
32203     
32204     needsResizeLayout : function()
32205     {
32206         var previousWidth = this.containerWidth;
32207         this.getContainerWidth();
32208         return previousWidth !== this.containerWidth;
32209     }
32210  
32211 });
32212
32213  
32214
32215  /*
32216  * - LGPL
32217  *
32218  * element
32219  * 
32220  */
32221
32222 /**
32223  * @class Roo.bootstrap.MasonryBrick
32224  * @extends Roo.bootstrap.Component
32225  * Bootstrap MasonryBrick class
32226  * 
32227  * @constructor
32228  * Create a new MasonryBrick
32229  * @param {Object} config The config object
32230  */
32231
32232 Roo.bootstrap.MasonryBrick = function(config){
32233     
32234     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
32235     
32236     Roo.bootstrap.MasonryBrick.register(this);
32237     
32238     this.addEvents({
32239         // raw events
32240         /**
32241          * @event click
32242          * When a MasonryBrick is clcik
32243          * @param {Roo.bootstrap.MasonryBrick} this
32244          * @param {Roo.EventObject} e
32245          */
32246         "click" : true
32247     });
32248 };
32249
32250 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
32251     
32252     /**
32253      * @cfg {String} title
32254      */   
32255     title : '',
32256     /**
32257      * @cfg {String} html
32258      */   
32259     html : '',
32260     /**
32261      * @cfg {String} bgimage
32262      */   
32263     bgimage : '',
32264     /**
32265      * @cfg {String} videourl
32266      */   
32267     videourl : '',
32268     /**
32269      * @cfg {String} cls
32270      */   
32271     cls : '',
32272     /**
32273      * @cfg {String} href
32274      */   
32275     href : '',
32276     /**
32277      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
32278      */   
32279     size : 'xs',
32280     
32281     /**
32282      * @cfg {String} placetitle (center|bottom)
32283      */   
32284     placetitle : '',
32285     
32286     /**
32287      * @cfg {Boolean} isFitContainer defalut true
32288      */   
32289     isFitContainer : true, 
32290     
32291     /**
32292      * @cfg {Boolean} preventDefault defalut false
32293      */   
32294     preventDefault : false, 
32295     
32296     /**
32297      * @cfg {Boolean} inverse defalut false
32298      */   
32299     maskInverse : false, 
32300     
32301     getAutoCreate : function()
32302     {
32303         if(!this.isFitContainer){
32304             return this.getSplitAutoCreate();
32305         }
32306         
32307         var cls = 'masonry-brick masonry-brick-full';
32308         
32309         if(this.href.length){
32310             cls += ' masonry-brick-link';
32311         }
32312         
32313         if(this.bgimage.length){
32314             cls += ' masonry-brick-image';
32315         }
32316         
32317         if(this.maskInverse){
32318             cls += ' mask-inverse';
32319         }
32320         
32321         if(!this.html.length && !this.maskInverse && !this.videourl.length){
32322             cls += ' enable-mask';
32323         }
32324         
32325         if(this.size){
32326             cls += ' masonry-' + this.size + '-brick';
32327         }
32328         
32329         if(this.placetitle.length){
32330             
32331             switch (this.placetitle) {
32332                 case 'center' :
32333                     cls += ' masonry-center-title';
32334                     break;
32335                 case 'bottom' :
32336                     cls += ' masonry-bottom-title';
32337                     break;
32338                 default:
32339                     break;
32340             }
32341             
32342         } else {
32343             if(!this.html.length && !this.bgimage.length){
32344                 cls += ' masonry-center-title';
32345             }
32346
32347             if(!this.html.length && this.bgimage.length){
32348                 cls += ' masonry-bottom-title';
32349             }
32350         }
32351         
32352         if(this.cls){
32353             cls += ' ' + this.cls;
32354         }
32355         
32356         var cfg = {
32357             tag: (this.href.length) ? 'a' : 'div',
32358             cls: cls,
32359             cn: [
32360                 {
32361                     tag: 'div',
32362                     cls: 'masonry-brick-mask'
32363                 },
32364                 {
32365                     tag: 'div',
32366                     cls: 'masonry-brick-paragraph',
32367                     cn: []
32368                 }
32369             ]
32370         };
32371         
32372         if(this.href.length){
32373             cfg.href = this.href;
32374         }
32375         
32376         var cn = cfg.cn[1].cn;
32377         
32378         if(this.title.length){
32379             cn.push({
32380                 tag: 'h4',
32381                 cls: 'masonry-brick-title',
32382                 html: this.title
32383             });
32384         }
32385         
32386         if(this.html.length){
32387             cn.push({
32388                 tag: 'p',
32389                 cls: 'masonry-brick-text',
32390                 html: this.html
32391             });
32392         }
32393         
32394         if (!this.title.length && !this.html.length) {
32395             cfg.cn[1].cls += ' hide';
32396         }
32397         
32398         if(this.bgimage.length){
32399             cfg.cn.push({
32400                 tag: 'img',
32401                 cls: 'masonry-brick-image-view',
32402                 src: this.bgimage
32403             });
32404         }
32405         
32406         if(this.videourl.length){
32407             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32408             // youtube support only?
32409             cfg.cn.push({
32410                 tag: 'iframe',
32411                 cls: 'masonry-brick-image-view',
32412                 src: vurl,
32413                 frameborder : 0,
32414                 allowfullscreen : true
32415             });
32416         }
32417         
32418         return cfg;
32419         
32420     },
32421     
32422     getSplitAutoCreate : function()
32423     {
32424         var cls = 'masonry-brick masonry-brick-split';
32425         
32426         if(this.href.length){
32427             cls += ' masonry-brick-link';
32428         }
32429         
32430         if(this.bgimage.length){
32431             cls += ' masonry-brick-image';
32432         }
32433         
32434         if(this.size){
32435             cls += ' masonry-' + this.size + '-brick';
32436         }
32437         
32438         switch (this.placetitle) {
32439             case 'center' :
32440                 cls += ' masonry-center-title';
32441                 break;
32442             case 'bottom' :
32443                 cls += ' masonry-bottom-title';
32444                 break;
32445             default:
32446                 if(!this.bgimage.length){
32447                     cls += ' masonry-center-title';
32448                 }
32449
32450                 if(this.bgimage.length){
32451                     cls += ' masonry-bottom-title';
32452                 }
32453                 break;
32454         }
32455         
32456         if(this.cls){
32457             cls += ' ' + this.cls;
32458         }
32459         
32460         var cfg = {
32461             tag: (this.href.length) ? 'a' : 'div',
32462             cls: cls,
32463             cn: [
32464                 {
32465                     tag: 'div',
32466                     cls: 'masonry-brick-split-head',
32467                     cn: [
32468                         {
32469                             tag: 'div',
32470                             cls: 'masonry-brick-paragraph',
32471                             cn: []
32472                         }
32473                     ]
32474                 },
32475                 {
32476                     tag: 'div',
32477                     cls: 'masonry-brick-split-body',
32478                     cn: []
32479                 }
32480             ]
32481         };
32482         
32483         if(this.href.length){
32484             cfg.href = this.href;
32485         }
32486         
32487         if(this.title.length){
32488             cfg.cn[0].cn[0].cn.push({
32489                 tag: 'h4',
32490                 cls: 'masonry-brick-title',
32491                 html: this.title
32492             });
32493         }
32494         
32495         if(this.html.length){
32496             cfg.cn[1].cn.push({
32497                 tag: 'p',
32498                 cls: 'masonry-brick-text',
32499                 html: this.html
32500             });
32501         }
32502
32503         if(this.bgimage.length){
32504             cfg.cn[0].cn.push({
32505                 tag: 'img',
32506                 cls: 'masonry-brick-image-view',
32507                 src: this.bgimage
32508             });
32509         }
32510         
32511         if(this.videourl.length){
32512             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
32513             // youtube support only?
32514             cfg.cn[0].cn.cn.push({
32515                 tag: 'iframe',
32516                 cls: 'masonry-brick-image-view',
32517                 src: vurl,
32518                 frameborder : 0,
32519                 allowfullscreen : true
32520             });
32521         }
32522         
32523         return cfg;
32524     },
32525     
32526     initEvents: function() 
32527     {
32528         switch (this.size) {
32529             case 'xs' :
32530                 this.x = 1;
32531                 this.y = 1;
32532                 break;
32533             case 'sm' :
32534                 this.x = 2;
32535                 this.y = 2;
32536                 break;
32537             case 'md' :
32538             case 'md-left' :
32539             case 'md-right' :
32540                 this.x = 3;
32541                 this.y = 3;
32542                 break;
32543             case 'tall' :
32544                 this.x = 2;
32545                 this.y = 3;
32546                 break;
32547             case 'wide' :
32548                 this.x = 3;
32549                 this.y = 2;
32550                 break;
32551             case 'wide-thin' :
32552                 this.x = 3;
32553                 this.y = 1;
32554                 break;
32555                         
32556             default :
32557                 break;
32558         }
32559         
32560         if(Roo.isTouch){
32561             this.el.on('touchstart', this.onTouchStart, this);
32562             this.el.on('touchmove', this.onTouchMove, this);
32563             this.el.on('touchend', this.onTouchEnd, this);
32564             this.el.on('contextmenu', this.onContextMenu, this);
32565         } else {
32566             this.el.on('mouseenter'  ,this.enter, this);
32567             this.el.on('mouseleave', this.leave, this);
32568             this.el.on('click', this.onClick, this);
32569         }
32570         
32571         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
32572             this.parent().bricks.push(this);   
32573         }
32574         
32575     },
32576     
32577     onClick: function(e, el)
32578     {
32579         var time = this.endTimer - this.startTimer;
32580         // Roo.log(e.preventDefault());
32581         if(Roo.isTouch){
32582             if(time > 1000){
32583                 e.preventDefault();
32584                 return;
32585             }
32586         }
32587         
32588         if(!this.preventDefault){
32589             return;
32590         }
32591         
32592         e.preventDefault();
32593         
32594         if (this.activcClass != '') {
32595             this.selectBrick();
32596         }
32597         
32598         this.fireEvent('click', this);
32599     },
32600     
32601     enter: function(e, el)
32602     {
32603         e.preventDefault();
32604         
32605         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
32606             return;
32607         }
32608         
32609         if(this.bgimage.length && this.html.length){
32610             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32611         }
32612     },
32613     
32614     leave: function(e, el)
32615     {
32616         e.preventDefault();
32617         
32618         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
32619             return;
32620         }
32621         
32622         if(this.bgimage.length && this.html.length){
32623             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32624         }
32625     },
32626     
32627     onTouchStart: function(e, el)
32628     {
32629 //        e.preventDefault();
32630         
32631         this.touchmoved = false;
32632         
32633         if(!this.isFitContainer){
32634             return;
32635         }
32636         
32637         if(!this.bgimage.length || !this.html.length){
32638             return;
32639         }
32640         
32641         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
32642         
32643         this.timer = new Date().getTime();
32644         
32645     },
32646     
32647     onTouchMove: function(e, el)
32648     {
32649         this.touchmoved = true;
32650     },
32651     
32652     onContextMenu : function(e,el)
32653     {
32654         e.preventDefault();
32655         e.stopPropagation();
32656         return false;
32657     },
32658     
32659     onTouchEnd: function(e, el)
32660     {
32661 //        e.preventDefault();
32662         
32663         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
32664         
32665             this.leave(e,el);
32666             
32667             return;
32668         }
32669         
32670         if(!this.bgimage.length || !this.html.length){
32671             
32672             if(this.href.length){
32673                 window.location.href = this.href;
32674             }
32675             
32676             return;
32677         }
32678         
32679         if(!this.isFitContainer){
32680             return;
32681         }
32682         
32683         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
32684         
32685         window.location.href = this.href;
32686     },
32687     
32688     //selection on single brick only
32689     selectBrick : function() {
32690         
32691         if (!this.parentId) {
32692             return;
32693         }
32694         
32695         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
32696         var index = m.selectedBrick.indexOf(this.id);
32697         
32698         if ( index > -1) {
32699             m.selectedBrick.splice(index,1);
32700             this.el.removeClass(this.activeClass);
32701             return;
32702         }
32703         
32704         for(var i = 0; i < m.selectedBrick.length; i++) {
32705             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
32706             b.el.removeClass(b.activeClass);
32707         }
32708         
32709         m.selectedBrick = [];
32710         
32711         m.selectedBrick.push(this.id);
32712         this.el.addClass(this.activeClass);
32713         return;
32714     }
32715     
32716 });
32717
32718 Roo.apply(Roo.bootstrap.MasonryBrick, {
32719     
32720     //groups: {},
32721     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
32722      /**
32723     * register a Masonry Brick
32724     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
32725     */
32726     
32727     register : function(brick)
32728     {
32729         //this.groups[brick.id] = brick;
32730         this.groups.add(brick.id, brick);
32731     },
32732     /**
32733     * fetch a  masonry brick based on the masonry brick ID
32734     * @param {string} the masonry brick to add
32735     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
32736     */
32737     
32738     get: function(brick_id) 
32739     {
32740         // if (typeof(this.groups[brick_id]) == 'undefined') {
32741         //     return false;
32742         // }
32743         // return this.groups[brick_id] ;
32744         
32745         if(this.groups.key(brick_id)) {
32746             return this.groups.key(brick_id);
32747         }
32748         
32749         return false;
32750     }
32751     
32752     
32753     
32754 });
32755
32756  /*
32757  * - LGPL
32758  *
32759  * element
32760  * 
32761  */
32762
32763 /**
32764  * @class Roo.bootstrap.Brick
32765  * @extends Roo.bootstrap.Component
32766  * Bootstrap Brick class
32767  * 
32768  * @constructor
32769  * Create a new Brick
32770  * @param {Object} config The config object
32771  */
32772
32773 Roo.bootstrap.Brick = function(config){
32774     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
32775     
32776     this.addEvents({
32777         // raw events
32778         /**
32779          * @event click
32780          * When a Brick is click
32781          * @param {Roo.bootstrap.Brick} this
32782          * @param {Roo.EventObject} e
32783          */
32784         "click" : true
32785     });
32786 };
32787
32788 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
32789     
32790     /**
32791      * @cfg {String} title
32792      */   
32793     title : '',
32794     /**
32795      * @cfg {String} html
32796      */   
32797     html : '',
32798     /**
32799      * @cfg {String} bgimage
32800      */   
32801     bgimage : '',
32802     /**
32803      * @cfg {String} cls
32804      */   
32805     cls : '',
32806     /**
32807      * @cfg {String} href
32808      */   
32809     href : '',
32810     /**
32811      * @cfg {String} video
32812      */   
32813     video : '',
32814     /**
32815      * @cfg {Boolean} square
32816      */   
32817     square : true,
32818     
32819     getAutoCreate : function()
32820     {
32821         var cls = 'roo-brick';
32822         
32823         if(this.href.length){
32824             cls += ' roo-brick-link';
32825         }
32826         
32827         if(this.bgimage.length){
32828             cls += ' roo-brick-image';
32829         }
32830         
32831         if(!this.html.length && !this.bgimage.length){
32832             cls += ' roo-brick-center-title';
32833         }
32834         
32835         if(!this.html.length && this.bgimage.length){
32836             cls += ' roo-brick-bottom-title';
32837         }
32838         
32839         if(this.cls){
32840             cls += ' ' + this.cls;
32841         }
32842         
32843         var cfg = {
32844             tag: (this.href.length) ? 'a' : 'div',
32845             cls: cls,
32846             cn: [
32847                 {
32848                     tag: 'div',
32849                     cls: 'roo-brick-paragraph',
32850                     cn: []
32851                 }
32852             ]
32853         };
32854         
32855         if(this.href.length){
32856             cfg.href = this.href;
32857         }
32858         
32859         var cn = cfg.cn[0].cn;
32860         
32861         if(this.title.length){
32862             cn.push({
32863                 tag: 'h4',
32864                 cls: 'roo-brick-title',
32865                 html: this.title
32866             });
32867         }
32868         
32869         if(this.html.length){
32870             cn.push({
32871                 tag: 'p',
32872                 cls: 'roo-brick-text',
32873                 html: this.html
32874             });
32875         } else {
32876             cn.cls += ' hide';
32877         }
32878         
32879         if(this.bgimage.length){
32880             cfg.cn.push({
32881                 tag: 'img',
32882                 cls: 'roo-brick-image-view',
32883                 src: this.bgimage
32884             });
32885         }
32886         
32887         return cfg;
32888     },
32889     
32890     initEvents: function() 
32891     {
32892         if(this.title.length || this.html.length){
32893             this.el.on('mouseenter'  ,this.enter, this);
32894             this.el.on('mouseleave', this.leave, this);
32895         }
32896         
32897         Roo.EventManager.onWindowResize(this.resize, this); 
32898         
32899         if(this.bgimage.length){
32900             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
32901             this.imageEl.on('load', this.onImageLoad, this);
32902             return;
32903         }
32904         
32905         this.resize();
32906     },
32907     
32908     onImageLoad : function()
32909     {
32910         this.resize();
32911     },
32912     
32913     resize : function()
32914     {
32915         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
32916         
32917         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
32918         
32919         if(this.bgimage.length){
32920             var image = this.el.select('.roo-brick-image-view', true).first();
32921             
32922             image.setWidth(paragraph.getWidth());
32923             
32924             if(this.square){
32925                 image.setHeight(paragraph.getWidth());
32926             }
32927             
32928             this.el.setHeight(image.getHeight());
32929             paragraph.setHeight(image.getHeight());
32930             
32931         }
32932         
32933     },
32934     
32935     enter: function(e, el)
32936     {
32937         e.preventDefault();
32938         
32939         if(this.bgimage.length){
32940             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
32941             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
32942         }
32943     },
32944     
32945     leave: function(e, el)
32946     {
32947         e.preventDefault();
32948         
32949         if(this.bgimage.length){
32950             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
32951             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
32952         }
32953     }
32954     
32955 });
32956
32957  
32958
32959  /*
32960  * - LGPL
32961  *
32962  * Input
32963  * 
32964  */
32965
32966 /**
32967  * @class Roo.bootstrap.NumberField
32968  * @extends Roo.bootstrap.Input
32969  * Bootstrap NumberField class
32970  * 
32971  * 
32972  * 
32973  * 
32974  * @constructor
32975  * Create a new NumberField
32976  * @param {Object} config The config object
32977  */
32978
32979 Roo.bootstrap.NumberField = function(config){
32980     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
32981 };
32982
32983 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
32984     
32985     /**
32986      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
32987      */
32988     allowDecimals : true,
32989     /**
32990      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
32991      */
32992     decimalSeparator : ".",
32993     /**
32994      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
32995      */
32996     decimalPrecision : 2,
32997     /**
32998      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
32999      */
33000     allowNegative : true,
33001     /**
33002      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
33003      */
33004     minValue : Number.NEGATIVE_INFINITY,
33005     /**
33006      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
33007      */
33008     maxValue : Number.MAX_VALUE,
33009     /**
33010      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
33011      */
33012     minText : "The minimum value for this field is {0}",
33013     /**
33014      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
33015      */
33016     maxText : "The maximum value for this field is {0}",
33017     /**
33018      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
33019      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
33020      */
33021     nanText : "{0} is not a valid number",
33022     /**
33023      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
33024      */
33025     castInt : true,
33026
33027     // private
33028     initEvents : function()
33029     {   
33030         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
33031         
33032         var allowed = "0123456789";
33033         
33034         if(this.allowDecimals){
33035             allowed += this.decimalSeparator;
33036         }
33037         
33038         if(this.allowNegative){
33039             allowed += "-";
33040         }
33041         
33042         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
33043         
33044         var keyPress = function(e){
33045             
33046             var k = e.getKey();
33047             
33048             var c = e.getCharCode();
33049             
33050             if(
33051                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
33052                     allowed.indexOf(String.fromCharCode(c)) === -1
33053             ){
33054                 e.stopEvent();
33055                 return;
33056             }
33057             
33058             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
33059                 return;
33060             }
33061             
33062             if(allowed.indexOf(String.fromCharCode(c)) === -1){
33063                 e.stopEvent();
33064             }
33065         };
33066         
33067         this.el.on("keypress", keyPress, this);
33068     },
33069     
33070     validateValue : function(value)
33071     {
33072         
33073         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
33074             return false;
33075         }
33076         
33077         var num = this.parseValue(value);
33078         
33079         if(isNaN(num)){
33080             this.markInvalid(String.format(this.nanText, value));
33081             return false;
33082         }
33083         
33084         if(num < this.minValue){
33085             this.markInvalid(String.format(this.minText, this.minValue));
33086             return false;
33087         }
33088         
33089         if(num > this.maxValue){
33090             this.markInvalid(String.format(this.maxText, this.maxValue));
33091             return false;
33092         }
33093         
33094         return true;
33095     },
33096
33097     getValue : function()
33098     {
33099         return this.fixPrecision(this.parseValue(Roo.bootstrap.NumberField.superclass.getValue.call(this)));
33100     },
33101
33102     parseValue : function(value)
33103     {
33104         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
33105         return isNaN(value) ? '' : value;
33106     },
33107
33108     fixPrecision : function(value)
33109     {
33110         var nan = isNaN(value);
33111         
33112         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
33113             return nan ? '' : value;
33114         }
33115         return parseFloat(value).toFixed(this.decimalPrecision);
33116     },
33117
33118     setValue : function(v)
33119     {
33120         v = this.fixPrecision(v);
33121         Roo.bootstrap.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
33122     },
33123
33124     decimalPrecisionFcn : function(v)
33125     {
33126         return Math.floor(v);
33127     },
33128
33129     beforeBlur : function()
33130     {
33131         if(!this.castInt){
33132             return;
33133         }
33134         
33135         var v = this.parseValue(this.getRawValue());
33136         if(v){
33137             this.setValue(v);
33138         }
33139     }
33140     
33141 });
33142
33143  
33144
33145 /*
33146 * Licence: LGPL
33147 */
33148
33149 /**
33150  * @class Roo.bootstrap.DocumentSlider
33151  * @extends Roo.bootstrap.Component
33152  * Bootstrap DocumentSlider class
33153  * 
33154  * @constructor
33155  * Create a new DocumentViewer
33156  * @param {Object} config The config object
33157  */
33158
33159 Roo.bootstrap.DocumentSlider = function(config){
33160     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
33161     
33162     this.files = [];
33163     
33164     this.addEvents({
33165         /**
33166          * @event initial
33167          * Fire after initEvent
33168          * @param {Roo.bootstrap.DocumentSlider} this
33169          */
33170         "initial" : true,
33171         /**
33172          * @event update
33173          * Fire after update
33174          * @param {Roo.bootstrap.DocumentSlider} this
33175          */
33176         "update" : true,
33177         /**
33178          * @event click
33179          * Fire after click
33180          * @param {Roo.bootstrap.DocumentSlider} this
33181          */
33182         "click" : true
33183     });
33184 };
33185
33186 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
33187     
33188     files : false,
33189     
33190     indicator : 0,
33191     
33192     getAutoCreate : function()
33193     {
33194         var cfg = {
33195             tag : 'div',
33196             cls : 'roo-document-slider',
33197             cn : [
33198                 {
33199                     tag : 'div',
33200                     cls : 'roo-document-slider-header',
33201                     cn : [
33202                         {
33203                             tag : 'div',
33204                             cls : 'roo-document-slider-header-title'
33205                         }
33206                     ]
33207                 },
33208                 {
33209                     tag : 'div',
33210                     cls : 'roo-document-slider-body',
33211                     cn : [
33212                         {
33213                             tag : 'div',
33214                             cls : 'roo-document-slider-prev',
33215                             cn : [
33216                                 {
33217                                     tag : 'i',
33218                                     cls : 'fa fa-chevron-left'
33219                                 }
33220                             ]
33221                         },
33222                         {
33223                             tag : 'div',
33224                             cls : 'roo-document-slider-thumb',
33225                             cn : [
33226                                 {
33227                                     tag : 'img',
33228                                     cls : 'roo-document-slider-image'
33229                                 }
33230                             ]
33231                         },
33232                         {
33233                             tag : 'div',
33234                             cls : 'roo-document-slider-next',
33235                             cn : [
33236                                 {
33237                                     tag : 'i',
33238                                     cls : 'fa fa-chevron-right'
33239                                 }
33240                             ]
33241                         }
33242                     ]
33243                 }
33244             ]
33245         };
33246         
33247         return cfg;
33248     },
33249     
33250     initEvents : function()
33251     {
33252         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
33253         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
33254         
33255         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
33256         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
33257         
33258         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
33259         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33260         
33261         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
33262         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33263         
33264         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
33265         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33266         
33267         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
33268         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33269         
33270         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
33271         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
33272         
33273         this.thumbEl.on('click', this.onClick, this);
33274         
33275         this.prevIndicator.on('click', this.prev, this);
33276         
33277         this.nextIndicator.on('click', this.next, this);
33278         
33279     },
33280     
33281     initial : function()
33282     {
33283         if(this.files.length){
33284             this.indicator = 1;
33285             this.update()
33286         }
33287         
33288         this.fireEvent('initial', this);
33289     },
33290     
33291     update : function()
33292     {
33293         this.imageEl.attr('src', this.files[this.indicator - 1]);
33294         
33295         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
33296         
33297         this.prevIndicator.show();
33298         
33299         if(this.indicator == 1){
33300             this.prevIndicator.hide();
33301         }
33302         
33303         this.nextIndicator.show();
33304         
33305         if(this.indicator == this.files.length){
33306             this.nextIndicator.hide();
33307         }
33308         
33309         this.thumbEl.scrollTo('top');
33310         
33311         this.fireEvent('update', this);
33312     },
33313     
33314     onClick : function(e)
33315     {
33316         e.preventDefault();
33317         
33318         this.fireEvent('click', this);
33319     },
33320     
33321     prev : function(e)
33322     {
33323         e.preventDefault();
33324         
33325         this.indicator = Math.max(1, this.indicator - 1);
33326         
33327         this.update();
33328     },
33329     
33330     next : function(e)
33331     {
33332         e.preventDefault();
33333         
33334         this.indicator = Math.min(this.files.length, this.indicator + 1);
33335         
33336         this.update();
33337     }
33338 });
33339 /*
33340  * - LGPL
33341  *
33342  * RadioSet
33343  *
33344  *
33345  */
33346
33347 /**
33348  * @class Roo.bootstrap.RadioSet
33349  * @extends Roo.bootstrap.Input
33350  * Bootstrap RadioSet class
33351  * @cfg {String} indicatorpos (left|right) default left
33352  * @cfg {Boolean} inline (true|false) inline the element (default true)
33353  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
33354  * @constructor
33355  * Create a new RadioSet
33356  * @param {Object} config The config object
33357  */
33358
33359 Roo.bootstrap.RadioSet = function(config){
33360     
33361     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
33362     
33363     this.radioes = [];
33364     
33365     Roo.bootstrap.RadioSet.register(this);
33366     
33367     this.addEvents({
33368         /**
33369         * @event check
33370         * Fires when the element is checked or unchecked.
33371         * @param {Roo.bootstrap.RadioSet} this This radio
33372         * @param {Roo.bootstrap.Radio} item The checked item
33373         */
33374        check : true
33375     });
33376     
33377 };
33378
33379 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
33380
33381     radioes : false,
33382     
33383     inline : true,
33384     
33385     weight : '',
33386     
33387     indicatorpos : 'left',
33388     
33389     getAutoCreate : function()
33390     {
33391         var label = {
33392             tag : 'label',
33393             cls : 'roo-radio-set-label',
33394             cn : [
33395                 {
33396                     tag : 'span',
33397                     html : this.fieldLabel
33398                 }
33399             ]
33400         };
33401         
33402         if(this.indicatorpos == 'left'){
33403             label.cn.unshift({
33404                 tag : 'i',
33405                 cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
33406                 tooltip : 'This field is required'
33407             });
33408         } else {
33409             label.cn.push({
33410                 tag : 'i',
33411                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
33412                 tooltip : 'This field is required'
33413             });
33414         }
33415         
33416         var items = {
33417             tag : 'div',
33418             cls : 'roo-radio-set-items'
33419         };
33420         
33421         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
33422         
33423         if (align === 'left' && this.fieldLabel.length) {
33424             
33425             items = {
33426                 cls : "roo-radio-set-right", 
33427                 cn: [
33428                     items
33429                 ]
33430             };
33431             
33432             if(this.labelWidth > 12){
33433                 label.style = "width: " + this.labelWidth + 'px';
33434             }
33435             
33436             if(this.labelWidth < 13 && this.labelmd == 0){
33437                 this.labelmd = this.labelWidth;
33438             }
33439             
33440             if(this.labellg > 0){
33441                 label.cls += ' col-lg-' + this.labellg;
33442                 items.cls += ' col-lg-' + (12 - this.labellg);
33443             }
33444             
33445             if(this.labelmd > 0){
33446                 label.cls += ' col-md-' + this.labelmd;
33447                 items.cls += ' col-md-' + (12 - this.labelmd);
33448             }
33449             
33450             if(this.labelsm > 0){
33451                 label.cls += ' col-sm-' + this.labelsm;
33452                 items.cls += ' col-sm-' + (12 - this.labelsm);
33453             }
33454             
33455             if(this.labelxs > 0){
33456                 label.cls += ' col-xs-' + this.labelxs;
33457                 items.cls += ' col-xs-' + (12 - this.labelxs);
33458             }
33459         }
33460         
33461         var cfg = {
33462             tag : 'div',
33463             cls : 'roo-radio-set',
33464             cn : [
33465                 {
33466                     tag : 'input',
33467                     cls : 'roo-radio-set-input',
33468                     type : 'hidden',
33469                     name : this.name,
33470                     value : this.value ? this.value :  ''
33471                 },
33472                 label,
33473                 items
33474             ]
33475         };
33476         
33477         if(this.weight.length){
33478             cfg.cls += ' roo-radio-' + this.weight;
33479         }
33480         
33481         if(this.inline) {
33482             cfg.cls += ' roo-radio-set-inline';
33483         }
33484         
33485         var settings=this;
33486         ['xs','sm','md','lg'].map(function(size){
33487             if (settings[size]) {
33488                 cfg.cls += ' col-' + size + '-' + settings[size];
33489             }
33490         });
33491         
33492         return cfg;
33493         
33494     },
33495
33496     initEvents : function()
33497     {
33498         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
33499         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
33500         
33501         if(!this.fieldLabel.length){
33502             this.labelEl.hide();
33503         }
33504         
33505         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
33506         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
33507         
33508         this.indicatorEl().addClass('invisible');
33509         
33510         this.originalValue = this.getValue();
33511         
33512     },
33513     
33514     inputEl: function ()
33515     {
33516         return this.el.select('.roo-radio-set-input', true).first();
33517     },
33518     
33519     getChildContainer : function()
33520     {
33521         return this.itemsEl;
33522     },
33523     
33524     register : function(item)
33525     {
33526         this.radioes.push(item);
33527         
33528     },
33529     
33530     validate : function()
33531     {   
33532         var valid = false;
33533         
33534         Roo.each(this.radioes, function(i){
33535             if(!i.checked){
33536                 return;
33537             }
33538             
33539             valid = true;
33540             return false;
33541         });
33542         
33543         if(this.allowBlank) {
33544             return true;
33545         }
33546         
33547         if(this.disabled || valid){
33548             this.markValid();
33549             return true;
33550         }
33551         
33552         this.markInvalid();
33553         return false;
33554         
33555     },
33556     
33557     markValid : function()
33558     {
33559         if(this.labelEl.isVisible(true)){
33560             this.indicatorEl().removeClass('visible');
33561             this.indicatorEl().addClass('invisible');
33562         }
33563         
33564         this.el.removeClass([this.invalidClass, this.validClass]);
33565         this.el.addClass(this.validClass);
33566         
33567         this.fireEvent('valid', this);
33568     },
33569     
33570     markInvalid : function(msg)
33571     {
33572         if(this.allowBlank || this.disabled){
33573             return;
33574         }
33575         
33576         if(this.labelEl.isVisible(true)){
33577             this.indicatorEl().removeClass('invisible');
33578             this.indicatorEl().addClass('visible');
33579         }
33580         
33581         this.el.removeClass([this.invalidClass, this.validClass]);
33582         this.el.addClass(this.invalidClass);
33583         
33584         this.fireEvent('invalid', this, msg);
33585         
33586     },
33587     
33588     setValue : function(v, suppressEvent)
33589     {   
33590         if(this.value === v){
33591             return;
33592         }
33593         
33594         this.value = v;
33595         
33596         if(this.rendered){
33597             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
33598         }
33599         
33600         Roo.each(this.radioes, function(i){
33601             
33602             i.checked = false;
33603             i.el.removeClass('checked');
33604             
33605             if(i.value === v || i.value.toString() === v.toString()){
33606                 i.checked = true;
33607                 i.el.addClass('checked');
33608                 
33609                 if(suppressEvent !== true){
33610                     this.fireEvent('check', this, i);
33611                 }
33612             }
33613             
33614         }, this);
33615         
33616         this.validate();
33617     },
33618     
33619     clearInvalid : function(){
33620         
33621         if(!this.el || this.preventMark){
33622             return;
33623         }
33624         
33625         this.el.removeClass([this.invalidClass]);
33626         
33627         this.fireEvent('valid', this);
33628     }
33629     
33630 });
33631
33632 Roo.apply(Roo.bootstrap.RadioSet, {
33633     
33634     groups: {},
33635     
33636     register : function(set)
33637     {
33638         this.groups[set.name] = set;
33639     },
33640     
33641     get: function(name) 
33642     {
33643         if (typeof(this.groups[name]) == 'undefined') {
33644             return false;
33645         }
33646         
33647         return this.groups[name] ;
33648     }
33649     
33650 });
33651 /*
33652  * Based on:
33653  * Ext JS Library 1.1.1
33654  * Copyright(c) 2006-2007, Ext JS, LLC.
33655  *
33656  * Originally Released Under LGPL - original licence link has changed is not relivant.
33657  *
33658  * Fork - LGPL
33659  * <script type="text/javascript">
33660  */
33661
33662
33663 /**
33664  * @class Roo.bootstrap.SplitBar
33665  * @extends Roo.util.Observable
33666  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
33667  * <br><br>
33668  * Usage:
33669  * <pre><code>
33670 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
33671                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
33672 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
33673 split.minSize = 100;
33674 split.maxSize = 600;
33675 split.animate = true;
33676 split.on('moved', splitterMoved);
33677 </code></pre>
33678  * @constructor
33679  * Create a new SplitBar
33680  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
33681  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
33682  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33683  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
33684                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
33685                         position of the SplitBar).
33686  */
33687 Roo.bootstrap.SplitBar = function(cfg){
33688     
33689     /** @private */
33690     
33691     //{
33692     //  dragElement : elm
33693     //  resizingElement: el,
33694         // optional..
33695     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
33696     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
33697         // existingProxy ???
33698     //}
33699     
33700     this.el = Roo.get(cfg.dragElement, true);
33701     this.el.dom.unselectable = "on";
33702     /** @private */
33703     this.resizingEl = Roo.get(cfg.resizingElement, true);
33704
33705     /**
33706      * @private
33707      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
33708      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
33709      * @type Number
33710      */
33711     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
33712     
33713     /**
33714      * The minimum size of the resizing element. (Defaults to 0)
33715      * @type Number
33716      */
33717     this.minSize = 0;
33718     
33719     /**
33720      * The maximum size of the resizing element. (Defaults to 2000)
33721      * @type Number
33722      */
33723     this.maxSize = 2000;
33724     
33725     /**
33726      * Whether to animate the transition to the new size
33727      * @type Boolean
33728      */
33729     this.animate = false;
33730     
33731     /**
33732      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
33733      * @type Boolean
33734      */
33735     this.useShim = false;
33736     
33737     /** @private */
33738     this.shim = null;
33739     
33740     if(!cfg.existingProxy){
33741         /** @private */
33742         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
33743     }else{
33744         this.proxy = Roo.get(cfg.existingProxy).dom;
33745     }
33746     /** @private */
33747     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
33748     
33749     /** @private */
33750     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
33751     
33752     /** @private */
33753     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
33754     
33755     /** @private */
33756     this.dragSpecs = {};
33757     
33758     /**
33759      * @private The adapter to use to positon and resize elements
33760      */
33761     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
33762     this.adapter.init(this);
33763     
33764     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33765         /** @private */
33766         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
33767         this.el.addClass("roo-splitbar-h");
33768     }else{
33769         /** @private */
33770         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
33771         this.el.addClass("roo-splitbar-v");
33772     }
33773     
33774     this.addEvents({
33775         /**
33776          * @event resize
33777          * Fires when the splitter is moved (alias for {@link #event-moved})
33778          * @param {Roo.bootstrap.SplitBar} this
33779          * @param {Number} newSize the new width or height
33780          */
33781         "resize" : true,
33782         /**
33783          * @event moved
33784          * Fires when the splitter is moved
33785          * @param {Roo.bootstrap.SplitBar} this
33786          * @param {Number} newSize the new width or height
33787          */
33788         "moved" : true,
33789         /**
33790          * @event beforeresize
33791          * Fires before the splitter is dragged
33792          * @param {Roo.bootstrap.SplitBar} this
33793          */
33794         "beforeresize" : true,
33795
33796         "beforeapply" : true
33797     });
33798
33799     Roo.util.Observable.call(this);
33800 };
33801
33802 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
33803     onStartProxyDrag : function(x, y){
33804         this.fireEvent("beforeresize", this);
33805         if(!this.overlay){
33806             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
33807             o.unselectable();
33808             o.enableDisplayMode("block");
33809             // all splitbars share the same overlay
33810             Roo.bootstrap.SplitBar.prototype.overlay = o;
33811         }
33812         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33813         this.overlay.show();
33814         Roo.get(this.proxy).setDisplayed("block");
33815         var size = this.adapter.getElementSize(this);
33816         this.activeMinSize = this.getMinimumSize();;
33817         this.activeMaxSize = this.getMaximumSize();;
33818         var c1 = size - this.activeMinSize;
33819         var c2 = Math.max(this.activeMaxSize - size, 0);
33820         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33821             this.dd.resetConstraints();
33822             this.dd.setXConstraint(
33823                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
33824                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
33825             );
33826             this.dd.setYConstraint(0, 0);
33827         }else{
33828             this.dd.resetConstraints();
33829             this.dd.setXConstraint(0, 0);
33830             this.dd.setYConstraint(
33831                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
33832                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
33833             );
33834          }
33835         this.dragSpecs.startSize = size;
33836         this.dragSpecs.startPoint = [x, y];
33837         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
33838     },
33839     
33840     /** 
33841      * @private Called after the drag operation by the DDProxy
33842      */
33843     onEndProxyDrag : function(e){
33844         Roo.get(this.proxy).setDisplayed(false);
33845         var endPoint = Roo.lib.Event.getXY(e);
33846         if(this.overlay){
33847             this.overlay.hide();
33848         }
33849         var newSize;
33850         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33851             newSize = this.dragSpecs.startSize + 
33852                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
33853                     endPoint[0] - this.dragSpecs.startPoint[0] :
33854                     this.dragSpecs.startPoint[0] - endPoint[0]
33855                 );
33856         }else{
33857             newSize = this.dragSpecs.startSize + 
33858                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
33859                     endPoint[1] - this.dragSpecs.startPoint[1] :
33860                     this.dragSpecs.startPoint[1] - endPoint[1]
33861                 );
33862         }
33863         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
33864         if(newSize != this.dragSpecs.startSize){
33865             if(this.fireEvent('beforeapply', this, newSize) !== false){
33866                 this.adapter.setElementSize(this, newSize);
33867                 this.fireEvent("moved", this, newSize);
33868                 this.fireEvent("resize", this, newSize);
33869             }
33870         }
33871     },
33872     
33873     /**
33874      * Get the adapter this SplitBar uses
33875      * @return The adapter object
33876      */
33877     getAdapter : function(){
33878         return this.adapter;
33879     },
33880     
33881     /**
33882      * Set the adapter this SplitBar uses
33883      * @param {Object} adapter A SplitBar adapter object
33884      */
33885     setAdapter : function(adapter){
33886         this.adapter = adapter;
33887         this.adapter.init(this);
33888     },
33889     
33890     /**
33891      * Gets the minimum size for the resizing element
33892      * @return {Number} The minimum size
33893      */
33894     getMinimumSize : function(){
33895         return this.minSize;
33896     },
33897     
33898     /**
33899      * Sets the minimum size for the resizing element
33900      * @param {Number} minSize The minimum size
33901      */
33902     setMinimumSize : function(minSize){
33903         this.minSize = minSize;
33904     },
33905     
33906     /**
33907      * Gets the maximum size for the resizing element
33908      * @return {Number} The maximum size
33909      */
33910     getMaximumSize : function(){
33911         return this.maxSize;
33912     },
33913     
33914     /**
33915      * Sets the maximum size for the resizing element
33916      * @param {Number} maxSize The maximum size
33917      */
33918     setMaximumSize : function(maxSize){
33919         this.maxSize = maxSize;
33920     },
33921     
33922     /**
33923      * Sets the initialize size for the resizing element
33924      * @param {Number} size The initial size
33925      */
33926     setCurrentSize : function(size){
33927         var oldAnimate = this.animate;
33928         this.animate = false;
33929         this.adapter.setElementSize(this, size);
33930         this.animate = oldAnimate;
33931     },
33932     
33933     /**
33934      * Destroy this splitbar. 
33935      * @param {Boolean} removeEl True to remove the element
33936      */
33937     destroy : function(removeEl){
33938         if(this.shim){
33939             this.shim.remove();
33940         }
33941         this.dd.unreg();
33942         this.proxy.parentNode.removeChild(this.proxy);
33943         if(removeEl){
33944             this.el.remove();
33945         }
33946     }
33947 });
33948
33949 /**
33950  * @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.
33951  */
33952 Roo.bootstrap.SplitBar.createProxy = function(dir){
33953     var proxy = new Roo.Element(document.createElement("div"));
33954     proxy.unselectable();
33955     var cls = 'roo-splitbar-proxy';
33956     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
33957     document.body.appendChild(proxy.dom);
33958     return proxy.dom;
33959 };
33960
33961 /** 
33962  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
33963  * Default Adapter. It assumes the splitter and resizing element are not positioned
33964  * elements and only gets/sets the width of the element. Generally used for table based layouts.
33965  */
33966 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
33967 };
33968
33969 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
33970     // do nothing for now
33971     init : function(s){
33972     
33973     },
33974     /**
33975      * Called before drag operations to get the current size of the resizing element. 
33976      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33977      */
33978      getElementSize : function(s){
33979         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33980             return s.resizingEl.getWidth();
33981         }else{
33982             return s.resizingEl.getHeight();
33983         }
33984     },
33985     
33986     /**
33987      * Called after drag operations to set the size of the resizing element.
33988      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
33989      * @param {Number} newSize The new size to set
33990      * @param {Function} onComplete A function to be invoked when resizing is complete
33991      */
33992     setElementSize : function(s, newSize, onComplete){
33993         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
33994             if(!s.animate){
33995                 s.resizingEl.setWidth(newSize);
33996                 if(onComplete){
33997                     onComplete(s, newSize);
33998                 }
33999             }else{
34000                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
34001             }
34002         }else{
34003             
34004             if(!s.animate){
34005                 s.resizingEl.setHeight(newSize);
34006                 if(onComplete){
34007                     onComplete(s, newSize);
34008                 }
34009             }else{
34010                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
34011             }
34012         }
34013     }
34014 };
34015
34016 /** 
34017  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
34018  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
34019  * Adapter that  moves the splitter element to align with the resized sizing element. 
34020  * Used with an absolute positioned SplitBar.
34021  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
34022  * document.body, make sure you assign an id to the body element.
34023  */
34024 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
34025     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
34026     this.container = Roo.get(container);
34027 };
34028
34029 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
34030     init : function(s){
34031         this.basic.init(s);
34032     },
34033     
34034     getElementSize : function(s){
34035         return this.basic.getElementSize(s);
34036     },
34037     
34038     setElementSize : function(s, newSize, onComplete){
34039         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
34040     },
34041     
34042     moveSplitter : function(s){
34043         var yes = Roo.bootstrap.SplitBar;
34044         switch(s.placement){
34045             case yes.LEFT:
34046                 s.el.setX(s.resizingEl.getRight());
34047                 break;
34048             case yes.RIGHT:
34049                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
34050                 break;
34051             case yes.TOP:
34052                 s.el.setY(s.resizingEl.getBottom());
34053                 break;
34054             case yes.BOTTOM:
34055                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
34056                 break;
34057         }
34058     }
34059 };
34060
34061 /**
34062  * Orientation constant - Create a vertical SplitBar
34063  * @static
34064  * @type Number
34065  */
34066 Roo.bootstrap.SplitBar.VERTICAL = 1;
34067
34068 /**
34069  * Orientation constant - Create a horizontal SplitBar
34070  * @static
34071  * @type Number
34072  */
34073 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
34074
34075 /**
34076  * Placement constant - The resizing element is to the left of the splitter element
34077  * @static
34078  * @type Number
34079  */
34080 Roo.bootstrap.SplitBar.LEFT = 1;
34081
34082 /**
34083  * Placement constant - The resizing element is to the right of the splitter element
34084  * @static
34085  * @type Number
34086  */
34087 Roo.bootstrap.SplitBar.RIGHT = 2;
34088
34089 /**
34090  * Placement constant - The resizing element is positioned above the splitter element
34091  * @static
34092  * @type Number
34093  */
34094 Roo.bootstrap.SplitBar.TOP = 3;
34095
34096 /**
34097  * Placement constant - The resizing element is positioned under splitter element
34098  * @static
34099  * @type Number
34100  */
34101 Roo.bootstrap.SplitBar.BOTTOM = 4;
34102 Roo.namespace("Roo.bootstrap.layout");/*
34103  * Based on:
34104  * Ext JS Library 1.1.1
34105  * Copyright(c) 2006-2007, Ext JS, LLC.
34106  *
34107  * Originally Released Under LGPL - original licence link has changed is not relivant.
34108  *
34109  * Fork - LGPL
34110  * <script type="text/javascript">
34111  */
34112
34113 /**
34114  * @class Roo.bootstrap.layout.Manager
34115  * @extends Roo.bootstrap.Component
34116  * Base class for layout managers.
34117  */
34118 Roo.bootstrap.layout.Manager = function(config)
34119 {
34120     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
34121
34122
34123
34124
34125
34126     /** false to disable window resize monitoring @type Boolean */
34127     this.monitorWindowResize = true;
34128     this.regions = {};
34129     this.addEvents({
34130         /**
34131          * @event layout
34132          * Fires when a layout is performed.
34133          * @param {Roo.LayoutManager} this
34134          */
34135         "layout" : true,
34136         /**
34137          * @event regionresized
34138          * Fires when the user resizes a region.
34139          * @param {Roo.LayoutRegion} region The resized region
34140          * @param {Number} newSize The new size (width for east/west, height for north/south)
34141          */
34142         "regionresized" : true,
34143         /**
34144          * @event regioncollapsed
34145          * Fires when a region is collapsed.
34146          * @param {Roo.LayoutRegion} region The collapsed region
34147          */
34148         "regioncollapsed" : true,
34149         /**
34150          * @event regionexpanded
34151          * Fires when a region is expanded.
34152          * @param {Roo.LayoutRegion} region The expanded region
34153          */
34154         "regionexpanded" : true
34155     });
34156     this.updating = false;
34157
34158     if (config.el) {
34159         this.el = Roo.get(config.el);
34160         this.initEvents();
34161     }
34162
34163 };
34164
34165 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
34166
34167
34168     regions : null,
34169
34170     monitorWindowResize : true,
34171
34172
34173     updating : false,
34174
34175
34176     onRender : function(ct, position)
34177     {
34178         if(!this.el){
34179             this.el = Roo.get(ct);
34180             this.initEvents();
34181         }
34182         //this.fireEvent('render',this);
34183     },
34184
34185
34186     initEvents: function()
34187     {
34188
34189
34190         // ie scrollbar fix
34191         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
34192             document.body.scroll = "no";
34193         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
34194             this.el.position('relative');
34195         }
34196         this.id = this.el.id;
34197         this.el.addClass("roo-layout-container");
34198         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
34199         if(this.el.dom != document.body ) {
34200             this.el.on('resize', this.layout,this);
34201             this.el.on('show', this.layout,this);
34202         }
34203
34204     },
34205
34206     /**
34207      * Returns true if this layout is currently being updated
34208      * @return {Boolean}
34209      */
34210     isUpdating : function(){
34211         return this.updating;
34212     },
34213
34214     /**
34215      * Suspend the LayoutManager from doing auto-layouts while
34216      * making multiple add or remove calls
34217      */
34218     beginUpdate : function(){
34219         this.updating = true;
34220     },
34221
34222     /**
34223      * Restore auto-layouts and optionally disable the manager from performing a layout
34224      * @param {Boolean} noLayout true to disable a layout update
34225      */
34226     endUpdate : function(noLayout){
34227         this.updating = false;
34228         if(!noLayout){
34229             this.layout();
34230         }
34231     },
34232
34233     layout: function(){
34234         // abstract...
34235     },
34236
34237     onRegionResized : function(region, newSize){
34238         this.fireEvent("regionresized", region, newSize);
34239         this.layout();
34240     },
34241
34242     onRegionCollapsed : function(region){
34243         this.fireEvent("regioncollapsed", region);
34244     },
34245
34246     onRegionExpanded : function(region){
34247         this.fireEvent("regionexpanded", region);
34248     },
34249
34250     /**
34251      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
34252      * performs box-model adjustments.
34253      * @return {Object} The size as an object {width: (the width), height: (the height)}
34254      */
34255     getViewSize : function()
34256     {
34257         var size;
34258         if(this.el.dom != document.body){
34259             size = this.el.getSize();
34260         }else{
34261             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
34262         }
34263         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
34264         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
34265         return size;
34266     },
34267
34268     /**
34269      * Returns the Element this layout is bound to.
34270      * @return {Roo.Element}
34271      */
34272     getEl : function(){
34273         return this.el;
34274     },
34275
34276     /**
34277      * Returns the specified region.
34278      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
34279      * @return {Roo.LayoutRegion}
34280      */
34281     getRegion : function(target){
34282         return this.regions[target.toLowerCase()];
34283     },
34284
34285     onWindowResize : function(){
34286         if(this.monitorWindowResize){
34287             this.layout();
34288         }
34289     }
34290 });
34291 /*
34292  * Based on:
34293  * Ext JS Library 1.1.1
34294  * Copyright(c) 2006-2007, Ext JS, LLC.
34295  *
34296  * Originally Released Under LGPL - original licence link has changed is not relivant.
34297  *
34298  * Fork - LGPL
34299  * <script type="text/javascript">
34300  */
34301 /**
34302  * @class Roo.bootstrap.layout.Border
34303  * @extends Roo.bootstrap.layout.Manager
34304  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
34305  * please see: examples/bootstrap/nested.html<br><br>
34306  
34307 <b>The container the layout is rendered into can be either the body element or any other element.
34308 If it is not the body element, the container needs to either be an absolute positioned element,
34309 or you will need to add "position:relative" to the css of the container.  You will also need to specify
34310 the container size if it is not the body element.</b>
34311
34312 * @constructor
34313 * Create a new Border
34314 * @param {Object} config Configuration options
34315  */
34316 Roo.bootstrap.layout.Border = function(config){
34317     config = config || {};
34318     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
34319     
34320     
34321     
34322     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34323         if(config[region]){
34324             config[region].region = region;
34325             this.addRegion(config[region]);
34326         }
34327     },this);
34328     
34329 };
34330
34331 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
34332
34333 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
34334     /**
34335      * Creates and adds a new region if it doesn't already exist.
34336      * @param {String} target The target region key (north, south, east, west or center).
34337      * @param {Object} config The regions config object
34338      * @return {BorderLayoutRegion} The new region
34339      */
34340     addRegion : function(config)
34341     {
34342         if(!this.regions[config.region]){
34343             var r = this.factory(config);
34344             this.bindRegion(r);
34345         }
34346         return this.regions[config.region];
34347     },
34348
34349     // private (kinda)
34350     bindRegion : function(r){
34351         this.regions[r.config.region] = r;
34352         
34353         r.on("visibilitychange",    this.layout, this);
34354         r.on("paneladded",          this.layout, this);
34355         r.on("panelremoved",        this.layout, this);
34356         r.on("invalidated",         this.layout, this);
34357         r.on("resized",             this.onRegionResized, this);
34358         r.on("collapsed",           this.onRegionCollapsed, this);
34359         r.on("expanded",            this.onRegionExpanded, this);
34360     },
34361
34362     /**
34363      * Performs a layout update.
34364      */
34365     layout : function()
34366     {
34367         if(this.updating) {
34368             return;
34369         }
34370         
34371         // render all the rebions if they have not been done alreayd?
34372         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
34373             if(this.regions[region] && !this.regions[region].bodyEl){
34374                 this.regions[region].onRender(this.el)
34375             }
34376         },this);
34377         
34378         var size = this.getViewSize();
34379         var w = size.width;
34380         var h = size.height;
34381         var centerW = w;
34382         var centerH = h;
34383         var centerY = 0;
34384         var centerX = 0;
34385         //var x = 0, y = 0;
34386
34387         var rs = this.regions;
34388         var north = rs["north"];
34389         var south = rs["south"]; 
34390         var west = rs["west"];
34391         var east = rs["east"];
34392         var center = rs["center"];
34393         //if(this.hideOnLayout){ // not supported anymore
34394             //c.el.setStyle("display", "none");
34395         //}
34396         if(north && north.isVisible()){
34397             var b = north.getBox();
34398             var m = north.getMargins();
34399             b.width = w - (m.left+m.right);
34400             b.x = m.left;
34401             b.y = m.top;
34402             centerY = b.height + b.y + m.bottom;
34403             centerH -= centerY;
34404             north.updateBox(this.safeBox(b));
34405         }
34406         if(south && south.isVisible()){
34407             var b = south.getBox();
34408             var m = south.getMargins();
34409             b.width = w - (m.left+m.right);
34410             b.x = m.left;
34411             var totalHeight = (b.height + m.top + m.bottom);
34412             b.y = h - totalHeight + m.top;
34413             centerH -= totalHeight;
34414             south.updateBox(this.safeBox(b));
34415         }
34416         if(west && west.isVisible()){
34417             var b = west.getBox();
34418             var m = west.getMargins();
34419             b.height = centerH - (m.top+m.bottom);
34420             b.x = m.left;
34421             b.y = centerY + m.top;
34422             var totalWidth = (b.width + m.left + m.right);
34423             centerX += totalWidth;
34424             centerW -= totalWidth;
34425             west.updateBox(this.safeBox(b));
34426         }
34427         if(east && east.isVisible()){
34428             var b = east.getBox();
34429             var m = east.getMargins();
34430             b.height = centerH - (m.top+m.bottom);
34431             var totalWidth = (b.width + m.left + m.right);
34432             b.x = w - totalWidth + m.left;
34433             b.y = centerY + m.top;
34434             centerW -= totalWidth;
34435             east.updateBox(this.safeBox(b));
34436         }
34437         if(center){
34438             var m = center.getMargins();
34439             var centerBox = {
34440                 x: centerX + m.left,
34441                 y: centerY + m.top,
34442                 width: centerW - (m.left+m.right),
34443                 height: centerH - (m.top+m.bottom)
34444             };
34445             //if(this.hideOnLayout){
34446                 //center.el.setStyle("display", "block");
34447             //}
34448             center.updateBox(this.safeBox(centerBox));
34449         }
34450         this.el.repaint();
34451         this.fireEvent("layout", this);
34452     },
34453
34454     // private
34455     safeBox : function(box){
34456         box.width = Math.max(0, box.width);
34457         box.height = Math.max(0, box.height);
34458         return box;
34459     },
34460
34461     /**
34462      * Adds a ContentPanel (or subclass) to this layout.
34463      * @param {String} target The target region key (north, south, east, west or center).
34464      * @param {Roo.ContentPanel} panel The panel to add
34465      * @return {Roo.ContentPanel} The added panel
34466      */
34467     add : function(target, panel){
34468          
34469         target = target.toLowerCase();
34470         return this.regions[target].add(panel);
34471     },
34472
34473     /**
34474      * Remove a ContentPanel (or subclass) to this layout.
34475      * @param {String} target The target region key (north, south, east, west or center).
34476      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
34477      * @return {Roo.ContentPanel} The removed panel
34478      */
34479     remove : function(target, panel){
34480         target = target.toLowerCase();
34481         return this.regions[target].remove(panel);
34482     },
34483
34484     /**
34485      * Searches all regions for a panel with the specified id
34486      * @param {String} panelId
34487      * @return {Roo.ContentPanel} The panel or null if it wasn't found
34488      */
34489     findPanel : function(panelId){
34490         var rs = this.regions;
34491         for(var target in rs){
34492             if(typeof rs[target] != "function"){
34493                 var p = rs[target].getPanel(panelId);
34494                 if(p){
34495                     return p;
34496                 }
34497             }
34498         }
34499         return null;
34500     },
34501
34502     /**
34503      * Searches all regions for a panel with the specified id and activates (shows) it.
34504      * @param {String/ContentPanel} panelId The panels id or the panel itself
34505      * @return {Roo.ContentPanel} The shown panel or null
34506      */
34507     showPanel : function(panelId) {
34508       var rs = this.regions;
34509       for(var target in rs){
34510          var r = rs[target];
34511          if(typeof r != "function"){
34512             if(r.hasPanel(panelId)){
34513                return r.showPanel(panelId);
34514             }
34515          }
34516       }
34517       return null;
34518    },
34519
34520    /**
34521      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
34522      * @param {Roo.state.Provider} provider (optional) An alternate state provider
34523      */
34524    /*
34525     restoreState : function(provider){
34526         if(!provider){
34527             provider = Roo.state.Manager;
34528         }
34529         var sm = new Roo.LayoutStateManager();
34530         sm.init(this, provider);
34531     },
34532 */
34533  
34534  
34535     /**
34536      * Adds a xtype elements to the layout.
34537      * <pre><code>
34538
34539 layout.addxtype({
34540        xtype : 'ContentPanel',
34541        region: 'west',
34542        items: [ .... ]
34543    }
34544 );
34545
34546 layout.addxtype({
34547         xtype : 'NestedLayoutPanel',
34548         region: 'west',
34549         layout: {
34550            center: { },
34551            west: { }   
34552         },
34553         items : [ ... list of content panels or nested layout panels.. ]
34554    }
34555 );
34556 </code></pre>
34557      * @param {Object} cfg Xtype definition of item to add.
34558      */
34559     addxtype : function(cfg)
34560     {
34561         // basically accepts a pannel...
34562         // can accept a layout region..!?!?
34563         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
34564         
34565         
34566         // theory?  children can only be panels??
34567         
34568         //if (!cfg.xtype.match(/Panel$/)) {
34569         //    return false;
34570         //}
34571         var ret = false;
34572         
34573         if (typeof(cfg.region) == 'undefined') {
34574             Roo.log("Failed to add Panel, region was not set");
34575             Roo.log(cfg);
34576             return false;
34577         }
34578         var region = cfg.region;
34579         delete cfg.region;
34580         
34581           
34582         var xitems = [];
34583         if (cfg.items) {
34584             xitems = cfg.items;
34585             delete cfg.items;
34586         }
34587         var nb = false;
34588         
34589         switch(cfg.xtype) 
34590         {
34591             case 'Content':  // ContentPanel (el, cfg)
34592             case 'Scroll':  // ContentPanel (el, cfg)
34593             case 'View': 
34594                 cfg.autoCreate = true;
34595                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34596                 //} else {
34597                 //    var el = this.el.createChild();
34598                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
34599                 //}
34600                 
34601                 this.add(region, ret);
34602                 break;
34603             
34604             /*
34605             case 'TreePanel': // our new panel!
34606                 cfg.el = this.el.createChild();
34607                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34608                 this.add(region, ret);
34609                 break;
34610             */
34611             
34612             case 'Nest': 
34613                 // create a new Layout (which is  a Border Layout...
34614                 
34615                 var clayout = cfg.layout;
34616                 clayout.el  = this.el.createChild();
34617                 clayout.items   = clayout.items  || [];
34618                 
34619                 delete cfg.layout;
34620                 
34621                 // replace this exitems with the clayout ones..
34622                 xitems = clayout.items;
34623                  
34624                 // force background off if it's in center...
34625                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
34626                     cfg.background = false;
34627                 }
34628                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
34629                 
34630                 
34631                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34632                 //console.log('adding nested layout panel '  + cfg.toSource());
34633                 this.add(region, ret);
34634                 nb = {}; /// find first...
34635                 break;
34636             
34637             case 'Grid':
34638                 
34639                 // needs grid and region
34640                 
34641                 //var el = this.getRegion(region).el.createChild();
34642                 /*
34643                  *var el = this.el.createChild();
34644                 // create the grid first...
34645                 cfg.grid.container = el;
34646                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
34647                 */
34648                 
34649                 if (region == 'center' && this.active ) {
34650                     cfg.background = false;
34651                 }
34652                 
34653                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
34654                 
34655                 this.add(region, ret);
34656                 /*
34657                 if (cfg.background) {
34658                     // render grid on panel activation (if panel background)
34659                     ret.on('activate', function(gp) {
34660                         if (!gp.grid.rendered) {
34661                     //        gp.grid.render(el);
34662                         }
34663                     });
34664                 } else {
34665                   //  cfg.grid.render(el);
34666                 }
34667                 */
34668                 break;
34669            
34670            
34671             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
34672                 // it was the old xcomponent building that caused this before.
34673                 // espeically if border is the top element in the tree.
34674                 ret = this;
34675                 break; 
34676                 
34677                     
34678                 
34679                 
34680                 
34681             default:
34682                 /*
34683                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
34684                     
34685                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
34686                     this.add(region, ret);
34687                 } else {
34688                 */
34689                     Roo.log(cfg);
34690                     throw "Can not add '" + cfg.xtype + "' to Border";
34691                     return null;
34692              
34693                                 
34694              
34695         }
34696         this.beginUpdate();
34697         // add children..
34698         var region = '';
34699         var abn = {};
34700         Roo.each(xitems, function(i)  {
34701             region = nb && i.region ? i.region : false;
34702             
34703             var add = ret.addxtype(i);
34704            
34705             if (region) {
34706                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
34707                 if (!i.background) {
34708                     abn[region] = nb[region] ;
34709                 }
34710             }
34711             
34712         });
34713         this.endUpdate();
34714
34715         // make the last non-background panel active..
34716         //if (nb) { Roo.log(abn); }
34717         if (nb) {
34718             
34719             for(var r in abn) {
34720                 region = this.getRegion(r);
34721                 if (region) {
34722                     // tried using nb[r], but it does not work..
34723                      
34724                     region.showPanel(abn[r]);
34725                    
34726                 }
34727             }
34728         }
34729         return ret;
34730         
34731     },
34732     
34733     
34734 // private
34735     factory : function(cfg)
34736     {
34737         
34738         var validRegions = Roo.bootstrap.layout.Border.regions;
34739
34740         var target = cfg.region;
34741         cfg.mgr = this;
34742         
34743         var r = Roo.bootstrap.layout;
34744         Roo.log(target);
34745         switch(target){
34746             case "north":
34747                 return new r.North(cfg);
34748             case "south":
34749                 return new r.South(cfg);
34750             case "east":
34751                 return new r.East(cfg);
34752             case "west":
34753                 return new r.West(cfg);
34754             case "center":
34755                 return new r.Center(cfg);
34756         }
34757         throw 'Layout region "'+target+'" not supported.';
34758     }
34759     
34760     
34761 });
34762  /*
34763  * Based on:
34764  * Ext JS Library 1.1.1
34765  * Copyright(c) 2006-2007, Ext JS, LLC.
34766  *
34767  * Originally Released Under LGPL - original licence link has changed is not relivant.
34768  *
34769  * Fork - LGPL
34770  * <script type="text/javascript">
34771  */
34772  
34773 /**
34774  * @class Roo.bootstrap.layout.Basic
34775  * @extends Roo.util.Observable
34776  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
34777  * and does not have a titlebar, tabs or any other features. All it does is size and position 
34778  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
34779  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
34780  * @cfg {string}   region  the region that it inhabits..
34781  * @cfg {bool}   skipConfig skip config?
34782  * 
34783
34784  */
34785 Roo.bootstrap.layout.Basic = function(config){
34786     
34787     this.mgr = config.mgr;
34788     
34789     this.position = config.region;
34790     
34791     var skipConfig = config.skipConfig;
34792     
34793     this.events = {
34794         /**
34795          * @scope Roo.BasicLayoutRegion
34796          */
34797         
34798         /**
34799          * @event beforeremove
34800          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
34801          * @param {Roo.LayoutRegion} this
34802          * @param {Roo.ContentPanel} panel The panel
34803          * @param {Object} e The cancel event object
34804          */
34805         "beforeremove" : true,
34806         /**
34807          * @event invalidated
34808          * Fires when the layout for this region is changed.
34809          * @param {Roo.LayoutRegion} this
34810          */
34811         "invalidated" : true,
34812         /**
34813          * @event visibilitychange
34814          * Fires when this region is shown or hidden 
34815          * @param {Roo.LayoutRegion} this
34816          * @param {Boolean} visibility true or false
34817          */
34818         "visibilitychange" : true,
34819         /**
34820          * @event paneladded
34821          * Fires when a panel is added. 
34822          * @param {Roo.LayoutRegion} this
34823          * @param {Roo.ContentPanel} panel The panel
34824          */
34825         "paneladded" : true,
34826         /**
34827          * @event panelremoved
34828          * Fires when a panel is removed. 
34829          * @param {Roo.LayoutRegion} this
34830          * @param {Roo.ContentPanel} panel The panel
34831          */
34832         "panelremoved" : true,
34833         /**
34834          * @event beforecollapse
34835          * Fires when this region before collapse.
34836          * @param {Roo.LayoutRegion} this
34837          */
34838         "beforecollapse" : true,
34839         /**
34840          * @event collapsed
34841          * Fires when this region is collapsed.
34842          * @param {Roo.LayoutRegion} this
34843          */
34844         "collapsed" : true,
34845         /**
34846          * @event expanded
34847          * Fires when this region is expanded.
34848          * @param {Roo.LayoutRegion} this
34849          */
34850         "expanded" : true,
34851         /**
34852          * @event slideshow
34853          * Fires when this region is slid into view.
34854          * @param {Roo.LayoutRegion} this
34855          */
34856         "slideshow" : true,
34857         /**
34858          * @event slidehide
34859          * Fires when this region slides out of view. 
34860          * @param {Roo.LayoutRegion} this
34861          */
34862         "slidehide" : true,
34863         /**
34864          * @event panelactivated
34865          * Fires when a panel is activated. 
34866          * @param {Roo.LayoutRegion} this
34867          * @param {Roo.ContentPanel} panel The activated panel
34868          */
34869         "panelactivated" : true,
34870         /**
34871          * @event resized
34872          * Fires when the user resizes this region. 
34873          * @param {Roo.LayoutRegion} this
34874          * @param {Number} newSize The new size (width for east/west, height for north/south)
34875          */
34876         "resized" : true
34877     };
34878     /** A collection of panels in this region. @type Roo.util.MixedCollection */
34879     this.panels = new Roo.util.MixedCollection();
34880     this.panels.getKey = this.getPanelId.createDelegate(this);
34881     this.box = null;
34882     this.activePanel = null;
34883     // ensure listeners are added...
34884     
34885     if (config.listeners || config.events) {
34886         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
34887             listeners : config.listeners || {},
34888             events : config.events || {}
34889         });
34890     }
34891     
34892     if(skipConfig !== true){
34893         this.applyConfig(config);
34894     }
34895 };
34896
34897 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
34898 {
34899     getPanelId : function(p){
34900         return p.getId();
34901     },
34902     
34903     applyConfig : function(config){
34904         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
34905         this.config = config;
34906         
34907     },
34908     
34909     /**
34910      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
34911      * the width, for horizontal (north, south) the height.
34912      * @param {Number} newSize The new width or height
34913      */
34914     resizeTo : function(newSize){
34915         var el = this.el ? this.el :
34916                  (this.activePanel ? this.activePanel.getEl() : null);
34917         if(el){
34918             switch(this.position){
34919                 case "east":
34920                 case "west":
34921                     el.setWidth(newSize);
34922                     this.fireEvent("resized", this, newSize);
34923                 break;
34924                 case "north":
34925                 case "south":
34926                     el.setHeight(newSize);
34927                     this.fireEvent("resized", this, newSize);
34928                 break;                
34929             }
34930         }
34931     },
34932     
34933     getBox : function(){
34934         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
34935     },
34936     
34937     getMargins : function(){
34938         return this.margins;
34939     },
34940     
34941     updateBox : function(box){
34942         this.box = box;
34943         var el = this.activePanel.getEl();
34944         el.dom.style.left = box.x + "px";
34945         el.dom.style.top = box.y + "px";
34946         this.activePanel.setSize(box.width, box.height);
34947     },
34948     
34949     /**
34950      * Returns the container element for this region.
34951      * @return {Roo.Element}
34952      */
34953     getEl : function(){
34954         return this.activePanel;
34955     },
34956     
34957     /**
34958      * Returns true if this region is currently visible.
34959      * @return {Boolean}
34960      */
34961     isVisible : function(){
34962         return this.activePanel ? true : false;
34963     },
34964     
34965     setActivePanel : function(panel){
34966         panel = this.getPanel(panel);
34967         if(this.activePanel && this.activePanel != panel){
34968             this.activePanel.setActiveState(false);
34969             this.activePanel.getEl().setLeftTop(-10000,-10000);
34970         }
34971         this.activePanel = panel;
34972         panel.setActiveState(true);
34973         if(this.box){
34974             panel.setSize(this.box.width, this.box.height);
34975         }
34976         this.fireEvent("panelactivated", this, panel);
34977         this.fireEvent("invalidated");
34978     },
34979     
34980     /**
34981      * Show the specified panel.
34982      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
34983      * @return {Roo.ContentPanel} The shown panel or null
34984      */
34985     showPanel : function(panel){
34986         panel = this.getPanel(panel);
34987         if(panel){
34988             this.setActivePanel(panel);
34989         }
34990         return panel;
34991     },
34992     
34993     /**
34994      * Get the active panel for this region.
34995      * @return {Roo.ContentPanel} The active panel or null
34996      */
34997     getActivePanel : function(){
34998         return this.activePanel;
34999     },
35000     
35001     /**
35002      * Add the passed ContentPanel(s)
35003      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35004      * @return {Roo.ContentPanel} The panel added (if only one was added)
35005      */
35006     add : function(panel){
35007         if(arguments.length > 1){
35008             for(var i = 0, len = arguments.length; i < len; i++) {
35009                 this.add(arguments[i]);
35010             }
35011             return null;
35012         }
35013         if(this.hasPanel(panel)){
35014             this.showPanel(panel);
35015             return panel;
35016         }
35017         var el = panel.getEl();
35018         if(el.dom.parentNode != this.mgr.el.dom){
35019             this.mgr.el.dom.appendChild(el.dom);
35020         }
35021         if(panel.setRegion){
35022             panel.setRegion(this);
35023         }
35024         this.panels.add(panel);
35025         el.setStyle("position", "absolute");
35026         if(!panel.background){
35027             this.setActivePanel(panel);
35028             if(this.config.initialSize && this.panels.getCount()==1){
35029                 this.resizeTo(this.config.initialSize);
35030             }
35031         }
35032         this.fireEvent("paneladded", this, panel);
35033         return panel;
35034     },
35035     
35036     /**
35037      * Returns true if the panel is in this region.
35038      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35039      * @return {Boolean}
35040      */
35041     hasPanel : function(panel){
35042         if(typeof panel == "object"){ // must be panel obj
35043             panel = panel.getId();
35044         }
35045         return this.getPanel(panel) ? true : false;
35046     },
35047     
35048     /**
35049      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35050      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35051      * @param {Boolean} preservePanel Overrides the config preservePanel option
35052      * @return {Roo.ContentPanel} The panel that was removed
35053      */
35054     remove : function(panel, preservePanel){
35055         panel = this.getPanel(panel);
35056         if(!panel){
35057             return null;
35058         }
35059         var e = {};
35060         this.fireEvent("beforeremove", this, panel, e);
35061         if(e.cancel === true){
35062             return null;
35063         }
35064         var panelId = panel.getId();
35065         this.panels.removeKey(panelId);
35066         return panel;
35067     },
35068     
35069     /**
35070      * Returns the panel specified or null if it's not in this region.
35071      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
35072      * @return {Roo.ContentPanel}
35073      */
35074     getPanel : function(id){
35075         if(typeof id == "object"){ // must be panel obj
35076             return id;
35077         }
35078         return this.panels.get(id);
35079     },
35080     
35081     /**
35082      * Returns this regions position (north/south/east/west/center).
35083      * @return {String} 
35084      */
35085     getPosition: function(){
35086         return this.position;    
35087     }
35088 });/*
35089  * Based on:
35090  * Ext JS Library 1.1.1
35091  * Copyright(c) 2006-2007, Ext JS, LLC.
35092  *
35093  * Originally Released Under LGPL - original licence link has changed is not relivant.
35094  *
35095  * Fork - LGPL
35096  * <script type="text/javascript">
35097  */
35098  
35099 /**
35100  * @class Roo.bootstrap.layout.Region
35101  * @extends Roo.bootstrap.layout.Basic
35102  * This class represents a region in a layout manager.
35103  
35104  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
35105  * @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})
35106  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
35107  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
35108  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
35109  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
35110  * @cfg {String}    title           The title for the region (overrides panel titles)
35111  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
35112  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
35113  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
35114  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
35115  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
35116  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
35117  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
35118  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
35119  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
35120  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
35121
35122  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
35123  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
35124  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
35125  * @cfg {Number}    width           For East/West panels
35126  * @cfg {Number}    height          For North/South panels
35127  * @cfg {Boolean}   split           To show the splitter
35128  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
35129  * 
35130  * @cfg {string}   cls             Extra CSS classes to add to region
35131  * 
35132  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
35133  * @cfg {string}   region  the region that it inhabits..
35134  *
35135
35136  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
35137  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
35138
35139  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
35140  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
35141  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
35142  */
35143 Roo.bootstrap.layout.Region = function(config)
35144 {
35145     this.applyConfig(config);
35146
35147     var mgr = config.mgr;
35148     var pos = config.region;
35149     config.skipConfig = true;
35150     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
35151     
35152     if (mgr.el) {
35153         this.onRender(mgr.el);   
35154     }
35155      
35156     this.visible = true;
35157     this.collapsed = false;
35158     this.unrendered_panels = [];
35159 };
35160
35161 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
35162
35163     position: '', // set by wrapper (eg. north/south etc..)
35164     unrendered_panels : null,  // unrendered panels.
35165     createBody : function(){
35166         /** This region's body element 
35167         * @type Roo.Element */
35168         this.bodyEl = this.el.createChild({
35169                 tag: "div",
35170                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
35171         });
35172     },
35173
35174     onRender: function(ctr, pos)
35175     {
35176         var dh = Roo.DomHelper;
35177         /** This region's container element 
35178         * @type Roo.Element */
35179         this.el = dh.append(ctr.dom, {
35180                 tag: "div",
35181                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
35182             }, true);
35183         /** This region's title element 
35184         * @type Roo.Element */
35185     
35186         this.titleEl = dh.append(this.el.dom,
35187             {
35188                     tag: "div",
35189                     unselectable: "on",
35190                     cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
35191                     children:[
35192                         {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
35193                         {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
35194                     ]}, true);
35195         
35196         this.titleEl.enableDisplayMode();
35197         /** This region's title text element 
35198         * @type HTMLElement */
35199         this.titleTextEl = this.titleEl.dom.firstChild;
35200         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
35201         /*
35202         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
35203         this.closeBtn.enableDisplayMode();
35204         this.closeBtn.on("click", this.closeClicked, this);
35205         this.closeBtn.hide();
35206     */
35207         this.createBody(this.config);
35208         if(this.config.hideWhenEmpty){
35209             this.hide();
35210             this.on("paneladded", this.validateVisibility, this);
35211             this.on("panelremoved", this.validateVisibility, this);
35212         }
35213         if(this.autoScroll){
35214             this.bodyEl.setStyle("overflow", "auto");
35215         }else{
35216             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
35217         }
35218         //if(c.titlebar !== false){
35219             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
35220                 this.titleEl.hide();
35221             }else{
35222                 this.titleEl.show();
35223                 if(this.config.title){
35224                     this.titleTextEl.innerHTML = this.config.title;
35225                 }
35226             }
35227         //}
35228         if(this.config.collapsed){
35229             this.collapse(true);
35230         }
35231         if(this.config.hidden){
35232             this.hide();
35233         }
35234         
35235         if (this.unrendered_panels && this.unrendered_panels.length) {
35236             for (var i =0;i< this.unrendered_panels.length; i++) {
35237                 this.add(this.unrendered_panels[i]);
35238             }
35239             this.unrendered_panels = null;
35240             
35241         }
35242         
35243     },
35244     
35245     applyConfig : function(c)
35246     {
35247         /*
35248          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
35249             var dh = Roo.DomHelper;
35250             if(c.titlebar !== false){
35251                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
35252                 this.collapseBtn.on("click", this.collapse, this);
35253                 this.collapseBtn.enableDisplayMode();
35254                 /*
35255                 if(c.showPin === true || this.showPin){
35256                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
35257                     this.stickBtn.enableDisplayMode();
35258                     this.stickBtn.on("click", this.expand, this);
35259                     this.stickBtn.hide();
35260                 }
35261                 
35262             }
35263             */
35264             /** This region's collapsed element
35265             * @type Roo.Element */
35266             /*
35267              *
35268             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
35269                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
35270             ]}, true);
35271             
35272             if(c.floatable !== false){
35273                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
35274                this.collapsedEl.on("click", this.collapseClick, this);
35275             }
35276
35277             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
35278                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
35279                    id: "message", unselectable: "on", style:{"float":"left"}});
35280                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
35281              }
35282             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
35283             this.expandBtn.on("click", this.expand, this);
35284             
35285         }
35286         
35287         if(this.collapseBtn){
35288             this.collapseBtn.setVisible(c.collapsible == true);
35289         }
35290         
35291         this.cmargins = c.cmargins || this.cmargins ||
35292                          (this.position == "west" || this.position == "east" ?
35293                              {top: 0, left: 2, right:2, bottom: 0} :
35294                              {top: 2, left: 0, right:0, bottom: 2});
35295         */
35296         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
35297         
35298         
35299         this.bottomTabs = c.tabPosition != "top";
35300         
35301         this.autoScroll = c.autoScroll || false;
35302         
35303         
35304        
35305         
35306         this.duration = c.duration || .30;
35307         this.slideDuration = c.slideDuration || .45;
35308         this.config = c;
35309        
35310     },
35311     /**
35312      * Returns true if this region is currently visible.
35313      * @return {Boolean}
35314      */
35315     isVisible : function(){
35316         return this.visible;
35317     },
35318
35319     /**
35320      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
35321      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
35322      */
35323     //setCollapsedTitle : function(title){
35324     //    title = title || "&#160;";
35325      //   if(this.collapsedTitleTextEl){
35326       //      this.collapsedTitleTextEl.innerHTML = title;
35327        // }
35328     //},
35329
35330     getBox : function(){
35331         var b;
35332       //  if(!this.collapsed){
35333             b = this.el.getBox(false, true);
35334        // }else{
35335           //  b = this.collapsedEl.getBox(false, true);
35336         //}
35337         return b;
35338     },
35339
35340     getMargins : function(){
35341         return this.margins;
35342         //return this.collapsed ? this.cmargins : this.margins;
35343     },
35344 /*
35345     highlight : function(){
35346         this.el.addClass("x-layout-panel-dragover");
35347     },
35348
35349     unhighlight : function(){
35350         this.el.removeClass("x-layout-panel-dragover");
35351     },
35352 */
35353     updateBox : function(box)
35354     {
35355         if (!this.bodyEl) {
35356             return; // not rendered yet..
35357         }
35358         
35359         this.box = box;
35360         if(!this.collapsed){
35361             this.el.dom.style.left = box.x + "px";
35362             this.el.dom.style.top = box.y + "px";
35363             this.updateBody(box.width, box.height);
35364         }else{
35365             this.collapsedEl.dom.style.left = box.x + "px";
35366             this.collapsedEl.dom.style.top = box.y + "px";
35367             this.collapsedEl.setSize(box.width, box.height);
35368         }
35369         if(this.tabs){
35370             this.tabs.autoSizeTabs();
35371         }
35372     },
35373
35374     updateBody : function(w, h)
35375     {
35376         if(w !== null){
35377             this.el.setWidth(w);
35378             w -= this.el.getBorderWidth("rl");
35379             if(this.config.adjustments){
35380                 w += this.config.adjustments[0];
35381             }
35382         }
35383         if(h !== null && h > 0){
35384             this.el.setHeight(h);
35385             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
35386             h -= this.el.getBorderWidth("tb");
35387             if(this.config.adjustments){
35388                 h += this.config.adjustments[1];
35389             }
35390             this.bodyEl.setHeight(h);
35391             if(this.tabs){
35392                 h = this.tabs.syncHeight(h);
35393             }
35394         }
35395         if(this.panelSize){
35396             w = w !== null ? w : this.panelSize.width;
35397             h = h !== null ? h : this.panelSize.height;
35398         }
35399         if(this.activePanel){
35400             var el = this.activePanel.getEl();
35401             w = w !== null ? w : el.getWidth();
35402             h = h !== null ? h : el.getHeight();
35403             this.panelSize = {width: w, height: h};
35404             this.activePanel.setSize(w, h);
35405         }
35406         if(Roo.isIE && this.tabs){
35407             this.tabs.el.repaint();
35408         }
35409     },
35410
35411     /**
35412      * Returns the container element for this region.
35413      * @return {Roo.Element}
35414      */
35415     getEl : function(){
35416         return this.el;
35417     },
35418
35419     /**
35420      * Hides this region.
35421      */
35422     hide : function(){
35423         //if(!this.collapsed){
35424             this.el.dom.style.left = "-2000px";
35425             this.el.hide();
35426         //}else{
35427          //   this.collapsedEl.dom.style.left = "-2000px";
35428          //   this.collapsedEl.hide();
35429        // }
35430         this.visible = false;
35431         this.fireEvent("visibilitychange", this, false);
35432     },
35433
35434     /**
35435      * Shows this region if it was previously hidden.
35436      */
35437     show : function(){
35438         //if(!this.collapsed){
35439             this.el.show();
35440         //}else{
35441         //    this.collapsedEl.show();
35442        // }
35443         this.visible = true;
35444         this.fireEvent("visibilitychange", this, true);
35445     },
35446 /*
35447     closeClicked : function(){
35448         if(this.activePanel){
35449             this.remove(this.activePanel);
35450         }
35451     },
35452
35453     collapseClick : function(e){
35454         if(this.isSlid){
35455            e.stopPropagation();
35456            this.slideIn();
35457         }else{
35458            e.stopPropagation();
35459            this.slideOut();
35460         }
35461     },
35462 */
35463     /**
35464      * Collapses this region.
35465      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
35466      */
35467     /*
35468     collapse : function(skipAnim, skipCheck = false){
35469         if(this.collapsed) {
35470             return;
35471         }
35472         
35473         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
35474             
35475             this.collapsed = true;
35476             if(this.split){
35477                 this.split.el.hide();
35478             }
35479             if(this.config.animate && skipAnim !== true){
35480                 this.fireEvent("invalidated", this);
35481                 this.animateCollapse();
35482             }else{
35483                 this.el.setLocation(-20000,-20000);
35484                 this.el.hide();
35485                 this.collapsedEl.show();
35486                 this.fireEvent("collapsed", this);
35487                 this.fireEvent("invalidated", this);
35488             }
35489         }
35490         
35491     },
35492 */
35493     animateCollapse : function(){
35494         // overridden
35495     },
35496
35497     /**
35498      * Expands this region if it was previously collapsed.
35499      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
35500      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
35501      */
35502     /*
35503     expand : function(e, skipAnim){
35504         if(e) {
35505             e.stopPropagation();
35506         }
35507         if(!this.collapsed || this.el.hasActiveFx()) {
35508             return;
35509         }
35510         if(this.isSlid){
35511             this.afterSlideIn();
35512             skipAnim = true;
35513         }
35514         this.collapsed = false;
35515         if(this.config.animate && skipAnim !== true){
35516             this.animateExpand();
35517         }else{
35518             this.el.show();
35519             if(this.split){
35520                 this.split.el.show();
35521             }
35522             this.collapsedEl.setLocation(-2000,-2000);
35523             this.collapsedEl.hide();
35524             this.fireEvent("invalidated", this);
35525             this.fireEvent("expanded", this);
35526         }
35527     },
35528 */
35529     animateExpand : function(){
35530         // overridden
35531     },
35532
35533     initTabs : function()
35534     {
35535         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
35536         
35537         var ts = new Roo.bootstrap.panel.Tabs({
35538                 el: this.bodyEl.dom,
35539                 tabPosition: this.bottomTabs ? 'bottom' : 'top',
35540                 disableTooltips: this.config.disableTabTips,
35541                 toolbar : this.config.toolbar
35542             });
35543         
35544         if(this.config.hideTabs){
35545             ts.stripWrap.setDisplayed(false);
35546         }
35547         this.tabs = ts;
35548         ts.resizeTabs = this.config.resizeTabs === true;
35549         ts.minTabWidth = this.config.minTabWidth || 40;
35550         ts.maxTabWidth = this.config.maxTabWidth || 250;
35551         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
35552         ts.monitorResize = false;
35553         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
35554         ts.bodyEl.addClass('roo-layout-tabs-body');
35555         this.panels.each(this.initPanelAsTab, this);
35556     },
35557
35558     initPanelAsTab : function(panel){
35559         var ti = this.tabs.addTab(
35560             panel.getEl().id,
35561             panel.getTitle(),
35562             null,
35563             this.config.closeOnTab && panel.isClosable(),
35564             panel.tpl
35565         );
35566         if(panel.tabTip !== undefined){
35567             ti.setTooltip(panel.tabTip);
35568         }
35569         ti.on("activate", function(){
35570               this.setActivePanel(panel);
35571         }, this);
35572         
35573         if(this.config.closeOnTab){
35574             ti.on("beforeclose", function(t, e){
35575                 e.cancel = true;
35576                 this.remove(panel);
35577             }, this);
35578         }
35579         
35580         panel.tabItem = ti;
35581         
35582         return ti;
35583     },
35584
35585     updatePanelTitle : function(panel, title)
35586     {
35587         if(this.activePanel == panel){
35588             this.updateTitle(title);
35589         }
35590         if(this.tabs){
35591             var ti = this.tabs.getTab(panel.getEl().id);
35592             ti.setText(title);
35593             if(panel.tabTip !== undefined){
35594                 ti.setTooltip(panel.tabTip);
35595             }
35596         }
35597     },
35598
35599     updateTitle : function(title){
35600         if(this.titleTextEl && !this.config.title){
35601             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
35602         }
35603     },
35604
35605     setActivePanel : function(panel)
35606     {
35607         panel = this.getPanel(panel);
35608         if(this.activePanel && this.activePanel != panel){
35609             if(this.activePanel.setActiveState(false) === false){
35610                 return;
35611             }
35612         }
35613         this.activePanel = panel;
35614         panel.setActiveState(true);
35615         if(this.panelSize){
35616             panel.setSize(this.panelSize.width, this.panelSize.height);
35617         }
35618         if(this.closeBtn){
35619             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
35620         }
35621         this.updateTitle(panel.getTitle());
35622         if(this.tabs){
35623             this.fireEvent("invalidated", this);
35624         }
35625         this.fireEvent("panelactivated", this, panel);
35626     },
35627
35628     /**
35629      * Shows the specified panel.
35630      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
35631      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
35632      */
35633     showPanel : function(panel)
35634     {
35635         panel = this.getPanel(panel);
35636         if(panel){
35637             if(this.tabs){
35638                 var tab = this.tabs.getTab(panel.getEl().id);
35639                 if(tab.isHidden()){
35640                     this.tabs.unhideTab(tab.id);
35641                 }
35642                 tab.activate();
35643             }else{
35644                 this.setActivePanel(panel);
35645             }
35646         }
35647         return panel;
35648     },
35649
35650     /**
35651      * Get the active panel for this region.
35652      * @return {Roo.ContentPanel} The active panel or null
35653      */
35654     getActivePanel : function(){
35655         return this.activePanel;
35656     },
35657
35658     validateVisibility : function(){
35659         if(this.panels.getCount() < 1){
35660             this.updateTitle("&#160;");
35661             this.closeBtn.hide();
35662             this.hide();
35663         }else{
35664             if(!this.isVisible()){
35665                 this.show();
35666             }
35667         }
35668     },
35669
35670     /**
35671      * Adds the passed ContentPanel(s) to this region.
35672      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
35673      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
35674      */
35675     add : function(panel)
35676     {
35677         if(arguments.length > 1){
35678             for(var i = 0, len = arguments.length; i < len; i++) {
35679                 this.add(arguments[i]);
35680             }
35681             return null;
35682         }
35683         
35684         // if we have not been rendered yet, then we can not really do much of this..
35685         if (!this.bodyEl) {
35686             this.unrendered_panels.push(panel);
35687             return panel;
35688         }
35689         
35690         
35691         
35692         
35693         if(this.hasPanel(panel)){
35694             this.showPanel(panel);
35695             return panel;
35696         }
35697         panel.setRegion(this);
35698         this.panels.add(panel);
35699        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
35700             // sinle panel - no tab...?? would it not be better to render it with the tabs,
35701             // and hide them... ???
35702             this.bodyEl.dom.appendChild(panel.getEl().dom);
35703             if(panel.background !== true){
35704                 this.setActivePanel(panel);
35705             }
35706             this.fireEvent("paneladded", this, panel);
35707             return panel;
35708         }
35709         */
35710         if(!this.tabs){
35711             this.initTabs();
35712         }else{
35713             this.initPanelAsTab(panel);
35714         }
35715         
35716         
35717         if(panel.background !== true){
35718             this.tabs.activate(panel.getEl().id);
35719         }
35720         this.fireEvent("paneladded", this, panel);
35721         return panel;
35722     },
35723
35724     /**
35725      * Hides the tab for the specified panel.
35726      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35727      */
35728     hidePanel : function(panel){
35729         if(this.tabs && (panel = this.getPanel(panel))){
35730             this.tabs.hideTab(panel.getEl().id);
35731         }
35732     },
35733
35734     /**
35735      * Unhides the tab for a previously hidden panel.
35736      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35737      */
35738     unhidePanel : function(panel){
35739         if(this.tabs && (panel = this.getPanel(panel))){
35740             this.tabs.unhideTab(panel.getEl().id);
35741         }
35742     },
35743
35744     clearPanels : function(){
35745         while(this.panels.getCount() > 0){
35746              this.remove(this.panels.first());
35747         }
35748     },
35749
35750     /**
35751      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
35752      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
35753      * @param {Boolean} preservePanel Overrides the config preservePanel option
35754      * @return {Roo.ContentPanel} The panel that was removed
35755      */
35756     remove : function(panel, preservePanel)
35757     {
35758         panel = this.getPanel(panel);
35759         if(!panel){
35760             return null;
35761         }
35762         var e = {};
35763         this.fireEvent("beforeremove", this, panel, e);
35764         if(e.cancel === true){
35765             return null;
35766         }
35767         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
35768         var panelId = panel.getId();
35769         this.panels.removeKey(panelId);
35770         if(preservePanel){
35771             document.body.appendChild(panel.getEl().dom);
35772         }
35773         if(this.tabs){
35774             this.tabs.removeTab(panel.getEl().id);
35775         }else if (!preservePanel){
35776             this.bodyEl.dom.removeChild(panel.getEl().dom);
35777         }
35778         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
35779             var p = this.panels.first();
35780             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
35781             tempEl.appendChild(p.getEl().dom);
35782             this.bodyEl.update("");
35783             this.bodyEl.dom.appendChild(p.getEl().dom);
35784             tempEl = null;
35785             this.updateTitle(p.getTitle());
35786             this.tabs = null;
35787             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
35788             this.setActivePanel(p);
35789         }
35790         panel.setRegion(null);
35791         if(this.activePanel == panel){
35792             this.activePanel = null;
35793         }
35794         if(this.config.autoDestroy !== false && preservePanel !== true){
35795             try{panel.destroy();}catch(e){}
35796         }
35797         this.fireEvent("panelremoved", this, panel);
35798         return panel;
35799     },
35800
35801     /**
35802      * Returns the TabPanel component used by this region
35803      * @return {Roo.TabPanel}
35804      */
35805     getTabs : function(){
35806         return this.tabs;
35807     },
35808
35809     createTool : function(parentEl, className){
35810         var btn = Roo.DomHelper.append(parentEl, {
35811             tag: "div",
35812             cls: "x-layout-tools-button",
35813             children: [ {
35814                 tag: "div",
35815                 cls: "roo-layout-tools-button-inner " + className,
35816                 html: "&#160;"
35817             }]
35818         }, true);
35819         btn.addClassOnOver("roo-layout-tools-button-over");
35820         return btn;
35821     }
35822 });/*
35823  * Based on:
35824  * Ext JS Library 1.1.1
35825  * Copyright(c) 2006-2007, Ext JS, LLC.
35826  *
35827  * Originally Released Under LGPL - original licence link has changed is not relivant.
35828  *
35829  * Fork - LGPL
35830  * <script type="text/javascript">
35831  */
35832  
35833
35834
35835 /**
35836  * @class Roo.SplitLayoutRegion
35837  * @extends Roo.LayoutRegion
35838  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
35839  */
35840 Roo.bootstrap.layout.Split = function(config){
35841     this.cursor = config.cursor;
35842     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
35843 };
35844
35845 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
35846 {
35847     splitTip : "Drag to resize.",
35848     collapsibleSplitTip : "Drag to resize. Double click to hide.",
35849     useSplitTips : false,
35850
35851     applyConfig : function(config){
35852         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
35853     },
35854     
35855     onRender : function(ctr,pos) {
35856         
35857         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
35858         if(!this.config.split){
35859             return;
35860         }
35861         if(!this.split){
35862             
35863             var splitEl = Roo.DomHelper.append(ctr.dom,  {
35864                             tag: "div",
35865                             id: this.el.id + "-split",
35866                             cls: "roo-layout-split roo-layout-split-"+this.position,
35867                             html: "&#160;"
35868             });
35869             /** The SplitBar for this region 
35870             * @type Roo.SplitBar */
35871             // does not exist yet...
35872             Roo.log([this.position, this.orientation]);
35873             
35874             this.split = new Roo.bootstrap.SplitBar({
35875                 dragElement : splitEl,
35876                 resizingElement: this.el,
35877                 orientation : this.orientation
35878             });
35879             
35880             this.split.on("moved", this.onSplitMove, this);
35881             this.split.useShim = this.config.useShim === true;
35882             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
35883             if(this.useSplitTips){
35884                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
35885             }
35886             //if(config.collapsible){
35887             //    this.split.el.on("dblclick", this.collapse,  this);
35888             //}
35889         }
35890         if(typeof this.config.minSize != "undefined"){
35891             this.split.minSize = this.config.minSize;
35892         }
35893         if(typeof this.config.maxSize != "undefined"){
35894             this.split.maxSize = this.config.maxSize;
35895         }
35896         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
35897             this.hideSplitter();
35898         }
35899         
35900     },
35901
35902     getHMaxSize : function(){
35903          var cmax = this.config.maxSize || 10000;
35904          var center = this.mgr.getRegion("center");
35905          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
35906     },
35907
35908     getVMaxSize : function(){
35909          var cmax = this.config.maxSize || 10000;
35910          var center = this.mgr.getRegion("center");
35911          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
35912     },
35913
35914     onSplitMove : function(split, newSize){
35915         this.fireEvent("resized", this, newSize);
35916     },
35917     
35918     /** 
35919      * Returns the {@link Roo.SplitBar} for this region.
35920      * @return {Roo.SplitBar}
35921      */
35922     getSplitBar : function(){
35923         return this.split;
35924     },
35925     
35926     hide : function(){
35927         this.hideSplitter();
35928         Roo.bootstrap.layout.Split.superclass.hide.call(this);
35929     },
35930
35931     hideSplitter : function(){
35932         if(this.split){
35933             this.split.el.setLocation(-2000,-2000);
35934             this.split.el.hide();
35935         }
35936     },
35937
35938     show : function(){
35939         if(this.split){
35940             this.split.el.show();
35941         }
35942         Roo.bootstrap.layout.Split.superclass.show.call(this);
35943     },
35944     
35945     beforeSlide: function(){
35946         if(Roo.isGecko){// firefox overflow auto bug workaround
35947             this.bodyEl.clip();
35948             if(this.tabs) {
35949                 this.tabs.bodyEl.clip();
35950             }
35951             if(this.activePanel){
35952                 this.activePanel.getEl().clip();
35953                 
35954                 if(this.activePanel.beforeSlide){
35955                     this.activePanel.beforeSlide();
35956                 }
35957             }
35958         }
35959     },
35960     
35961     afterSlide : function(){
35962         if(Roo.isGecko){// firefox overflow auto bug workaround
35963             this.bodyEl.unclip();
35964             if(this.tabs) {
35965                 this.tabs.bodyEl.unclip();
35966             }
35967             if(this.activePanel){
35968                 this.activePanel.getEl().unclip();
35969                 if(this.activePanel.afterSlide){
35970                     this.activePanel.afterSlide();
35971                 }
35972             }
35973         }
35974     },
35975
35976     initAutoHide : function(){
35977         if(this.autoHide !== false){
35978             if(!this.autoHideHd){
35979                 var st = new Roo.util.DelayedTask(this.slideIn, this);
35980                 this.autoHideHd = {
35981                     "mouseout": function(e){
35982                         if(!e.within(this.el, true)){
35983                             st.delay(500);
35984                         }
35985                     },
35986                     "mouseover" : function(e){
35987                         st.cancel();
35988                     },
35989                     scope : this
35990                 };
35991             }
35992             this.el.on(this.autoHideHd);
35993         }
35994     },
35995
35996     clearAutoHide : function(){
35997         if(this.autoHide !== false){
35998             this.el.un("mouseout", this.autoHideHd.mouseout);
35999             this.el.un("mouseover", this.autoHideHd.mouseover);
36000         }
36001     },
36002
36003     clearMonitor : function(){
36004         Roo.get(document).un("click", this.slideInIf, this);
36005     },
36006
36007     // these names are backwards but not changed for compat
36008     slideOut : function(){
36009         if(this.isSlid || this.el.hasActiveFx()){
36010             return;
36011         }
36012         this.isSlid = true;
36013         if(this.collapseBtn){
36014             this.collapseBtn.hide();
36015         }
36016         this.closeBtnState = this.closeBtn.getStyle('display');
36017         this.closeBtn.hide();
36018         if(this.stickBtn){
36019             this.stickBtn.show();
36020         }
36021         this.el.show();
36022         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
36023         this.beforeSlide();
36024         this.el.setStyle("z-index", 10001);
36025         this.el.slideIn(this.getSlideAnchor(), {
36026             callback: function(){
36027                 this.afterSlide();
36028                 this.initAutoHide();
36029                 Roo.get(document).on("click", this.slideInIf, this);
36030                 this.fireEvent("slideshow", this);
36031             },
36032             scope: this,
36033             block: true
36034         });
36035     },
36036
36037     afterSlideIn : function(){
36038         this.clearAutoHide();
36039         this.isSlid = false;
36040         this.clearMonitor();
36041         this.el.setStyle("z-index", "");
36042         if(this.collapseBtn){
36043             this.collapseBtn.show();
36044         }
36045         this.closeBtn.setStyle('display', this.closeBtnState);
36046         if(this.stickBtn){
36047             this.stickBtn.hide();
36048         }
36049         this.fireEvent("slidehide", this);
36050     },
36051
36052     slideIn : function(cb){
36053         if(!this.isSlid || this.el.hasActiveFx()){
36054             Roo.callback(cb);
36055             return;
36056         }
36057         this.isSlid = false;
36058         this.beforeSlide();
36059         this.el.slideOut(this.getSlideAnchor(), {
36060             callback: function(){
36061                 this.el.setLeftTop(-10000, -10000);
36062                 this.afterSlide();
36063                 this.afterSlideIn();
36064                 Roo.callback(cb);
36065             },
36066             scope: this,
36067             block: true
36068         });
36069     },
36070     
36071     slideInIf : function(e){
36072         if(!e.within(this.el)){
36073             this.slideIn();
36074         }
36075     },
36076
36077     animateCollapse : function(){
36078         this.beforeSlide();
36079         this.el.setStyle("z-index", 20000);
36080         var anchor = this.getSlideAnchor();
36081         this.el.slideOut(anchor, {
36082             callback : function(){
36083                 this.el.setStyle("z-index", "");
36084                 this.collapsedEl.slideIn(anchor, {duration:.3});
36085                 this.afterSlide();
36086                 this.el.setLocation(-10000,-10000);
36087                 this.el.hide();
36088                 this.fireEvent("collapsed", this);
36089             },
36090             scope: this,
36091             block: true
36092         });
36093     },
36094
36095     animateExpand : function(){
36096         this.beforeSlide();
36097         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
36098         this.el.setStyle("z-index", 20000);
36099         this.collapsedEl.hide({
36100             duration:.1
36101         });
36102         this.el.slideIn(this.getSlideAnchor(), {
36103             callback : function(){
36104                 this.el.setStyle("z-index", "");
36105                 this.afterSlide();
36106                 if(this.split){
36107                     this.split.el.show();
36108                 }
36109                 this.fireEvent("invalidated", this);
36110                 this.fireEvent("expanded", this);
36111             },
36112             scope: this,
36113             block: true
36114         });
36115     },
36116
36117     anchors : {
36118         "west" : "left",
36119         "east" : "right",
36120         "north" : "top",
36121         "south" : "bottom"
36122     },
36123
36124     sanchors : {
36125         "west" : "l",
36126         "east" : "r",
36127         "north" : "t",
36128         "south" : "b"
36129     },
36130
36131     canchors : {
36132         "west" : "tl-tr",
36133         "east" : "tr-tl",
36134         "north" : "tl-bl",
36135         "south" : "bl-tl"
36136     },
36137
36138     getAnchor : function(){
36139         return this.anchors[this.position];
36140     },
36141
36142     getCollapseAnchor : function(){
36143         return this.canchors[this.position];
36144     },
36145
36146     getSlideAnchor : function(){
36147         return this.sanchors[this.position];
36148     },
36149
36150     getAlignAdj : function(){
36151         var cm = this.cmargins;
36152         switch(this.position){
36153             case "west":
36154                 return [0, 0];
36155             break;
36156             case "east":
36157                 return [0, 0];
36158             break;
36159             case "north":
36160                 return [0, 0];
36161             break;
36162             case "south":
36163                 return [0, 0];
36164             break;
36165         }
36166     },
36167
36168     getExpandAdj : function(){
36169         var c = this.collapsedEl, cm = this.cmargins;
36170         switch(this.position){
36171             case "west":
36172                 return [-(cm.right+c.getWidth()+cm.left), 0];
36173             break;
36174             case "east":
36175                 return [cm.right+c.getWidth()+cm.left, 0];
36176             break;
36177             case "north":
36178                 return [0, -(cm.top+cm.bottom+c.getHeight())];
36179             break;
36180             case "south":
36181                 return [0, cm.top+cm.bottom+c.getHeight()];
36182             break;
36183         }
36184     }
36185 });/*
36186  * Based on:
36187  * Ext JS Library 1.1.1
36188  * Copyright(c) 2006-2007, Ext JS, LLC.
36189  *
36190  * Originally Released Under LGPL - original licence link has changed is not relivant.
36191  *
36192  * Fork - LGPL
36193  * <script type="text/javascript">
36194  */
36195 /*
36196  * These classes are private internal classes
36197  */
36198 Roo.bootstrap.layout.Center = function(config){
36199     config.region = "center";
36200     Roo.bootstrap.layout.Region.call(this, config);
36201     this.visible = true;
36202     this.minWidth = config.minWidth || 20;
36203     this.minHeight = config.minHeight || 20;
36204 };
36205
36206 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
36207     hide : function(){
36208         // center panel can't be hidden
36209     },
36210     
36211     show : function(){
36212         // center panel can't be hidden
36213     },
36214     
36215     getMinWidth: function(){
36216         return this.minWidth;
36217     },
36218     
36219     getMinHeight: function(){
36220         return this.minHeight;
36221     }
36222 });
36223
36224
36225
36226
36227  
36228
36229
36230
36231
36232
36233 Roo.bootstrap.layout.North = function(config)
36234 {
36235     config.region = 'north';
36236     config.cursor = 'n-resize';
36237     
36238     Roo.bootstrap.layout.Split.call(this, config);
36239     
36240     
36241     if(this.split){
36242         this.split.placement = Roo.bootstrap.SplitBar.TOP;
36243         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36244         this.split.el.addClass("roo-layout-split-v");
36245     }
36246     var size = config.initialSize || config.height;
36247     if(typeof size != "undefined"){
36248         this.el.setHeight(size);
36249     }
36250 };
36251 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
36252 {
36253     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36254     
36255     
36256     
36257     getBox : function(){
36258         if(this.collapsed){
36259             return this.collapsedEl.getBox();
36260         }
36261         var box = this.el.getBox();
36262         if(this.split){
36263             box.height += this.split.el.getHeight();
36264         }
36265         return box;
36266     },
36267     
36268     updateBox : function(box){
36269         if(this.split && !this.collapsed){
36270             box.height -= this.split.el.getHeight();
36271             this.split.el.setLeft(box.x);
36272             this.split.el.setTop(box.y+box.height);
36273             this.split.el.setWidth(box.width);
36274         }
36275         if(this.collapsed){
36276             this.updateBody(box.width, null);
36277         }
36278         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36279     }
36280 });
36281
36282
36283
36284
36285
36286 Roo.bootstrap.layout.South = function(config){
36287     config.region = 'south';
36288     config.cursor = 's-resize';
36289     Roo.bootstrap.layout.Split.call(this, config);
36290     if(this.split){
36291         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
36292         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
36293         this.split.el.addClass("roo-layout-split-v");
36294     }
36295     var size = config.initialSize || config.height;
36296     if(typeof size != "undefined"){
36297         this.el.setHeight(size);
36298     }
36299 };
36300
36301 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
36302     orientation: Roo.bootstrap.SplitBar.VERTICAL,
36303     getBox : function(){
36304         if(this.collapsed){
36305             return this.collapsedEl.getBox();
36306         }
36307         var box = this.el.getBox();
36308         if(this.split){
36309             var sh = this.split.el.getHeight();
36310             box.height += sh;
36311             box.y -= sh;
36312         }
36313         return box;
36314     },
36315     
36316     updateBox : function(box){
36317         if(this.split && !this.collapsed){
36318             var sh = this.split.el.getHeight();
36319             box.height -= sh;
36320             box.y += sh;
36321             this.split.el.setLeft(box.x);
36322             this.split.el.setTop(box.y-sh);
36323             this.split.el.setWidth(box.width);
36324         }
36325         if(this.collapsed){
36326             this.updateBody(box.width, null);
36327         }
36328         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36329     }
36330 });
36331
36332 Roo.bootstrap.layout.East = function(config){
36333     config.region = "east";
36334     config.cursor = "e-resize";
36335     Roo.bootstrap.layout.Split.call(this, config);
36336     if(this.split){
36337         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
36338         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36339         this.split.el.addClass("roo-layout-split-h");
36340     }
36341     var size = config.initialSize || config.width;
36342     if(typeof size != "undefined"){
36343         this.el.setWidth(size);
36344     }
36345 };
36346 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
36347     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36348     getBox : function(){
36349         if(this.collapsed){
36350             return this.collapsedEl.getBox();
36351         }
36352         var box = this.el.getBox();
36353         if(this.split){
36354             var sw = this.split.el.getWidth();
36355             box.width += sw;
36356             box.x -= sw;
36357         }
36358         return box;
36359     },
36360
36361     updateBox : function(box){
36362         if(this.split && !this.collapsed){
36363             var sw = this.split.el.getWidth();
36364             box.width -= sw;
36365             this.split.el.setLeft(box.x);
36366             this.split.el.setTop(box.y);
36367             this.split.el.setHeight(box.height);
36368             box.x += sw;
36369         }
36370         if(this.collapsed){
36371             this.updateBody(null, box.height);
36372         }
36373         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36374     }
36375 });
36376
36377 Roo.bootstrap.layout.West = function(config){
36378     config.region = "west";
36379     config.cursor = "w-resize";
36380     
36381     Roo.bootstrap.layout.Split.call(this, config);
36382     if(this.split){
36383         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
36384         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
36385         this.split.el.addClass("roo-layout-split-h");
36386     }
36387     
36388 };
36389 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
36390     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
36391     
36392     onRender: function(ctr, pos)
36393     {
36394         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
36395         var size = this.config.initialSize || this.config.width;
36396         if(typeof size != "undefined"){
36397             this.el.setWidth(size);
36398         }
36399     },
36400     
36401     getBox : function(){
36402         if(this.collapsed){
36403             return this.collapsedEl.getBox();
36404         }
36405         var box = this.el.getBox();
36406         if(this.split){
36407             box.width += this.split.el.getWidth();
36408         }
36409         return box;
36410     },
36411     
36412     updateBox : function(box){
36413         if(this.split && !this.collapsed){
36414             var sw = this.split.el.getWidth();
36415             box.width -= sw;
36416             this.split.el.setLeft(box.x+box.width);
36417             this.split.el.setTop(box.y);
36418             this.split.el.setHeight(box.height);
36419         }
36420         if(this.collapsed){
36421             this.updateBody(null, box.height);
36422         }
36423         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
36424     }
36425 });
36426 Roo.namespace("Roo.bootstrap.panel");/*
36427  * Based on:
36428  * Ext JS Library 1.1.1
36429  * Copyright(c) 2006-2007, Ext JS, LLC.
36430  *
36431  * Originally Released Under LGPL - original licence link has changed is not relivant.
36432  *
36433  * Fork - LGPL
36434  * <script type="text/javascript">
36435  */
36436 /**
36437  * @class Roo.ContentPanel
36438  * @extends Roo.util.Observable
36439  * A basic ContentPanel element.
36440  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
36441  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
36442  * @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
36443  * @cfg {Boolean}   closable      True if the panel can be closed/removed
36444  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
36445  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
36446  * @cfg {Toolbar}   toolbar       A toolbar for this panel
36447  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
36448  * @cfg {String} title          The title for this panel
36449  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
36450  * @cfg {String} url            Calls {@link #setUrl} with this value
36451  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
36452  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
36453  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
36454  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
36455  * @cfg {Boolean} badges render the badges
36456
36457  * @constructor
36458  * Create a new ContentPanel.
36459  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
36460  * @param {String/Object} config A string to set only the title or a config object
36461  * @param {String} content (optional) Set the HTML content for this panel
36462  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
36463  */
36464 Roo.bootstrap.panel.Content = function( config){
36465     
36466     this.tpl = config.tpl || false;
36467     
36468     var el = config.el;
36469     var content = config.content;
36470
36471     if(config.autoCreate){ // xtype is available if this is called from factory
36472         el = Roo.id();
36473     }
36474     this.el = Roo.get(el);
36475     if(!this.el && config && config.autoCreate){
36476         if(typeof config.autoCreate == "object"){
36477             if(!config.autoCreate.id){
36478                 config.autoCreate.id = config.id||el;
36479             }
36480             this.el = Roo.DomHelper.append(document.body,
36481                         config.autoCreate, true);
36482         }else{
36483             var elcfg =  {   tag: "div",
36484                             cls: "roo-layout-inactive-content",
36485                             id: config.id||el
36486                             };
36487             if (config.html) {
36488                 elcfg.html = config.html;
36489                 
36490             }
36491                         
36492             this.el = Roo.DomHelper.append(document.body, elcfg , true);
36493         }
36494     } 
36495     this.closable = false;
36496     this.loaded = false;
36497     this.active = false;
36498    
36499       
36500     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
36501         
36502         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
36503         
36504         this.wrapEl = this.el; //this.el.wrap();
36505         var ti = [];
36506         if (config.toolbar.items) {
36507             ti = config.toolbar.items ;
36508             delete config.toolbar.items ;
36509         }
36510         
36511         var nitems = [];
36512         this.toolbar.render(this.wrapEl, 'before');
36513         for(var i =0;i < ti.length;i++) {
36514           //  Roo.log(['add child', items[i]]);
36515             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36516         }
36517         this.toolbar.items = nitems;
36518         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
36519         delete config.toolbar;
36520         
36521     }
36522     /*
36523     // xtype created footer. - not sure if will work as we normally have to render first..
36524     if (this.footer && !this.footer.el && this.footer.xtype) {
36525         if (!this.wrapEl) {
36526             this.wrapEl = this.el.wrap();
36527         }
36528     
36529         this.footer.container = this.wrapEl.createChild();
36530          
36531         this.footer = Roo.factory(this.footer, Roo);
36532         
36533     }
36534     */
36535     
36536      if(typeof config == "string"){
36537         this.title = config;
36538     }else{
36539         Roo.apply(this, config);
36540     }
36541     
36542     if(this.resizeEl){
36543         this.resizeEl = Roo.get(this.resizeEl, true);
36544     }else{
36545         this.resizeEl = this.el;
36546     }
36547     // handle view.xtype
36548     
36549  
36550     
36551     
36552     this.addEvents({
36553         /**
36554          * @event activate
36555          * Fires when this panel is activated. 
36556          * @param {Roo.ContentPanel} this
36557          */
36558         "activate" : true,
36559         /**
36560          * @event deactivate
36561          * Fires when this panel is activated. 
36562          * @param {Roo.ContentPanel} this
36563          */
36564         "deactivate" : true,
36565
36566         /**
36567          * @event resize
36568          * Fires when this panel is resized if fitToFrame is true.
36569          * @param {Roo.ContentPanel} this
36570          * @param {Number} width The width after any component adjustments
36571          * @param {Number} height The height after any component adjustments
36572          */
36573         "resize" : true,
36574         
36575          /**
36576          * @event render
36577          * Fires when this tab is created
36578          * @param {Roo.ContentPanel} this
36579          */
36580         "render" : true
36581         
36582         
36583         
36584     });
36585     
36586
36587     
36588     
36589     if(this.autoScroll){
36590         this.resizeEl.setStyle("overflow", "auto");
36591     } else {
36592         // fix randome scrolling
36593         //this.el.on('scroll', function() {
36594         //    Roo.log('fix random scolling');
36595         //    this.scrollTo('top',0); 
36596         //});
36597     }
36598     content = content || this.content;
36599     if(content){
36600         this.setContent(content);
36601     }
36602     if(config && config.url){
36603         this.setUrl(this.url, this.params, this.loadOnce);
36604     }
36605     
36606     
36607     
36608     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
36609     
36610     if (this.view && typeof(this.view.xtype) != 'undefined') {
36611         this.view.el = this.el.appendChild(document.createElement("div"));
36612         this.view = Roo.factory(this.view); 
36613         this.view.render  &&  this.view.render(false, '');  
36614     }
36615     
36616     
36617     this.fireEvent('render', this);
36618 };
36619
36620 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
36621     
36622     tabTip : '',
36623     
36624     setRegion : function(region){
36625         this.region = region;
36626         this.setActiveClass(region && !this.background);
36627     },
36628     
36629     
36630     setActiveClass: function(state)
36631     {
36632         if(state){
36633            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
36634            this.el.setStyle('position','relative');
36635         }else{
36636            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
36637            this.el.setStyle('position', 'absolute');
36638         } 
36639     },
36640     
36641     /**
36642      * Returns the toolbar for this Panel if one was configured. 
36643      * @return {Roo.Toolbar} 
36644      */
36645     getToolbar : function(){
36646         return this.toolbar;
36647     },
36648     
36649     setActiveState : function(active)
36650     {
36651         this.active = active;
36652         this.setActiveClass(active);
36653         if(!active){
36654             if(this.fireEvent("deactivate", this) === false){
36655                 return false;
36656             }
36657             return true;
36658         }
36659         this.fireEvent("activate", this);
36660         return true;
36661     },
36662     /**
36663      * Updates this panel's element
36664      * @param {String} content The new content
36665      * @param {Boolean} loadScripts (optional) true to look for and process scripts
36666     */
36667     setContent : function(content, loadScripts){
36668         this.el.update(content, loadScripts);
36669     },
36670
36671     ignoreResize : function(w, h){
36672         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
36673             return true;
36674         }else{
36675             this.lastSize = {width: w, height: h};
36676             return false;
36677         }
36678     },
36679     /**
36680      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
36681      * @return {Roo.UpdateManager} The UpdateManager
36682      */
36683     getUpdateManager : function(){
36684         return this.el.getUpdateManager();
36685     },
36686      /**
36687      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
36688      * @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:
36689 <pre><code>
36690 panel.load({
36691     url: "your-url.php",
36692     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
36693     callback: yourFunction,
36694     scope: yourObject, //(optional scope)
36695     discardUrl: false,
36696     nocache: false,
36697     text: "Loading...",
36698     timeout: 30,
36699     scripts: false
36700 });
36701 </code></pre>
36702      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
36703      * 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.
36704      * @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}
36705      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
36706      * @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.
36707      * @return {Roo.ContentPanel} this
36708      */
36709     load : function(){
36710         var um = this.el.getUpdateManager();
36711         um.update.apply(um, arguments);
36712         return this;
36713     },
36714
36715
36716     /**
36717      * 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.
36718      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
36719      * @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)
36720      * @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)
36721      * @return {Roo.UpdateManager} The UpdateManager
36722      */
36723     setUrl : function(url, params, loadOnce){
36724         if(this.refreshDelegate){
36725             this.removeListener("activate", this.refreshDelegate);
36726         }
36727         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
36728         this.on("activate", this.refreshDelegate);
36729         return this.el.getUpdateManager();
36730     },
36731     
36732     _handleRefresh : function(url, params, loadOnce){
36733         if(!loadOnce || !this.loaded){
36734             var updater = this.el.getUpdateManager();
36735             updater.update(url, params, this._setLoaded.createDelegate(this));
36736         }
36737     },
36738     
36739     _setLoaded : function(){
36740         this.loaded = true;
36741     }, 
36742     
36743     /**
36744      * Returns this panel's id
36745      * @return {String} 
36746      */
36747     getId : function(){
36748         return this.el.id;
36749     },
36750     
36751     /** 
36752      * Returns this panel's element - used by regiosn to add.
36753      * @return {Roo.Element} 
36754      */
36755     getEl : function(){
36756         return this.wrapEl || this.el;
36757     },
36758     
36759    
36760     
36761     adjustForComponents : function(width, height)
36762     {
36763         //Roo.log('adjustForComponents ');
36764         if(this.resizeEl != this.el){
36765             width -= this.el.getFrameWidth('lr');
36766             height -= this.el.getFrameWidth('tb');
36767         }
36768         if(this.toolbar){
36769             var te = this.toolbar.getEl();
36770             te.setWidth(width);
36771             height -= te.getHeight();
36772         }
36773         if(this.footer){
36774             var te = this.footer.getEl();
36775             te.setWidth(width);
36776             height -= te.getHeight();
36777         }
36778         
36779         
36780         if(this.adjustments){
36781             width += this.adjustments[0];
36782             height += this.adjustments[1];
36783         }
36784         return {"width": width, "height": height};
36785     },
36786     
36787     setSize : function(width, height){
36788         if(this.fitToFrame && !this.ignoreResize(width, height)){
36789             if(this.fitContainer && this.resizeEl != this.el){
36790                 this.el.setSize(width, height);
36791             }
36792             var size = this.adjustForComponents(width, height);
36793             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
36794             this.fireEvent('resize', this, size.width, size.height);
36795         }
36796     },
36797     
36798     /**
36799      * Returns this panel's title
36800      * @return {String} 
36801      */
36802     getTitle : function(){
36803         
36804         if (typeof(this.title) != 'object') {
36805             return this.title;
36806         }
36807         
36808         var t = '';
36809         for (var k in this.title) {
36810             if (!this.title.hasOwnProperty(k)) {
36811                 continue;
36812             }
36813             
36814             if (k.indexOf('-') >= 0) {
36815                 var s = k.split('-');
36816                 for (var i = 0; i<s.length; i++) {
36817                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
36818                 }
36819             } else {
36820                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
36821             }
36822         }
36823         return t;
36824     },
36825     
36826     /**
36827      * Set this panel's title
36828      * @param {String} title
36829      */
36830     setTitle : function(title){
36831         this.title = title;
36832         if(this.region){
36833             this.region.updatePanelTitle(this, title);
36834         }
36835     },
36836     
36837     /**
36838      * Returns true is this panel was configured to be closable
36839      * @return {Boolean} 
36840      */
36841     isClosable : function(){
36842         return this.closable;
36843     },
36844     
36845     beforeSlide : function(){
36846         this.el.clip();
36847         this.resizeEl.clip();
36848     },
36849     
36850     afterSlide : function(){
36851         this.el.unclip();
36852         this.resizeEl.unclip();
36853     },
36854     
36855     /**
36856      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
36857      *   Will fail silently if the {@link #setUrl} method has not been called.
36858      *   This does not activate the panel, just updates its content.
36859      */
36860     refresh : function(){
36861         if(this.refreshDelegate){
36862            this.loaded = false;
36863            this.refreshDelegate();
36864         }
36865     },
36866     
36867     /**
36868      * Destroys this panel
36869      */
36870     destroy : function(){
36871         this.el.removeAllListeners();
36872         var tempEl = document.createElement("span");
36873         tempEl.appendChild(this.el.dom);
36874         tempEl.innerHTML = "";
36875         this.el.remove();
36876         this.el = null;
36877     },
36878     
36879     /**
36880      * form - if the content panel contains a form - this is a reference to it.
36881      * @type {Roo.form.Form}
36882      */
36883     form : false,
36884     /**
36885      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
36886      *    This contains a reference to it.
36887      * @type {Roo.View}
36888      */
36889     view : false,
36890     
36891       /**
36892      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
36893      * <pre><code>
36894
36895 layout.addxtype({
36896        xtype : 'Form',
36897        items: [ .... ]
36898    }
36899 );
36900
36901 </code></pre>
36902      * @param {Object} cfg Xtype definition of item to add.
36903      */
36904     
36905     
36906     getChildContainer: function () {
36907         return this.getEl();
36908     }
36909     
36910     
36911     /*
36912         var  ret = new Roo.factory(cfg);
36913         return ret;
36914         
36915         
36916         // add form..
36917         if (cfg.xtype.match(/^Form$/)) {
36918             
36919             var el;
36920             //if (this.footer) {
36921             //    el = this.footer.container.insertSibling(false, 'before');
36922             //} else {
36923                 el = this.el.createChild();
36924             //}
36925
36926             this.form = new  Roo.form.Form(cfg);
36927             
36928             
36929             if ( this.form.allItems.length) {
36930                 this.form.render(el.dom);
36931             }
36932             return this.form;
36933         }
36934         // should only have one of theses..
36935         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
36936             // views.. should not be just added - used named prop 'view''
36937             
36938             cfg.el = this.el.appendChild(document.createElement("div"));
36939             // factory?
36940             
36941             var ret = new Roo.factory(cfg);
36942              
36943              ret.render && ret.render(false, ''); // render blank..
36944             this.view = ret;
36945             return ret;
36946         }
36947         return false;
36948     }
36949     \*/
36950 });
36951  
36952 /**
36953  * @class Roo.bootstrap.panel.Grid
36954  * @extends Roo.bootstrap.panel.Content
36955  * @constructor
36956  * Create a new GridPanel.
36957  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
36958  * @param {Object} config A the config object
36959   
36960  */
36961
36962
36963
36964 Roo.bootstrap.panel.Grid = function(config)
36965 {
36966     
36967       
36968     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
36969         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
36970
36971     config.el = this.wrapper;
36972     //this.el = this.wrapper;
36973     
36974       if (config.container) {
36975         // ctor'ed from a Border/panel.grid
36976         
36977         
36978         this.wrapper.setStyle("overflow", "hidden");
36979         this.wrapper.addClass('roo-grid-container');
36980
36981     }
36982     
36983     
36984     if(config.toolbar){
36985         var tool_el = this.wrapper.createChild();    
36986         this.toolbar = Roo.factory(config.toolbar);
36987         var ti = [];
36988         if (config.toolbar.items) {
36989             ti = config.toolbar.items ;
36990             delete config.toolbar.items ;
36991         }
36992         
36993         var nitems = [];
36994         this.toolbar.render(tool_el);
36995         for(var i =0;i < ti.length;i++) {
36996           //  Roo.log(['add child', items[i]]);
36997             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
36998         }
36999         this.toolbar.items = nitems;
37000         
37001         delete config.toolbar;
37002     }
37003     
37004     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
37005     config.grid.scrollBody = true;;
37006     config.grid.monitorWindowResize = false; // turn off autosizing
37007     config.grid.autoHeight = false;
37008     config.grid.autoWidth = false;
37009     
37010     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
37011     
37012     if (config.background) {
37013         // render grid on panel activation (if panel background)
37014         this.on('activate', function(gp) {
37015             if (!gp.grid.rendered) {
37016                 gp.grid.render(this.wrapper);
37017                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
37018             }
37019         });
37020             
37021     } else {
37022         this.grid.render(this.wrapper);
37023         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
37024
37025     }
37026     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
37027     // ??? needed ??? config.el = this.wrapper;
37028     
37029     
37030     
37031   
37032     // xtype created footer. - not sure if will work as we normally have to render first..
37033     if (this.footer && !this.footer.el && this.footer.xtype) {
37034         
37035         var ctr = this.grid.getView().getFooterPanel(true);
37036         this.footer.dataSource = this.grid.dataSource;
37037         this.footer = Roo.factory(this.footer, Roo);
37038         this.footer.render(ctr);
37039         
37040     }
37041     
37042     
37043     
37044     
37045      
37046 };
37047
37048 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
37049     getId : function(){
37050         return this.grid.id;
37051     },
37052     
37053     /**
37054      * Returns the grid for this panel
37055      * @return {Roo.bootstrap.Table} 
37056      */
37057     getGrid : function(){
37058         return this.grid;    
37059     },
37060     
37061     setSize : function(width, height){
37062         if(!this.ignoreResize(width, height)){
37063             var grid = this.grid;
37064             var size = this.adjustForComponents(width, height);
37065             var gridel = grid.getGridEl();
37066             gridel.setSize(size.width, size.height);
37067             /*
37068             var thd = grid.getGridEl().select('thead',true).first();
37069             var tbd = grid.getGridEl().select('tbody', true).first();
37070             if (tbd) {
37071                 tbd.setSize(width, height - thd.getHeight());
37072             }
37073             */
37074             grid.autoSize();
37075         }
37076     },
37077      
37078     
37079     
37080     beforeSlide : function(){
37081         this.grid.getView().scroller.clip();
37082     },
37083     
37084     afterSlide : function(){
37085         this.grid.getView().scroller.unclip();
37086     },
37087     
37088     destroy : function(){
37089         this.grid.destroy();
37090         delete this.grid;
37091         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
37092     }
37093 });
37094
37095 /**
37096  * @class Roo.bootstrap.panel.Nest
37097  * @extends Roo.bootstrap.panel.Content
37098  * @constructor
37099  * Create a new Panel, that can contain a layout.Border.
37100  * 
37101  * 
37102  * @param {Roo.BorderLayout} layout The layout for this panel
37103  * @param {String/Object} config A string to set only the title or a config object
37104  */
37105 Roo.bootstrap.panel.Nest = function(config)
37106 {
37107     // construct with only one argument..
37108     /* FIXME - implement nicer consturctors
37109     if (layout.layout) {
37110         config = layout;
37111         layout = config.layout;
37112         delete config.layout;
37113     }
37114     if (layout.xtype && !layout.getEl) {
37115         // then layout needs constructing..
37116         layout = Roo.factory(layout, Roo);
37117     }
37118     */
37119     
37120     config.el =  config.layout.getEl();
37121     
37122     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
37123     
37124     config.layout.monitorWindowResize = false; // turn off autosizing
37125     this.layout = config.layout;
37126     this.layout.getEl().addClass("roo-layout-nested-layout");
37127     
37128     
37129     
37130     
37131 };
37132
37133 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
37134
37135     setSize : function(width, height){
37136         if(!this.ignoreResize(width, height)){
37137             var size = this.adjustForComponents(width, height);
37138             var el = this.layout.getEl();
37139             if (size.height < 1) {
37140                 el.setWidth(size.width);   
37141             } else {
37142                 el.setSize(size.width, size.height);
37143             }
37144             var touch = el.dom.offsetWidth;
37145             this.layout.layout();
37146             // ie requires a double layout on the first pass
37147             if(Roo.isIE && !this.initialized){
37148                 this.initialized = true;
37149                 this.layout.layout();
37150             }
37151         }
37152     },
37153     
37154     // activate all subpanels if not currently active..
37155     
37156     setActiveState : function(active){
37157         this.active = active;
37158         this.setActiveClass(active);
37159         
37160         if(!active){
37161             this.fireEvent("deactivate", this);
37162             return;
37163         }
37164         
37165         this.fireEvent("activate", this);
37166         // not sure if this should happen before or after..
37167         if (!this.layout) {
37168             return; // should not happen..
37169         }
37170         var reg = false;
37171         for (var r in this.layout.regions) {
37172             reg = this.layout.getRegion(r);
37173             if (reg.getActivePanel()) {
37174                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
37175                 reg.setActivePanel(reg.getActivePanel());
37176                 continue;
37177             }
37178             if (!reg.panels.length) {
37179                 continue;
37180             }
37181             reg.showPanel(reg.getPanel(0));
37182         }
37183         
37184         
37185         
37186         
37187     },
37188     
37189     /**
37190      * Returns the nested BorderLayout for this panel
37191      * @return {Roo.BorderLayout} 
37192      */
37193     getLayout : function(){
37194         return this.layout;
37195     },
37196     
37197      /**
37198      * Adds a xtype elements to the layout of the nested panel
37199      * <pre><code>
37200
37201 panel.addxtype({
37202        xtype : 'ContentPanel',
37203        region: 'west',
37204        items: [ .... ]
37205    }
37206 );
37207
37208 panel.addxtype({
37209         xtype : 'NestedLayoutPanel',
37210         region: 'west',
37211         layout: {
37212            center: { },
37213            west: { }   
37214         },
37215         items : [ ... list of content panels or nested layout panels.. ]
37216    }
37217 );
37218 </code></pre>
37219      * @param {Object} cfg Xtype definition of item to add.
37220      */
37221     addxtype : function(cfg) {
37222         return this.layout.addxtype(cfg);
37223     
37224     }
37225 });        /*
37226  * Based on:
37227  * Ext JS Library 1.1.1
37228  * Copyright(c) 2006-2007, Ext JS, LLC.
37229  *
37230  * Originally Released Under LGPL - original licence link has changed is not relivant.
37231  *
37232  * Fork - LGPL
37233  * <script type="text/javascript">
37234  */
37235 /**
37236  * @class Roo.TabPanel
37237  * @extends Roo.util.Observable
37238  * A lightweight tab container.
37239  * <br><br>
37240  * Usage:
37241  * <pre><code>
37242 // basic tabs 1, built from existing content
37243 var tabs = new Roo.TabPanel("tabs1");
37244 tabs.addTab("script", "View Script");
37245 tabs.addTab("markup", "View Markup");
37246 tabs.activate("script");
37247
37248 // more advanced tabs, built from javascript
37249 var jtabs = new Roo.TabPanel("jtabs");
37250 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
37251
37252 // set up the UpdateManager
37253 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
37254 var updater = tab2.getUpdateManager();
37255 updater.setDefaultUrl("ajax1.htm");
37256 tab2.on('activate', updater.refresh, updater, true);
37257
37258 // Use setUrl for Ajax loading
37259 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
37260 tab3.setUrl("ajax2.htm", null, true);
37261
37262 // Disabled tab
37263 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
37264 tab4.disable();
37265
37266 jtabs.activate("jtabs-1");
37267  * </code></pre>
37268  * @constructor
37269  * Create a new TabPanel.
37270  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
37271  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
37272  */
37273 Roo.bootstrap.panel.Tabs = function(config){
37274     /**
37275     * The container element for this TabPanel.
37276     * @type Roo.Element
37277     */
37278     this.el = Roo.get(config.el);
37279     delete config.el;
37280     if(config){
37281         if(typeof config == "boolean"){
37282             this.tabPosition = config ? "bottom" : "top";
37283         }else{
37284             Roo.apply(this, config);
37285         }
37286     }
37287     
37288     if(this.tabPosition == "bottom"){
37289         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37290         this.el.addClass("roo-tabs-bottom");
37291     }
37292     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
37293     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
37294     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
37295     if(Roo.isIE){
37296         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
37297     }
37298     if(this.tabPosition != "bottom"){
37299         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
37300          * @type Roo.Element
37301          */
37302         this.bodyEl = Roo.get(this.createBody(this.el.dom));
37303         this.el.addClass("roo-tabs-top");
37304     }
37305     this.items = [];
37306
37307     this.bodyEl.setStyle("position", "relative");
37308
37309     this.active = null;
37310     this.activateDelegate = this.activate.createDelegate(this);
37311
37312     this.addEvents({
37313         /**
37314          * @event tabchange
37315          * Fires when the active tab changes
37316          * @param {Roo.TabPanel} this
37317          * @param {Roo.TabPanelItem} activePanel The new active tab
37318          */
37319         "tabchange": true,
37320         /**
37321          * @event beforetabchange
37322          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
37323          * @param {Roo.TabPanel} this
37324          * @param {Object} e Set cancel to true on this object to cancel the tab change
37325          * @param {Roo.TabPanelItem} tab The tab being changed to
37326          */
37327         "beforetabchange" : true
37328     });
37329
37330     Roo.EventManager.onWindowResize(this.onResize, this);
37331     this.cpad = this.el.getPadding("lr");
37332     this.hiddenCount = 0;
37333
37334
37335     // toolbar on the tabbar support...
37336     if (this.toolbar) {
37337         alert("no toolbar support yet");
37338         this.toolbar  = false;
37339         /*
37340         var tcfg = this.toolbar;
37341         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
37342         this.toolbar = new Roo.Toolbar(tcfg);
37343         if (Roo.isSafari) {
37344             var tbl = tcfg.container.child('table', true);
37345             tbl.setAttribute('width', '100%');
37346         }
37347         */
37348         
37349     }
37350    
37351
37352
37353     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
37354 };
37355
37356 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
37357     /*
37358      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
37359      */
37360     tabPosition : "top",
37361     /*
37362      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
37363      */
37364     currentTabWidth : 0,
37365     /*
37366      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
37367      */
37368     minTabWidth : 40,
37369     /*
37370      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
37371      */
37372     maxTabWidth : 250,
37373     /*
37374      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
37375      */
37376     preferredTabWidth : 175,
37377     /*
37378      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
37379      */
37380     resizeTabs : false,
37381     /*
37382      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
37383      */
37384     monitorResize : true,
37385     /*
37386      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
37387      */
37388     toolbar : false,
37389
37390     /**
37391      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
37392      * @param {String} id The id of the div to use <b>or create</b>
37393      * @param {String} text The text for the tab
37394      * @param {String} content (optional) Content to put in the TabPanelItem body
37395      * @param {Boolean} closable (optional) True to create a close icon on the tab
37396      * @return {Roo.TabPanelItem} The created TabPanelItem
37397      */
37398     addTab : function(id, text, content, closable, tpl)
37399     {
37400         var item = new Roo.bootstrap.panel.TabItem({
37401             panel: this,
37402             id : id,
37403             text : text,
37404             closable : closable,
37405             tpl : tpl
37406         });
37407         this.addTabItem(item);
37408         if(content){
37409             item.setContent(content);
37410         }
37411         return item;
37412     },
37413
37414     /**
37415      * Returns the {@link Roo.TabPanelItem} with the specified id/index
37416      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
37417      * @return {Roo.TabPanelItem}
37418      */
37419     getTab : function(id){
37420         return this.items[id];
37421     },
37422
37423     /**
37424      * Hides the {@link Roo.TabPanelItem} with the specified id/index
37425      * @param {String/Number} id The id or index of the TabPanelItem to hide.
37426      */
37427     hideTab : function(id){
37428         var t = this.items[id];
37429         if(!t.isHidden()){
37430            t.setHidden(true);
37431            this.hiddenCount++;
37432            this.autoSizeTabs();
37433         }
37434     },
37435
37436     /**
37437      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
37438      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
37439      */
37440     unhideTab : function(id){
37441         var t = this.items[id];
37442         if(t.isHidden()){
37443            t.setHidden(false);
37444            this.hiddenCount--;
37445            this.autoSizeTabs();
37446         }
37447     },
37448
37449     /**
37450      * Adds an existing {@link Roo.TabPanelItem}.
37451      * @param {Roo.TabPanelItem} item The TabPanelItem to add
37452      */
37453     addTabItem : function(item){
37454         this.items[item.id] = item;
37455         this.items.push(item);
37456       //  if(this.resizeTabs){
37457     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
37458   //         this.autoSizeTabs();
37459 //        }else{
37460 //            item.autoSize();
37461        // }
37462     },
37463
37464     /**
37465      * Removes a {@link Roo.TabPanelItem}.
37466      * @param {String/Number} id The id or index of the TabPanelItem to remove.
37467      */
37468     removeTab : function(id){
37469         var items = this.items;
37470         var tab = items[id];
37471         if(!tab) { return; }
37472         var index = items.indexOf(tab);
37473         if(this.active == tab && items.length > 1){
37474             var newTab = this.getNextAvailable(index);
37475             if(newTab) {
37476                 newTab.activate();
37477             }
37478         }
37479         this.stripEl.dom.removeChild(tab.pnode.dom);
37480         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
37481             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
37482         }
37483         items.splice(index, 1);
37484         delete this.items[tab.id];
37485         tab.fireEvent("close", tab);
37486         tab.purgeListeners();
37487         this.autoSizeTabs();
37488     },
37489
37490     getNextAvailable : function(start){
37491         var items = this.items;
37492         var index = start;
37493         // look for a next tab that will slide over to
37494         // replace the one being removed
37495         while(index < items.length){
37496             var item = items[++index];
37497             if(item && !item.isHidden()){
37498                 return item;
37499             }
37500         }
37501         // if one isn't found select the previous tab (on the left)
37502         index = start;
37503         while(index >= 0){
37504             var item = items[--index];
37505             if(item && !item.isHidden()){
37506                 return item;
37507             }
37508         }
37509         return null;
37510     },
37511
37512     /**
37513      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
37514      * @param {String/Number} id The id or index of the TabPanelItem to disable.
37515      */
37516     disableTab : function(id){
37517         var tab = this.items[id];
37518         if(tab && this.active != tab){
37519             tab.disable();
37520         }
37521     },
37522
37523     /**
37524      * Enables a {@link Roo.TabPanelItem} that is disabled.
37525      * @param {String/Number} id The id or index of the TabPanelItem to enable.
37526      */
37527     enableTab : function(id){
37528         var tab = this.items[id];
37529         tab.enable();
37530     },
37531
37532     /**
37533      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
37534      * @param {String/Number} id The id or index of the TabPanelItem to activate.
37535      * @return {Roo.TabPanelItem} The TabPanelItem.
37536      */
37537     activate : function(id){
37538         var tab = this.items[id];
37539         if(!tab){
37540             return null;
37541         }
37542         if(tab == this.active || tab.disabled){
37543             return tab;
37544         }
37545         var e = {};
37546         this.fireEvent("beforetabchange", this, e, tab);
37547         if(e.cancel !== true && !tab.disabled){
37548             if(this.active){
37549                 this.active.hide();
37550             }
37551             this.active = this.items[id];
37552             this.active.show();
37553             this.fireEvent("tabchange", this, this.active);
37554         }
37555         return tab;
37556     },
37557
37558     /**
37559      * Gets the active {@link Roo.TabPanelItem}.
37560      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
37561      */
37562     getActiveTab : function(){
37563         return this.active;
37564     },
37565
37566     /**
37567      * Updates the tab body element to fit the height of the container element
37568      * for overflow scrolling
37569      * @param {Number} targetHeight (optional) Override the starting height from the elements height
37570      */
37571     syncHeight : function(targetHeight){
37572         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37573         var bm = this.bodyEl.getMargins();
37574         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
37575         this.bodyEl.setHeight(newHeight);
37576         return newHeight;
37577     },
37578
37579     onResize : function(){
37580         if(this.monitorResize){
37581             this.autoSizeTabs();
37582         }
37583     },
37584
37585     /**
37586      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
37587      */
37588     beginUpdate : function(){
37589         this.updating = true;
37590     },
37591
37592     /**
37593      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
37594      */
37595     endUpdate : function(){
37596         this.updating = false;
37597         this.autoSizeTabs();
37598     },
37599
37600     /**
37601      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
37602      */
37603     autoSizeTabs : function(){
37604         var count = this.items.length;
37605         var vcount = count - this.hiddenCount;
37606         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
37607             return;
37608         }
37609         var w = Math.max(this.el.getWidth() - this.cpad, 10);
37610         var availWidth = Math.floor(w / vcount);
37611         var b = this.stripBody;
37612         if(b.getWidth() > w){
37613             var tabs = this.items;
37614             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
37615             if(availWidth < this.minTabWidth){
37616                 /*if(!this.sleft){    // incomplete scrolling code
37617                     this.createScrollButtons();
37618                 }
37619                 this.showScroll();
37620                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
37621             }
37622         }else{
37623             if(this.currentTabWidth < this.preferredTabWidth){
37624                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
37625             }
37626         }
37627     },
37628
37629     /**
37630      * Returns the number of tabs in this TabPanel.
37631      * @return {Number}
37632      */
37633      getCount : function(){
37634          return this.items.length;
37635      },
37636
37637     /**
37638      * Resizes all the tabs to the passed width
37639      * @param {Number} The new width
37640      */
37641     setTabWidth : function(width){
37642         this.currentTabWidth = width;
37643         for(var i = 0, len = this.items.length; i < len; i++) {
37644                 if(!this.items[i].isHidden()) {
37645                 this.items[i].setWidth(width);
37646             }
37647         }
37648     },
37649
37650     /**
37651      * Destroys this TabPanel
37652      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
37653      */
37654     destroy : function(removeEl){
37655         Roo.EventManager.removeResizeListener(this.onResize, this);
37656         for(var i = 0, len = this.items.length; i < len; i++){
37657             this.items[i].purgeListeners();
37658         }
37659         if(removeEl === true){
37660             this.el.update("");
37661             this.el.remove();
37662         }
37663     },
37664     
37665     createStrip : function(container)
37666     {
37667         var strip = document.createElement("nav");
37668         strip.className = "navbar navbar-default"; //"x-tabs-wrap";
37669         container.appendChild(strip);
37670         return strip;
37671     },
37672     
37673     createStripList : function(strip)
37674     {
37675         // div wrapper for retard IE
37676         // returns the "tr" element.
37677         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
37678         //'<div class="x-tabs-strip-wrap">'+
37679           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
37680           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
37681         return strip.firstChild; //.firstChild.firstChild.firstChild;
37682     },
37683     createBody : function(container)
37684     {
37685         var body = document.createElement("div");
37686         Roo.id(body, "tab-body");
37687         //Roo.fly(body).addClass("x-tabs-body");
37688         Roo.fly(body).addClass("tab-content");
37689         container.appendChild(body);
37690         return body;
37691     },
37692     createItemBody :function(bodyEl, id){
37693         var body = Roo.getDom(id);
37694         if(!body){
37695             body = document.createElement("div");
37696             body.id = id;
37697         }
37698         //Roo.fly(body).addClass("x-tabs-item-body");
37699         Roo.fly(body).addClass("tab-pane");
37700          bodyEl.insertBefore(body, bodyEl.firstChild);
37701         return body;
37702     },
37703     /** @private */
37704     createStripElements :  function(stripEl, text, closable, tpl)
37705     {
37706         var td = document.createElement("li"); // was td..
37707         
37708         
37709         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
37710         
37711         
37712         stripEl.appendChild(td);
37713         /*if(closable){
37714             td.className = "x-tabs-closable";
37715             if(!this.closeTpl){
37716                 this.closeTpl = new Roo.Template(
37717                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37718                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
37719                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
37720                 );
37721             }
37722             var el = this.closeTpl.overwrite(td, {"text": text});
37723             var close = el.getElementsByTagName("div")[0];
37724             var inner = el.getElementsByTagName("em")[0];
37725             return {"el": el, "close": close, "inner": inner};
37726         } else {
37727         */
37728         // not sure what this is..
37729 //            if(!this.tabTpl){
37730                 //this.tabTpl = new Roo.Template(
37731                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
37732                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
37733                 //);
37734 //                this.tabTpl = new Roo.Template(
37735 //                   '<a href="#">' +
37736 //                   '<span unselectable="on"' +
37737 //                            (this.disableTooltips ? '' : ' title="{text}"') +
37738 //                            ' >{text}</span></a>'
37739 //                );
37740 //                
37741 //            }
37742
37743
37744             var template = tpl || this.tabTpl || false;
37745             
37746             if(!template){
37747                 
37748                 template = new Roo.Template(
37749                    '<a href="#">' +
37750                    '<span unselectable="on"' +
37751                             (this.disableTooltips ? '' : ' title="{text}"') +
37752                             ' >{text}</span></a>'
37753                 );
37754             }
37755             
37756             switch (typeof(template)) {
37757                 case 'object' :
37758                     break;
37759                 case 'string' :
37760                     template = new Roo.Template(template);
37761                     break;
37762                 default :
37763                     break;
37764             }
37765             
37766             var el = template.overwrite(td, {"text": text});
37767             
37768             var inner = el.getElementsByTagName("span")[0];
37769             
37770             return {"el": el, "inner": inner};
37771             
37772     }
37773         
37774     
37775 });
37776
37777 /**
37778  * @class Roo.TabPanelItem
37779  * @extends Roo.util.Observable
37780  * Represents an individual item (tab plus body) in a TabPanel.
37781  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
37782  * @param {String} id The id of this TabPanelItem
37783  * @param {String} text The text for the tab of this TabPanelItem
37784  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
37785  */
37786 Roo.bootstrap.panel.TabItem = function(config){
37787     /**
37788      * The {@link Roo.TabPanel} this TabPanelItem belongs to
37789      * @type Roo.TabPanel
37790      */
37791     this.tabPanel = config.panel;
37792     /**
37793      * The id for this TabPanelItem
37794      * @type String
37795      */
37796     this.id = config.id;
37797     /** @private */
37798     this.disabled = false;
37799     /** @private */
37800     this.text = config.text;
37801     /** @private */
37802     this.loaded = false;
37803     this.closable = config.closable;
37804
37805     /**
37806      * The body element for this TabPanelItem.
37807      * @type Roo.Element
37808      */
37809     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
37810     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
37811     this.bodyEl.setStyle("display", "block");
37812     this.bodyEl.setStyle("zoom", "1");
37813     //this.hideAction();
37814
37815     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
37816     /** @private */
37817     this.el = Roo.get(els.el);
37818     this.inner = Roo.get(els.inner, true);
37819     this.textEl = Roo.get(this.el.dom.firstChild, true);
37820     this.pnode = Roo.get(els.el.parentNode, true);
37821 //    this.el.on("mousedown", this.onTabMouseDown, this);
37822     this.el.on("click", this.onTabClick, this);
37823     /** @private */
37824     if(config.closable){
37825         var c = Roo.get(els.close, true);
37826         c.dom.title = this.closeText;
37827         c.addClassOnOver("close-over");
37828         c.on("click", this.closeClick, this);
37829      }
37830
37831     this.addEvents({
37832          /**
37833          * @event activate
37834          * Fires when this tab becomes the active tab.
37835          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37836          * @param {Roo.TabPanelItem} this
37837          */
37838         "activate": true,
37839         /**
37840          * @event beforeclose
37841          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
37842          * @param {Roo.TabPanelItem} this
37843          * @param {Object} e Set cancel to true on this object to cancel the close.
37844          */
37845         "beforeclose": true,
37846         /**
37847          * @event close
37848          * Fires when this tab is closed.
37849          * @param {Roo.TabPanelItem} this
37850          */
37851          "close": true,
37852         /**
37853          * @event deactivate
37854          * Fires when this tab is no longer the active tab.
37855          * @param {Roo.TabPanel} tabPanel The parent TabPanel
37856          * @param {Roo.TabPanelItem} this
37857          */
37858          "deactivate" : true
37859     });
37860     this.hidden = false;
37861
37862     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
37863 };
37864
37865 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
37866            {
37867     purgeListeners : function(){
37868        Roo.util.Observable.prototype.purgeListeners.call(this);
37869        this.el.removeAllListeners();
37870     },
37871     /**
37872      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
37873      */
37874     show : function(){
37875         this.pnode.addClass("active");
37876         this.showAction();
37877         if(Roo.isOpera){
37878             this.tabPanel.stripWrap.repaint();
37879         }
37880         this.fireEvent("activate", this.tabPanel, this);
37881     },
37882
37883     /**
37884      * Returns true if this tab is the active tab.
37885      * @return {Boolean}
37886      */
37887     isActive : function(){
37888         return this.tabPanel.getActiveTab() == this;
37889     },
37890
37891     /**
37892      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
37893      */
37894     hide : function(){
37895         this.pnode.removeClass("active");
37896         this.hideAction();
37897         this.fireEvent("deactivate", this.tabPanel, this);
37898     },
37899
37900     hideAction : function(){
37901         this.bodyEl.hide();
37902         this.bodyEl.setStyle("position", "absolute");
37903         this.bodyEl.setLeft("-20000px");
37904         this.bodyEl.setTop("-20000px");
37905     },
37906
37907     showAction : function(){
37908         this.bodyEl.setStyle("position", "relative");
37909         this.bodyEl.setTop("");
37910         this.bodyEl.setLeft("");
37911         this.bodyEl.show();
37912     },
37913
37914     /**
37915      * Set the tooltip for the tab.
37916      * @param {String} tooltip The tab's tooltip
37917      */
37918     setTooltip : function(text){
37919         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
37920             this.textEl.dom.qtip = text;
37921             this.textEl.dom.removeAttribute('title');
37922         }else{
37923             this.textEl.dom.title = text;
37924         }
37925     },
37926
37927     onTabClick : function(e){
37928         e.preventDefault();
37929         this.tabPanel.activate(this.id);
37930     },
37931
37932     onTabMouseDown : function(e){
37933         e.preventDefault();
37934         this.tabPanel.activate(this.id);
37935     },
37936 /*
37937     getWidth : function(){
37938         return this.inner.getWidth();
37939     },
37940
37941     setWidth : function(width){
37942         var iwidth = width - this.pnode.getPadding("lr");
37943         this.inner.setWidth(iwidth);
37944         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
37945         this.pnode.setWidth(width);
37946     },
37947 */
37948     /**
37949      * Show or hide the tab
37950      * @param {Boolean} hidden True to hide or false to show.
37951      */
37952     setHidden : function(hidden){
37953         this.hidden = hidden;
37954         this.pnode.setStyle("display", hidden ? "none" : "");
37955     },
37956
37957     /**
37958      * Returns true if this tab is "hidden"
37959      * @return {Boolean}
37960      */
37961     isHidden : function(){
37962         return this.hidden;
37963     },
37964
37965     /**
37966      * Returns the text for this tab
37967      * @return {String}
37968      */
37969     getText : function(){
37970         return this.text;
37971     },
37972     /*
37973     autoSize : function(){
37974         //this.el.beginMeasure();
37975         this.textEl.setWidth(1);
37976         /*
37977          *  #2804 [new] Tabs in Roojs
37978          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
37979          */
37980         //this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
37981         //this.el.endMeasure();
37982     //},
37983
37984     /**
37985      * Sets the text for the tab (Note: this also sets the tooltip text)
37986      * @param {String} text The tab's text and tooltip
37987      */
37988     setText : function(text){
37989         this.text = text;
37990         this.textEl.update(text);
37991         this.setTooltip(text);
37992         //if(!this.tabPanel.resizeTabs){
37993         //    this.autoSize();
37994         //}
37995     },
37996     /**
37997      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
37998      */
37999     activate : function(){
38000         this.tabPanel.activate(this.id);
38001     },
38002
38003     /**
38004      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
38005      */
38006     disable : function(){
38007         if(this.tabPanel.active != this){
38008             this.disabled = true;
38009             this.pnode.addClass("disabled");
38010         }
38011     },
38012
38013     /**
38014      * Enables this TabPanelItem if it was previously disabled.
38015      */
38016     enable : function(){
38017         this.disabled = false;
38018         this.pnode.removeClass("disabled");
38019     },
38020
38021     /**
38022      * Sets the content for this TabPanelItem.
38023      * @param {String} content The content
38024      * @param {Boolean} loadScripts true to look for and load scripts
38025      */
38026     setContent : function(content, loadScripts){
38027         this.bodyEl.update(content, loadScripts);
38028     },
38029
38030     /**
38031      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
38032      * @return {Roo.UpdateManager} The UpdateManager
38033      */
38034     getUpdateManager : function(){
38035         return this.bodyEl.getUpdateManager();
38036     },
38037
38038     /**
38039      * Set a URL to be used to load the content for this TabPanelItem.
38040      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
38041      * @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)
38042      * @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)
38043      * @return {Roo.UpdateManager} The UpdateManager
38044      */
38045     setUrl : function(url, params, loadOnce){
38046         if(this.refreshDelegate){
38047             this.un('activate', this.refreshDelegate);
38048         }
38049         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
38050         this.on("activate", this.refreshDelegate);
38051         return this.bodyEl.getUpdateManager();
38052     },
38053
38054     /** @private */
38055     _handleRefresh : function(url, params, loadOnce){
38056         if(!loadOnce || !this.loaded){
38057             var updater = this.bodyEl.getUpdateManager();
38058             updater.update(url, params, this._setLoaded.createDelegate(this));
38059         }
38060     },
38061
38062     /**
38063      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
38064      *   Will fail silently if the setUrl method has not been called.
38065      *   This does not activate the panel, just updates its content.
38066      */
38067     refresh : function(){
38068         if(this.refreshDelegate){
38069            this.loaded = false;
38070            this.refreshDelegate();
38071         }
38072     },
38073
38074     /** @private */
38075     _setLoaded : function(){
38076         this.loaded = true;
38077     },
38078
38079     /** @private */
38080     closeClick : function(e){
38081         var o = {};
38082         e.stopEvent();
38083         this.fireEvent("beforeclose", this, o);
38084         if(o.cancel !== true){
38085             this.tabPanel.removeTab(this.id);
38086         }
38087     },
38088     /**
38089      * The text displayed in the tooltip for the close icon.
38090      * @type String
38091      */
38092     closeText : "Close this tab"
38093 });
38094 /**
38095 *    This script refer to:
38096 *    Title: International Telephone Input
38097 *    Author: Jack O'Connor
38098 *    Code version:  v12.1.12
38099 *    Availability: https://github.com/jackocnr/intl-tel-input.git
38100 **/
38101
38102 Roo.bootstrap.PhoneInputData = function() {
38103     var d = [
38104       [
38105         "Afghanistan (‫افغانستان‬‎)",
38106         "af",
38107         "93"
38108       ],
38109       [
38110         "Albania (Shqipëri)",
38111         "al",
38112         "355"
38113       ],
38114       [
38115         "Algeria (‫الجزائر‬‎)",
38116         "dz",
38117         "213"
38118       ],
38119       [
38120         "American Samoa",
38121         "as",
38122         "1684"
38123       ],
38124       [
38125         "Andorra",
38126         "ad",
38127         "376"
38128       ],
38129       [
38130         "Angola",
38131         "ao",
38132         "244"
38133       ],
38134       [
38135         "Anguilla",
38136         "ai",
38137         "1264"
38138       ],
38139       [
38140         "Antigua and Barbuda",
38141         "ag",
38142         "1268"
38143       ],
38144       [
38145         "Argentina",
38146         "ar",
38147         "54"
38148       ],
38149       [
38150         "Armenia (Հայաստան)",
38151         "am",
38152         "374"
38153       ],
38154       [
38155         "Aruba",
38156         "aw",
38157         "297"
38158       ],
38159       [
38160         "Australia",
38161         "au",
38162         "61",
38163         0
38164       ],
38165       [
38166         "Austria (Österreich)",
38167         "at",
38168         "43"
38169       ],
38170       [
38171         "Azerbaijan (Azərbaycan)",
38172         "az",
38173         "994"
38174       ],
38175       [
38176         "Bahamas",
38177         "bs",
38178         "1242"
38179       ],
38180       [
38181         "Bahrain (‫البحرين‬‎)",
38182         "bh",
38183         "973"
38184       ],
38185       [
38186         "Bangladesh (বাংলাদেশ)",
38187         "bd",
38188         "880"
38189       ],
38190       [
38191         "Barbados",
38192         "bb",
38193         "1246"
38194       ],
38195       [
38196         "Belarus (Беларусь)",
38197         "by",
38198         "375"
38199       ],
38200       [
38201         "Belgium (België)",
38202         "be",
38203         "32"
38204       ],
38205       [
38206         "Belize",
38207         "bz",
38208         "501"
38209       ],
38210       [
38211         "Benin (Bénin)",
38212         "bj",
38213         "229"
38214       ],
38215       [
38216         "Bermuda",
38217         "bm",
38218         "1441"
38219       ],
38220       [
38221         "Bhutan (འབྲུག)",
38222         "bt",
38223         "975"
38224       ],
38225       [
38226         "Bolivia",
38227         "bo",
38228         "591"
38229       ],
38230       [
38231         "Bosnia and Herzegovina (Босна и Херцеговина)",
38232         "ba",
38233         "387"
38234       ],
38235       [
38236         "Botswana",
38237         "bw",
38238         "267"
38239       ],
38240       [
38241         "Brazil (Brasil)",
38242         "br",
38243         "55"
38244       ],
38245       [
38246         "British Indian Ocean Territory",
38247         "io",
38248         "246"
38249       ],
38250       [
38251         "British Virgin Islands",
38252         "vg",
38253         "1284"
38254       ],
38255       [
38256         "Brunei",
38257         "bn",
38258         "673"
38259       ],
38260       [
38261         "Bulgaria (България)",
38262         "bg",
38263         "359"
38264       ],
38265       [
38266         "Burkina Faso",
38267         "bf",
38268         "226"
38269       ],
38270       [
38271         "Burundi (Uburundi)",
38272         "bi",
38273         "257"
38274       ],
38275       [
38276         "Cambodia (កម្ពុជា)",
38277         "kh",
38278         "855"
38279       ],
38280       [
38281         "Cameroon (Cameroun)",
38282         "cm",
38283         "237"
38284       ],
38285       [
38286         "Canada",
38287         "ca",
38288         "1",
38289         1,
38290         ["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"]
38291       ],
38292       [
38293         "Cape Verde (Kabu Verdi)",
38294         "cv",
38295         "238"
38296       ],
38297       [
38298         "Caribbean Netherlands",
38299         "bq",
38300         "599",
38301         1
38302       ],
38303       [
38304         "Cayman Islands",
38305         "ky",
38306         "1345"
38307       ],
38308       [
38309         "Central African Republic (République centrafricaine)",
38310         "cf",
38311         "236"
38312       ],
38313       [
38314         "Chad (Tchad)",
38315         "td",
38316         "235"
38317       ],
38318       [
38319         "Chile",
38320         "cl",
38321         "56"
38322       ],
38323       [
38324         "China (中国)",
38325         "cn",
38326         "86"
38327       ],
38328       [
38329         "Christmas Island",
38330         "cx",
38331         "61",
38332         2
38333       ],
38334       [
38335         "Cocos (Keeling) Islands",
38336         "cc",
38337         "61",
38338         1
38339       ],
38340       [
38341         "Colombia",
38342         "co",
38343         "57"
38344       ],
38345       [
38346         "Comoros (‫جزر القمر‬‎)",
38347         "km",
38348         "269"
38349       ],
38350       [
38351         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
38352         "cd",
38353         "243"
38354       ],
38355       [
38356         "Congo (Republic) (Congo-Brazzaville)",
38357         "cg",
38358         "242"
38359       ],
38360       [
38361         "Cook Islands",
38362         "ck",
38363         "682"
38364       ],
38365       [
38366         "Costa Rica",
38367         "cr",
38368         "506"
38369       ],
38370       [
38371         "Côte d’Ivoire",
38372         "ci",
38373         "225"
38374       ],
38375       [
38376         "Croatia (Hrvatska)",
38377         "hr",
38378         "385"
38379       ],
38380       [
38381         "Cuba",
38382         "cu",
38383         "53"
38384       ],
38385       [
38386         "Curaçao",
38387         "cw",
38388         "599",
38389         0
38390       ],
38391       [
38392         "Cyprus (Κύπρος)",
38393         "cy",
38394         "357"
38395       ],
38396       [
38397         "Czech Republic (Česká republika)",
38398         "cz",
38399         "420"
38400       ],
38401       [
38402         "Denmark (Danmark)",
38403         "dk",
38404         "45"
38405       ],
38406       [
38407         "Djibouti",
38408         "dj",
38409         "253"
38410       ],
38411       [
38412         "Dominica",
38413         "dm",
38414         "1767"
38415       ],
38416       [
38417         "Dominican Republic (República Dominicana)",
38418         "do",
38419         "1",
38420         2,
38421         ["809", "829", "849"]
38422       ],
38423       [
38424         "Ecuador",
38425         "ec",
38426         "593"
38427       ],
38428       [
38429         "Egypt (‫مصر‬‎)",
38430         "eg",
38431         "20"
38432       ],
38433       [
38434         "El Salvador",
38435         "sv",
38436         "503"
38437       ],
38438       [
38439         "Equatorial Guinea (Guinea Ecuatorial)",
38440         "gq",
38441         "240"
38442       ],
38443       [
38444         "Eritrea",
38445         "er",
38446         "291"
38447       ],
38448       [
38449         "Estonia (Eesti)",
38450         "ee",
38451         "372"
38452       ],
38453       [
38454         "Ethiopia",
38455         "et",
38456         "251"
38457       ],
38458       [
38459         "Falkland Islands (Islas Malvinas)",
38460         "fk",
38461         "500"
38462       ],
38463       [
38464         "Faroe Islands (Føroyar)",
38465         "fo",
38466         "298"
38467       ],
38468       [
38469         "Fiji",
38470         "fj",
38471         "679"
38472       ],
38473       [
38474         "Finland (Suomi)",
38475         "fi",
38476         "358",
38477         0
38478       ],
38479       [
38480         "France",
38481         "fr",
38482         "33"
38483       ],
38484       [
38485         "French Guiana (Guyane française)",
38486         "gf",
38487         "594"
38488       ],
38489       [
38490         "French Polynesia (Polynésie française)",
38491         "pf",
38492         "689"
38493       ],
38494       [
38495         "Gabon",
38496         "ga",
38497         "241"
38498       ],
38499       [
38500         "Gambia",
38501         "gm",
38502         "220"
38503       ],
38504       [
38505         "Georgia (საქართველო)",
38506         "ge",
38507         "995"
38508       ],
38509       [
38510         "Germany (Deutschland)",
38511         "de",
38512         "49"
38513       ],
38514       [
38515         "Ghana (Gaana)",
38516         "gh",
38517         "233"
38518       ],
38519       [
38520         "Gibraltar",
38521         "gi",
38522         "350"
38523       ],
38524       [
38525         "Greece (Ελλάδα)",
38526         "gr",
38527         "30"
38528       ],
38529       [
38530         "Greenland (Kalaallit Nunaat)",
38531         "gl",
38532         "299"
38533       ],
38534       [
38535         "Grenada",
38536         "gd",
38537         "1473"
38538       ],
38539       [
38540         "Guadeloupe",
38541         "gp",
38542         "590",
38543         0
38544       ],
38545       [
38546         "Guam",
38547         "gu",
38548         "1671"
38549       ],
38550       [
38551         "Guatemala",
38552         "gt",
38553         "502"
38554       ],
38555       [
38556         "Guernsey",
38557         "gg",
38558         "44",
38559         1
38560       ],
38561       [
38562         "Guinea (Guinée)",
38563         "gn",
38564         "224"
38565       ],
38566       [
38567         "Guinea-Bissau (Guiné Bissau)",
38568         "gw",
38569         "245"
38570       ],
38571       [
38572         "Guyana",
38573         "gy",
38574         "592"
38575       ],
38576       [
38577         "Haiti",
38578         "ht",
38579         "509"
38580       ],
38581       [
38582         "Honduras",
38583         "hn",
38584         "504"
38585       ],
38586       [
38587         "Hong Kong (香港)",
38588         "hk",
38589         "852"
38590       ],
38591       [
38592         "Hungary (Magyarország)",
38593         "hu",
38594         "36"
38595       ],
38596       [
38597         "Iceland (Ísland)",
38598         "is",
38599         "354"
38600       ],
38601       [
38602         "India (भारत)",
38603         "in",
38604         "91"
38605       ],
38606       [
38607         "Indonesia",
38608         "id",
38609         "62"
38610       ],
38611       [
38612         "Iran (‫ایران‬‎)",
38613         "ir",
38614         "98"
38615       ],
38616       [
38617         "Iraq (‫العراق‬‎)",
38618         "iq",
38619         "964"
38620       ],
38621       [
38622         "Ireland",
38623         "ie",
38624         "353"
38625       ],
38626       [
38627         "Isle of Man",
38628         "im",
38629         "44",
38630         2
38631       ],
38632       [
38633         "Israel (‫ישראל‬‎)",
38634         "il",
38635         "972"
38636       ],
38637       [
38638         "Italy (Italia)",
38639         "it",
38640         "39",
38641         0
38642       ],
38643       [
38644         "Jamaica",
38645         "jm",
38646         "1876"
38647       ],
38648       [
38649         "Japan (日本)",
38650         "jp",
38651         "81"
38652       ],
38653       [
38654         "Jersey",
38655         "je",
38656         "44",
38657         3
38658       ],
38659       [
38660         "Jordan (‫الأردن‬‎)",
38661         "jo",
38662         "962"
38663       ],
38664       [
38665         "Kazakhstan (Казахстан)",
38666         "kz",
38667         "7",
38668         1
38669       ],
38670       [
38671         "Kenya",
38672         "ke",
38673         "254"
38674       ],
38675       [
38676         "Kiribati",
38677         "ki",
38678         "686"
38679       ],
38680       [
38681         "Kosovo",
38682         "xk",
38683         "383"
38684       ],
38685       [
38686         "Kuwait (‫الكويت‬‎)",
38687         "kw",
38688         "965"
38689       ],
38690       [
38691         "Kyrgyzstan (Кыргызстан)",
38692         "kg",
38693         "996"
38694       ],
38695       [
38696         "Laos (ລາວ)",
38697         "la",
38698         "856"
38699       ],
38700       [
38701         "Latvia (Latvija)",
38702         "lv",
38703         "371"
38704       ],
38705       [
38706         "Lebanon (‫لبنان‬‎)",
38707         "lb",
38708         "961"
38709       ],
38710       [
38711         "Lesotho",
38712         "ls",
38713         "266"
38714       ],
38715       [
38716         "Liberia",
38717         "lr",
38718         "231"
38719       ],
38720       [
38721         "Libya (‫ليبيا‬‎)",
38722         "ly",
38723         "218"
38724       ],
38725       [
38726         "Liechtenstein",
38727         "li",
38728         "423"
38729       ],
38730       [
38731         "Lithuania (Lietuva)",
38732         "lt",
38733         "370"
38734       ],
38735       [
38736         "Luxembourg",
38737         "lu",
38738         "352"
38739       ],
38740       [
38741         "Macau (澳門)",
38742         "mo",
38743         "853"
38744       ],
38745       [
38746         "Macedonia (FYROM) (Македонија)",
38747         "mk",
38748         "389"
38749       ],
38750       [
38751         "Madagascar (Madagasikara)",
38752         "mg",
38753         "261"
38754       ],
38755       [
38756         "Malawi",
38757         "mw",
38758         "265"
38759       ],
38760       [
38761         "Malaysia",
38762         "my",
38763         "60"
38764       ],
38765       [
38766         "Maldives",
38767         "mv",
38768         "960"
38769       ],
38770       [
38771         "Mali",
38772         "ml",
38773         "223"
38774       ],
38775       [
38776         "Malta",
38777         "mt",
38778         "356"
38779       ],
38780       [
38781         "Marshall Islands",
38782         "mh",
38783         "692"
38784       ],
38785       [
38786         "Martinique",
38787         "mq",
38788         "596"
38789       ],
38790       [
38791         "Mauritania (‫موريتانيا‬‎)",
38792         "mr",
38793         "222"
38794       ],
38795       [
38796         "Mauritius (Moris)",
38797         "mu",
38798         "230"
38799       ],
38800       [
38801         "Mayotte",
38802         "yt",
38803         "262",
38804         1
38805       ],
38806       [
38807         "Mexico (México)",
38808         "mx",
38809         "52"
38810       ],
38811       [
38812         "Micronesia",
38813         "fm",
38814         "691"
38815       ],
38816       [
38817         "Moldova (Republica Moldova)",
38818         "md",
38819         "373"
38820       ],
38821       [
38822         "Monaco",
38823         "mc",
38824         "377"
38825       ],
38826       [
38827         "Mongolia (Монгол)",
38828         "mn",
38829         "976"
38830       ],
38831       [
38832         "Montenegro (Crna Gora)",
38833         "me",
38834         "382"
38835       ],
38836       [
38837         "Montserrat",
38838         "ms",
38839         "1664"
38840       ],
38841       [
38842         "Morocco (‫المغرب‬‎)",
38843         "ma",
38844         "212",
38845         0
38846       ],
38847       [
38848         "Mozambique (Moçambique)",
38849         "mz",
38850         "258"
38851       ],
38852       [
38853         "Myanmar (Burma) (မြန်မာ)",
38854         "mm",
38855         "95"
38856       ],
38857       [
38858         "Namibia (Namibië)",
38859         "na",
38860         "264"
38861       ],
38862       [
38863         "Nauru",
38864         "nr",
38865         "674"
38866       ],
38867       [
38868         "Nepal (नेपाल)",
38869         "np",
38870         "977"
38871       ],
38872       [
38873         "Netherlands (Nederland)",
38874         "nl",
38875         "31"
38876       ],
38877       [
38878         "New Caledonia (Nouvelle-Calédonie)",
38879         "nc",
38880         "687"
38881       ],
38882       [
38883         "New Zealand",
38884         "nz",
38885         "64"
38886       ],
38887       [
38888         "Nicaragua",
38889         "ni",
38890         "505"
38891       ],
38892       [
38893         "Niger (Nijar)",
38894         "ne",
38895         "227"
38896       ],
38897       [
38898         "Nigeria",
38899         "ng",
38900         "234"
38901       ],
38902       [
38903         "Niue",
38904         "nu",
38905         "683"
38906       ],
38907       [
38908         "Norfolk Island",
38909         "nf",
38910         "672"
38911       ],
38912       [
38913         "North Korea (조선 민주주의 인민 공화국)",
38914         "kp",
38915         "850"
38916       ],
38917       [
38918         "Northern Mariana Islands",
38919         "mp",
38920         "1670"
38921       ],
38922       [
38923         "Norway (Norge)",
38924         "no",
38925         "47",
38926         0
38927       ],
38928       [
38929         "Oman (‫عُمان‬‎)",
38930         "om",
38931         "968"
38932       ],
38933       [
38934         "Pakistan (‫پاکستان‬‎)",
38935         "pk",
38936         "92"
38937       ],
38938       [
38939         "Palau",
38940         "pw",
38941         "680"
38942       ],
38943       [
38944         "Palestine (‫فلسطين‬‎)",
38945         "ps",
38946         "970"
38947       ],
38948       [
38949         "Panama (Panamá)",
38950         "pa",
38951         "507"
38952       ],
38953       [
38954         "Papua New Guinea",
38955         "pg",
38956         "675"
38957       ],
38958       [
38959         "Paraguay",
38960         "py",
38961         "595"
38962       ],
38963       [
38964         "Peru (Perú)",
38965         "pe",
38966         "51"
38967       ],
38968       [
38969         "Philippines",
38970         "ph",
38971         "63"
38972       ],
38973       [
38974         "Poland (Polska)",
38975         "pl",
38976         "48"
38977       ],
38978       [
38979         "Portugal",
38980         "pt",
38981         "351"
38982       ],
38983       [
38984         "Puerto Rico",
38985         "pr",
38986         "1",
38987         3,
38988         ["787", "939"]
38989       ],
38990       [
38991         "Qatar (‫قطر‬‎)",
38992         "qa",
38993         "974"
38994       ],
38995       [
38996         "Réunion (La Réunion)",
38997         "re",
38998         "262",
38999         0
39000       ],
39001       [
39002         "Romania (România)",
39003         "ro",
39004         "40"
39005       ],
39006       [
39007         "Russia (Россия)",
39008         "ru",
39009         "7",
39010         0
39011       ],
39012       [
39013         "Rwanda",
39014         "rw",
39015         "250"
39016       ],
39017       [
39018         "Saint Barthélemy",
39019         "bl",
39020         "590",
39021         1
39022       ],
39023       [
39024         "Saint Helena",
39025         "sh",
39026         "290"
39027       ],
39028       [
39029         "Saint Kitts and Nevis",
39030         "kn",
39031         "1869"
39032       ],
39033       [
39034         "Saint Lucia",
39035         "lc",
39036         "1758"
39037       ],
39038       [
39039         "Saint Martin (Saint-Martin (partie française))",
39040         "mf",
39041         "590",
39042         2
39043       ],
39044       [
39045         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
39046         "pm",
39047         "508"
39048       ],
39049       [
39050         "Saint Vincent and the Grenadines",
39051         "vc",
39052         "1784"
39053       ],
39054       [
39055         "Samoa",
39056         "ws",
39057         "685"
39058       ],
39059       [
39060         "San Marino",
39061         "sm",
39062         "378"
39063       ],
39064       [
39065         "São Tomé and Príncipe (São Tomé e Príncipe)",
39066         "st",
39067         "239"
39068       ],
39069       [
39070         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
39071         "sa",
39072         "966"
39073       ],
39074       [
39075         "Senegal (Sénégal)",
39076         "sn",
39077         "221"
39078       ],
39079       [
39080         "Serbia (Србија)",
39081         "rs",
39082         "381"
39083       ],
39084       [
39085         "Seychelles",
39086         "sc",
39087         "248"
39088       ],
39089       [
39090         "Sierra Leone",
39091         "sl",
39092         "232"
39093       ],
39094       [
39095         "Singapore",
39096         "sg",
39097         "65"
39098       ],
39099       [
39100         "Sint Maarten",
39101         "sx",
39102         "1721"
39103       ],
39104       [
39105         "Slovakia (Slovensko)",
39106         "sk",
39107         "421"
39108       ],
39109       [
39110         "Slovenia (Slovenija)",
39111         "si",
39112         "386"
39113       ],
39114       [
39115         "Solomon Islands",
39116         "sb",
39117         "677"
39118       ],
39119       [
39120         "Somalia (Soomaaliya)",
39121         "so",
39122         "252"
39123       ],
39124       [
39125         "South Africa",
39126         "za",
39127         "27"
39128       ],
39129       [
39130         "South Korea (대한민국)",
39131         "kr",
39132         "82"
39133       ],
39134       [
39135         "South Sudan (‫جنوب السودان‬‎)",
39136         "ss",
39137         "211"
39138       ],
39139       [
39140         "Spain (España)",
39141         "es",
39142         "34"
39143       ],
39144       [
39145         "Sri Lanka (ශ්‍රී ලංකාව)",
39146         "lk",
39147         "94"
39148       ],
39149       [
39150         "Sudan (‫السودان‬‎)",
39151         "sd",
39152         "249"
39153       ],
39154       [
39155         "Suriname",
39156         "sr",
39157         "597"
39158       ],
39159       [
39160         "Svalbard and Jan Mayen",
39161         "sj",
39162         "47",
39163         1
39164       ],
39165       [
39166         "Swaziland",
39167         "sz",
39168         "268"
39169       ],
39170       [
39171         "Sweden (Sverige)",
39172         "se",
39173         "46"
39174       ],
39175       [
39176         "Switzerland (Schweiz)",
39177         "ch",
39178         "41"
39179       ],
39180       [
39181         "Syria (‫سوريا‬‎)",
39182         "sy",
39183         "963"
39184       ],
39185       [
39186         "Taiwan (台灣)",
39187         "tw",
39188         "886"
39189       ],
39190       [
39191         "Tajikistan",
39192         "tj",
39193         "992"
39194       ],
39195       [
39196         "Tanzania",
39197         "tz",
39198         "255"
39199       ],
39200       [
39201         "Thailand (ไทย)",
39202         "th",
39203         "66"
39204       ],
39205       [
39206         "Timor-Leste",
39207         "tl",
39208         "670"
39209       ],
39210       [
39211         "Togo",
39212         "tg",
39213         "228"
39214       ],
39215       [
39216         "Tokelau",
39217         "tk",
39218         "690"
39219       ],
39220       [
39221         "Tonga",
39222         "to",
39223         "676"
39224       ],
39225       [
39226         "Trinidad and Tobago",
39227         "tt",
39228         "1868"
39229       ],
39230       [
39231         "Tunisia (‫تونس‬‎)",
39232         "tn",
39233         "216"
39234       ],
39235       [
39236         "Turkey (Türkiye)",
39237         "tr",
39238         "90"
39239       ],
39240       [
39241         "Turkmenistan",
39242         "tm",
39243         "993"
39244       ],
39245       [
39246         "Turks and Caicos Islands",
39247         "tc",
39248         "1649"
39249       ],
39250       [
39251         "Tuvalu",
39252         "tv",
39253         "688"
39254       ],
39255       [
39256         "U.S. Virgin Islands",
39257         "vi",
39258         "1340"
39259       ],
39260       [
39261         "Uganda",
39262         "ug",
39263         "256"
39264       ],
39265       [
39266         "Ukraine (Україна)",
39267         "ua",
39268         "380"
39269       ],
39270       [
39271         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
39272         "ae",
39273         "971"
39274       ],
39275       [
39276         "United Kingdom",
39277         "gb",
39278         "44",
39279         0
39280       ],
39281       [
39282         "United States",
39283         "us",
39284         "1",
39285         0
39286       ],
39287       [
39288         "Uruguay",
39289         "uy",
39290         "598"
39291       ],
39292       [
39293         "Uzbekistan (Oʻzbekiston)",
39294         "uz",
39295         "998"
39296       ],
39297       [
39298         "Vanuatu",
39299         "vu",
39300         "678"
39301       ],
39302       [
39303         "Vatican City (Città del Vaticano)",
39304         "va",
39305         "39",
39306         1
39307       ],
39308       [
39309         "Venezuela",
39310         "ve",
39311         "58"
39312       ],
39313       [
39314         "Vietnam (Việt Nam)",
39315         "vn",
39316         "84"
39317       ],
39318       [
39319         "Wallis and Futuna (Wallis-et-Futuna)",
39320         "wf",
39321         "681"
39322       ],
39323       [
39324         "Western Sahara (‫الصحراء الغربية‬‎)",
39325         "eh",
39326         "212",
39327         1
39328       ],
39329       [
39330         "Yemen (‫اليمن‬‎)",
39331         "ye",
39332         "967"
39333       ],
39334       [
39335         "Zambia",
39336         "zm",
39337         "260"
39338       ],
39339       [
39340         "Zimbabwe",
39341         "zw",
39342         "263"
39343       ],
39344       [
39345         "Åland Islands",
39346         "ax",
39347         "358",
39348         1
39349       ]
39350   ];
39351   
39352   return d;
39353 }/**
39354 *    This script refer to:
39355 *    Title: International Telephone Input
39356 *    Author: Jack O'Connor
39357 *    Code version:  v12.1.12
39358 *    Availability: https://github.com/jackocnr/intl-tel-input.git
39359 **/
39360
39361 /**
39362  * @class Roo.bootstrap.PhoneInput
39363  * @extends Roo.bootstrap.TriggerField
39364  * An input with International dial-code selection
39365  
39366  * @cfg {String} defaultDialCode default '+852'
39367  * @cfg {Array} preferedCountries default []
39368   
39369  * @constructor
39370  * Create a new PhoneInput.
39371  * @param {Object} config Configuration options
39372  */
39373
39374 Roo.bootstrap.PhoneInput = function(config) {
39375     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
39376 };
39377
39378 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
39379         
39380         listWidth: undefined,
39381         
39382         selectedClass: 'active',
39383         
39384         invalidClass : "has-warning",
39385         
39386         validClass: 'has-success',
39387         
39388         allowed: '0123456789',
39389         
39390         /**
39391          * @cfg {String} defaultDialCode The default dial code when initializing the input
39392          */
39393         defaultDialCode: '+852',
39394         
39395         /**
39396          * @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
39397          */
39398         preferedCountries: false,
39399         
39400         getAutoCreate : function()
39401         {
39402             var data = Roo.bootstrap.PhoneInputData();
39403             var align = this.labelAlign || this.parentLabelAlign();
39404             var id = Roo.id();
39405             
39406             this.allCountries = [];
39407             this.dialCodeMapping = [];
39408             
39409             for (var i = 0; i < data.length; i++) {
39410               var c = data[i];
39411               this.allCountries[i] = {
39412                 name: c[0],
39413                 iso2: c[1],
39414                 dialCode: c[2],
39415                 priority: c[3] || 0,
39416                 areaCodes: c[4] || null
39417               };
39418               this.dialCodeMapping[c[2]] = {
39419                   name: c[0],
39420                   iso2: c[1],
39421                   priority: c[3] || 0,
39422                   areaCodes: c[4] || null
39423               };
39424             }
39425             
39426             var cfg = {
39427                 cls: 'form-group',
39428                 cn: []
39429             };
39430             
39431             var input =  {
39432                 tag: 'input',
39433                 id : id,
39434                 cls : 'form-control tel-input',
39435                 autocomplete: 'new-password'
39436             };
39437             
39438             var hiddenInput = {
39439                 tag: 'input',
39440                 type: 'hidden',
39441                 cls: 'hidden-tel-input'
39442             };
39443             
39444             if (this.name) {
39445                 hiddenInput.name = this.name;
39446             }
39447             
39448             if (this.disabled) {
39449                 input.disabled = true;
39450             }
39451             
39452             var flag_container = {
39453                 tag: 'div',
39454                 cls: 'flag-box',
39455                 cn: [
39456                     {
39457                         tag: 'div',
39458                         cls: 'flag'
39459                     },
39460                     {
39461                         tag: 'div',
39462                         cls: 'caret'
39463                     }
39464                 ]
39465             };
39466             
39467             var box = {
39468                 tag: 'div',
39469                 cls: this.hasFeedback ? 'has-feedback' : '',
39470                 cn: [
39471                     hiddenInput,
39472                     input,
39473                     {
39474                         tag: 'input',
39475                         cls: 'dial-code-holder',
39476                         disabled: true
39477                     }
39478                 ]
39479             };
39480             
39481             var container = {
39482                 cls: 'roo-select2-container input-group',
39483                 cn: [
39484                     flag_container,
39485                     box
39486                 ]
39487             };
39488             
39489             if (this.fieldLabel.length) {
39490                 var indicator = {
39491                     tag: 'i',
39492                     tooltip: 'This field is required'
39493                 };
39494                 
39495                 var label = {
39496                     tag: 'label',
39497                     'for':  id,
39498                     cls: 'control-label',
39499                     cn: []
39500                 };
39501                 
39502                 var label_text = {
39503                     tag: 'span',
39504                     html: this.fieldLabel
39505                 };
39506                 
39507                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
39508                 label.cn = [
39509                     indicator,
39510                     label_text
39511                 ];
39512                 
39513                 if(this.indicatorpos == 'right') {
39514                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
39515                     label.cn = [
39516                         label_text,
39517                         indicator
39518                     ];
39519                 }
39520                 
39521                 if(align == 'left') {
39522                     container = {
39523                         tag: 'div',
39524                         cn: [
39525                             container
39526                         ]
39527                     };
39528                     
39529                     if(this.labelWidth > 12){
39530                         label.style = "width: " + this.labelWidth + 'px';
39531                     }
39532                     if(this.labelWidth < 13 && this.labelmd == 0){
39533                         this.labelmd = this.labelWidth;
39534                     }
39535                     if(this.labellg > 0){
39536                         label.cls += ' col-lg-' + this.labellg;
39537                         input.cls += ' col-lg-' + (12 - this.labellg);
39538                     }
39539                     if(this.labelmd > 0){
39540                         label.cls += ' col-md-' + this.labelmd;
39541                         container.cls += ' col-md-' + (12 - this.labelmd);
39542                     }
39543                     if(this.labelsm > 0){
39544                         label.cls += ' col-sm-' + this.labelsm;
39545                         container.cls += ' col-sm-' + (12 - this.labelsm);
39546                     }
39547                     if(this.labelxs > 0){
39548                         label.cls += ' col-xs-' + this.labelxs;
39549                         container.cls += ' col-xs-' + (12 - this.labelxs);
39550                     }
39551                 }
39552             }
39553             
39554             cfg.cn = [
39555                 label,
39556                 container
39557             ];
39558             
39559             var settings = this;
39560             
39561             ['xs','sm','md','lg'].map(function(size){
39562                 if (settings[size]) {
39563                     cfg.cls += ' col-' + size + '-' + settings[size];
39564                 }
39565             });
39566             
39567             this.store = new Roo.data.Store({
39568                 proxy : new Roo.data.MemoryProxy({}),
39569                 reader : new Roo.data.JsonReader({
39570                     fields : [
39571                         {
39572                             'name' : 'name',
39573                             'type' : 'string'
39574                         },
39575                         {
39576                             'name' : 'iso2',
39577                             'type' : 'string'
39578                         },
39579                         {
39580                             'name' : 'dialCode',
39581                             'type' : 'string'
39582                         },
39583                         {
39584                             'name' : 'priority',
39585                             'type' : 'string'
39586                         },
39587                         {
39588                             'name' : 'areaCodes',
39589                             'type' : 'string'
39590                         }
39591                     ]
39592                 })
39593             });
39594             
39595             if(!this.preferedCountries) {
39596                 this.preferedCountries = [
39597                     'hk',
39598                     'gb',
39599                     'us'
39600                 ];
39601             }
39602             
39603             var p = this.preferedCountries.reverse();
39604             
39605             if(p) {
39606                 for (var i = 0; i < p.length; i++) {
39607                     for (var j = 0; j < this.allCountries.length; j++) {
39608                         if(this.allCountries[j].iso2 == p[i]) {
39609                             var t = this.allCountries[j];
39610                             this.allCountries.splice(j,1);
39611                             this.allCountries.unshift(t);
39612                         }
39613                     } 
39614                 }
39615             }
39616             
39617             this.store.proxy.data = {
39618                 success: true,
39619                 data: this.allCountries
39620             };
39621             
39622             return cfg;
39623         },
39624         
39625         initEvents : function()
39626         {
39627             this.createList();
39628             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
39629             
39630             this.indicator = this.indicatorEl();
39631             this.flag = this.flagEl();
39632             this.dialCodeHolder = this.dialCodeHolderEl();
39633             
39634             this.trigger = this.el.select('div.flag-box',true).first();
39635             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
39636             
39637             var _this = this;
39638             
39639             (function(){
39640                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
39641                 _this.list.setWidth(lw);
39642             }).defer(100);
39643             
39644             this.list.on('mouseover', this.onViewOver, this);
39645             this.list.on('mousemove', this.onViewMove, this);
39646             this.inputEl().on("keyup", this.onKeyUp, this);
39647             
39648             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
39649
39650             this.view = new Roo.View(this.list, this.tpl, {
39651                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
39652             });
39653             
39654             this.view.on('click', this.onViewClick, this);
39655             this.setValue(this.defaultDialCode);
39656         },
39657         
39658         onTriggerClick : function(e)
39659         {
39660             Roo.log('trigger click');
39661             if(this.disabled){
39662                 return;
39663             }
39664             
39665             if(this.isExpanded()){
39666                 this.collapse();
39667                 this.hasFocus = false;
39668             }else {
39669                 this.store.load({});
39670                 this.hasFocus = true;
39671                 this.expand();
39672             }
39673         },
39674         
39675         isExpanded : function()
39676         {
39677             return this.list.isVisible();
39678         },
39679         
39680         collapse : function()
39681         {
39682             if(!this.isExpanded()){
39683                 return;
39684             }
39685             this.list.hide();
39686             Roo.get(document).un('mousedown', this.collapseIf, this);
39687             Roo.get(document).un('mousewheel', this.collapseIf, this);
39688             this.fireEvent('collapse', this);
39689             this.validate();
39690         },
39691         
39692         expand : function()
39693         {
39694             Roo.log('expand');
39695
39696             if(this.isExpanded() || !this.hasFocus){
39697                 return;
39698             }
39699             
39700             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
39701             this.list.setWidth(lw);
39702             
39703             this.list.show();
39704             this.restrictHeight();
39705             
39706             Roo.get(document).on('mousedown', this.collapseIf, this);
39707             Roo.get(document).on('mousewheel', this.collapseIf, this);
39708             
39709             this.fireEvent('expand', this);
39710         },
39711         
39712         restrictHeight : function()
39713         {
39714             this.list.alignTo(this.inputEl(), this.listAlign);
39715             this.list.alignTo(this.inputEl(), this.listAlign);
39716         },
39717         
39718         onViewOver : function(e, t)
39719         {
39720             if(this.inKeyMode){
39721                 return;
39722             }
39723             var item = this.view.findItemFromChild(t);
39724             
39725             if(item){
39726                 var index = this.view.indexOf(item);
39727                 this.select(index, false);
39728             }
39729         },
39730
39731         // private
39732         onViewClick : function(view, doFocus, el, e)
39733         {
39734             var index = this.view.getSelectedIndexes()[0];
39735             
39736             var r = this.store.getAt(index);
39737             
39738             if(r){
39739                 this.onSelect(r, index);
39740             }
39741             if(doFocus !== false && !this.blockFocus){
39742                 this.inputEl().focus();
39743             }
39744         },
39745         
39746         onViewMove : function(e, t)
39747         {
39748             this.inKeyMode = false;
39749         },
39750         
39751         select : function(index, scrollIntoView)
39752         {
39753             this.selectedIndex = index;
39754             this.view.select(index);
39755             if(scrollIntoView !== false){
39756                 var el = this.view.getNode(index);
39757                 if(el){
39758                     this.list.scrollChildIntoView(el, false);
39759                 }
39760             }
39761         },
39762         
39763         createList : function()
39764         {
39765             this.list = Roo.get(document.body).createChild({
39766                 tag: 'ul',
39767                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
39768                 style: 'display:none'
39769             });
39770             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
39771         },
39772         
39773         collapseIf : function(e)
39774         {
39775             var in_combo  = e.within(this.el);
39776             var in_list =  e.within(this.list);
39777             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
39778             
39779             if (in_combo || in_list || is_list) {
39780                 return;
39781             }
39782             this.collapse();
39783         },
39784         
39785         onSelect : function(record, index)
39786         {
39787             if(this.fireEvent('beforeselect', this, record, index) !== false){
39788                 
39789                 this.setFlagClass(record.data.iso2);
39790                 this.setDialCode(record.data.dialCode);
39791                 this.hasFocus = false;
39792                 this.collapse();
39793                 this.fireEvent('select', this, record, index);
39794             }
39795         },
39796         
39797         flagEl : function()
39798         {
39799             var flag = this.el.select('div.flag',true).first();
39800             if(!flag){
39801                 return false;
39802             }
39803             return flag;
39804         },
39805         
39806         dialCodeHolderEl : function()
39807         {
39808             var d = this.el.select('input.dial-code-holder',true).first();
39809             if(!d){
39810                 return false;
39811             }
39812             return d;
39813         },
39814         
39815         setDialCode : function(v)
39816         {
39817             this.dialCodeHolder.dom.value = '+'+v;
39818         },
39819         
39820         setFlagClass : function(n)
39821         {
39822             this.flag.dom.className = 'flag '+n;
39823         },
39824         
39825         getValue : function()
39826         {
39827             var v = this.inputEl().getValue();
39828             if(this.dialCodeHolder) {
39829                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
39830             }
39831             return v;
39832         },
39833         
39834         setValue : function(v)
39835         {
39836             var d = this.getDialCode(v);
39837             
39838             //invalid dial code
39839             if(v.length == 0 || !d || d.length == 0) {
39840                 if(this.rendered){
39841                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
39842                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
39843                 }
39844                 return;
39845             }
39846             
39847             //valid dial code
39848             this.setFlagClass(this.dialCodeMapping[d].iso2);
39849             this.setDialCode(d);
39850             this.inputEl().dom.value = v.replace('+'+d,'');
39851             this.hiddenEl().dom.value = this.getValue();
39852             
39853             this.validate();
39854         },
39855         
39856         getDialCode : function(v = '')
39857         {
39858             if (v.length == 0) {
39859                 return this.dialCodeHolder.dom.value;
39860             }
39861             
39862             var dialCode = "";
39863             if (v.charAt(0) != "+") {
39864                 return false;
39865             }
39866             var numericChars = "";
39867             for (var i = 1; i < v.length; i++) {
39868               var c = v.charAt(i);
39869               if (!isNaN(c)) {
39870                 numericChars += c;
39871                 if (this.dialCodeMapping[numericChars]) {
39872                   dialCode = v.substr(1, i);
39873                 }
39874                 if (numericChars.length == 4) {
39875                   break;
39876                 }
39877               }
39878             }
39879             return dialCode;
39880         },
39881         
39882         reset : function()
39883         {
39884             this.setValue(this.defaultDialCode);
39885             this.validate();
39886         },
39887         
39888         hiddenEl : function()
39889         {
39890             return this.el.select('input.hidden-tel-input',true).first();
39891         },
39892         
39893         onKeyUp : function(e){
39894             
39895             var k = e.getKey();
39896             var c = e.getCharCode();
39897             
39898             if(
39899                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
39900                     this.allowed.indexOf(String.fromCharCode(c)) === -1
39901             ){
39902                 e.stopEvent();
39903             }
39904             
39905             // if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
39906             //     return;
39907             // }
39908             if(this.allowed.indexOf(String.fromCharCode(c)) === -1){
39909                 e.stopEvent();
39910             }
39911             
39912             this.setValue(this.getValue());
39913         }
39914         
39915 });
39916 /**
39917  * @class Roo.bootstrap.MoneyField
39918  * @extends Roo.bootstrap.ComboBox
39919  * Bootstrap MoneyField class
39920  * 
39921  * @constructor
39922  * Create a new MoneyField.
39923  * @param {Object} config Configuration options
39924  */
39925
39926 Roo.bootstrap.MoneyField = function(config) {
39927     
39928     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
39929     
39930 };
39931
39932 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
39933     
39934     /**
39935      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
39936      */
39937     allowDecimals : true,
39938     /**
39939      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
39940      */
39941     decimalSeparator : ".",
39942     /**
39943      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
39944      */
39945     decimalPrecision : 2,
39946     /**
39947      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
39948      */
39949     allowNegative : true,
39950     /**
39951      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
39952      */
39953     minValue : Number.NEGATIVE_INFINITY,
39954     /**
39955      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
39956      */
39957     maxValue : Number.MAX_VALUE,
39958     /**
39959      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
39960      */
39961     minText : "The minimum value for this field is {0}",
39962     /**
39963      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
39964      */
39965     maxText : "The maximum value for this field is {0}",
39966     /**
39967      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
39968      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
39969      */
39970     nanText : "{0} is not a valid number",
39971     /**
39972      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
39973      */
39974     castInt : true,
39975     /**
39976      * @cfg {String} defaults currency of the MoneyField
39977      * value should be in lkey
39978      */
39979     defaultCurrency : false,
39980     
39981     
39982     inputlg : 9,
39983     inputmd : 9,
39984     inputsm : 9,
39985     inputxs : 6,
39986     
39987     store : false,
39988     
39989     getAutoCreate : function()
39990     {
39991         var align = this.labelAlign || this.parentLabelAlign();
39992         
39993         var id = Roo.id();
39994
39995         var cfg = {
39996             cls: 'form-group',
39997             cn: []
39998         };
39999
40000         var input =  {
40001             tag: 'input',
40002             id : id,
40003             cls : 'form-control roo-money-amount-input',
40004             autocomplete: 'new-password'
40005         };
40006         
40007         if (this.name) {
40008             input.name = this.name;
40009         }
40010
40011         if (this.disabled) {
40012             input.disabled = true;
40013         }
40014
40015         var clg = 12 - this.inputlg;
40016         var cmd = 12 - this.inputmd;
40017         var csm = 12 - this.inputsm;
40018         var cxs = 12 - this.inputxs;
40019         
40020         var container = {
40021             tag : 'div',
40022             cls : 'row roo-money-field',
40023             cn : [
40024                 {
40025                     tag : 'div',
40026                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
40027                     cn : [
40028                         {
40029                             tag : 'div',
40030                             cls: 'roo-select2-container input-group',
40031                             cn: [
40032                                 {
40033                                     tag : 'input',
40034                                     cls : 'form-control roo-money-currency-input',
40035                                     autocomplete: 'new-password',
40036                                     readOnly : 1,
40037                                     name : this.currencyName
40038                                 },
40039                                 {
40040                                     tag :'span',
40041                                     cls : 'input-group-addon',
40042                                     cn : [
40043                                         {
40044                                             tag: 'span',
40045                                             cls: 'caret'
40046                                         }
40047                                     ]
40048                                 }
40049                             ]
40050                         }
40051                     ]
40052                 },
40053                 {
40054                     tag : 'div',
40055                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
40056                     cn : [
40057                         {
40058                             tag: 'div',
40059                             cls: this.hasFeedback ? 'has-feedback' : '',
40060                             cn: [
40061                                 input
40062                             ]
40063                         }
40064                     ]
40065                 }
40066             ]
40067             
40068         };
40069         
40070         if (this.fieldLabel.length) {
40071             var indicator = {
40072                 tag: 'i',
40073                 tooltip: 'This field is required'
40074             };
40075
40076             var label = {
40077                 tag: 'label',
40078                 'for':  id,
40079                 cls: 'control-label',
40080                 cn: []
40081             };
40082
40083             var label_text = {
40084                 tag: 'span',
40085                 html: this.fieldLabel
40086             };
40087
40088             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
40089             label.cn = [
40090                 indicator,
40091                 label_text
40092             ];
40093
40094             if(this.indicatorpos == 'right') {
40095                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
40096                 label.cn = [
40097                     label_text,
40098                     indicator
40099                 ];
40100             }
40101
40102             if(align == 'left') {
40103                 container = {
40104                     tag: 'div',
40105                     cn: [
40106                         container
40107                     ]
40108                 };
40109
40110                 if(this.labelWidth > 12){
40111                     label.style = "width: " + this.labelWidth + 'px';
40112                 }
40113                 if(this.labelWidth < 13 && this.labelmd == 0){
40114                     this.labelmd = this.labelWidth;
40115                 }
40116                 if(this.labellg > 0){
40117                     label.cls += ' col-lg-' + this.labellg;
40118                     input.cls += ' col-lg-' + (12 - this.labellg);
40119                 }
40120                 if(this.labelmd > 0){
40121                     label.cls += ' col-md-' + this.labelmd;
40122                     container.cls += ' col-md-' + (12 - this.labelmd);
40123                 }
40124                 if(this.labelsm > 0){
40125                     label.cls += ' col-sm-' + this.labelsm;
40126                     container.cls += ' col-sm-' + (12 - this.labelsm);
40127                 }
40128                 if(this.labelxs > 0){
40129                     label.cls += ' col-xs-' + this.labelxs;
40130                     container.cls += ' col-xs-' + (12 - this.labelxs);
40131                 }
40132             }
40133         }
40134
40135         cfg.cn = [
40136             label,
40137             container
40138         ];
40139
40140         var settings = this;
40141
40142         ['xs','sm','md','lg'].map(function(size){
40143             if (settings[size]) {
40144                 cfg.cls += ' col-' + size + '-' + settings[size];
40145             }
40146         });
40147         
40148         return cfg;
40149         
40150     },
40151     
40152     initEvents : function()
40153     {
40154         this.indicator = this.indicatorEl();
40155         
40156         this.initCurrencyEvent();
40157         
40158         this.initNumberEvent();
40159         
40160     },
40161     
40162     initCurrencyEvent : function()
40163     {
40164         if (!this.store) {
40165             throw "can not find store for combo";
40166         }
40167         
40168         this.store = Roo.factory(this.store, Roo.data);
40169         this.store.parent = this;
40170         
40171         this.createList();
40172         
40173         this.triggerEl = this.el.select('.input-group-addon', true).first();
40174         
40175         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
40176         
40177         var _this = this;
40178         
40179         (function(){
40180             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
40181             _this.list.setWidth(lw);
40182         }).defer(100);
40183         
40184         this.list.on('mouseover', this.onViewOver, this);
40185         this.list.on('mousemove', this.onViewMove, this);
40186         this.list.on('scroll', this.onViewScroll, this);
40187         
40188         if(!this.tpl){
40189             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
40190         }
40191         
40192         this.view = new Roo.View(this.list, this.tpl, {
40193             singleSelect:true, store: this.store, selectedClass: this.selectedClass
40194         });
40195         
40196         this.view.on('click', this.onViewClick, this);
40197         
40198         this.store.on('beforeload', this.onBeforeLoad, this);
40199         this.store.on('load', this.onLoad, this);
40200         this.store.on('loadexception', this.onLoadException, this);
40201         
40202         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
40203             "up" : function(e){
40204                 this.inKeyMode = true;
40205                 this.selectPrev();
40206             },
40207
40208             "down" : function(e){
40209                 if(!this.isExpanded()){
40210                     this.onTriggerClick();
40211                 }else{
40212                     this.inKeyMode = true;
40213                     this.selectNext();
40214                 }
40215             },
40216
40217             "enter" : function(e){
40218                 this.collapse();
40219                 
40220                 if(this.fireEvent("specialkey", this, e)){
40221                     this.onViewClick(false);
40222                 }
40223                 
40224                 return true;
40225             },
40226
40227             "esc" : function(e){
40228                 this.collapse();
40229             },
40230
40231             "tab" : function(e){
40232                 this.collapse();
40233                 
40234                 if(this.fireEvent("specialkey", this, e)){
40235                     this.onViewClick(false);
40236                 }
40237                 
40238                 return true;
40239             },
40240
40241             scope : this,
40242
40243             doRelay : function(foo, bar, hname){
40244                 if(hname == 'down' || this.scope.isExpanded()){
40245                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
40246                 }
40247                 return true;
40248             },
40249
40250             forceKeyDown: true
40251         });
40252         
40253         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
40254         
40255     },
40256     
40257     initNumberEvent : function(e)
40258     {
40259         this.inputEl().on("keydown" , this.fireKey,  this);
40260         this.inputEl().on("focus", this.onFocus,  this);
40261         this.inputEl().on("blur", this.onBlur,  this);
40262         
40263         this.inputEl().relayEvent('keyup', this);
40264         
40265         if(this.indicator){
40266             this.indicator.addClass('invisible');
40267         }
40268  
40269         this.originalValue = this.getValue();
40270         
40271         if(this.validationEvent == 'keyup'){
40272             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40273             this.inputEl().on('keyup', this.filterValidation, this);
40274         }
40275         else if(this.validationEvent !== false){
40276             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40277         }
40278         
40279         if(this.selectOnFocus){
40280             this.on("focus", this.preFocus, this);
40281             
40282         }
40283         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40284             this.inputEl().on("keypress", this.filterKeys, this);
40285         } else {
40286             this.inputEl().relayEvent('keypress', this);
40287         }
40288         
40289         var allowed = "0123456789";
40290         
40291         if(this.allowDecimals){
40292             allowed += this.decimalSeparator;
40293         }
40294         
40295         if(this.allowNegative){
40296             allowed += "-";
40297         }
40298         
40299         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40300         
40301         var keyPress = function(e){
40302             
40303             var k = e.getKey();
40304             
40305             var c = e.getCharCode();
40306             
40307             if(
40308                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
40309                     allowed.indexOf(String.fromCharCode(c)) === -1
40310             ){
40311                 e.stopEvent();
40312                 return;
40313             }
40314             
40315             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40316                 return;
40317             }
40318             
40319             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40320                 e.stopEvent();
40321             }
40322         };
40323         
40324         this.inputEl().on("keypress", keyPress, this);
40325         
40326     },
40327     
40328     onTriggerClick : function(e)
40329     {   
40330         if(this.disabled){
40331             return;
40332         }
40333         
40334         this.page = 0;
40335         this.loadNext = false;
40336         
40337         if(this.isExpanded()){
40338             this.collapse();
40339             return;
40340         }
40341         
40342         this.hasFocus = true;
40343         
40344         if(this.triggerAction == 'all') {
40345             this.doQuery(this.allQuery, true);
40346             return;
40347         }
40348         
40349         this.doQuery(this.getRawValue());
40350     },
40351     
40352     getCurrency : function()
40353     {   
40354         var v = this.currencyEl().getValue();
40355         
40356         return v;
40357     },
40358     
40359     restrictHeight : function()
40360     {
40361         this.list.alignTo(this.currencyEl(), this.listAlign);
40362         this.list.alignTo(this.currencyEl(), this.listAlign);
40363     },
40364     
40365     onViewClick : function(view, doFocus, el, e)
40366     {
40367         var index = this.view.getSelectedIndexes()[0];
40368         
40369         var r = this.store.getAt(index);
40370         
40371         if(r){
40372             this.onSelect(r, index);
40373         }
40374     },
40375     
40376     onSelect : function(record, index){
40377         
40378         if(this.fireEvent('beforeselect', this, record, index) !== false){
40379         
40380             this.setFromCurrencyData(index > -1 ? record.data : false);
40381             
40382             this.collapse();
40383             
40384             this.fireEvent('select', this, record, index);
40385         }
40386     },
40387     
40388     setFromCurrencyData : function(o)
40389     {
40390         var currency = '';
40391         
40392         this.lastCurrency = o;
40393         
40394         if (this.currencyField) {
40395             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
40396         } else {
40397             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
40398         }
40399         
40400         this.lastSelectionText = currency;
40401         
40402         //setting default currency
40403         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
40404             this.setCurrency(this.defaultCurrency);
40405             return;
40406         }
40407         
40408         this.setCurrency(currency);
40409     },
40410     
40411     setFromData : function(o)
40412     {
40413         var c = {};
40414         
40415         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
40416         
40417         this.setFromCurrencyData(c);
40418         
40419         var value = '';
40420         
40421         if (this.name) {
40422             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
40423         } else {
40424             Roo.log('no value set for '+ (this.name ? this.name : this.id));
40425         }
40426         
40427         this.setValue(value);
40428         
40429     },
40430     
40431     setCurrency : function(v)
40432     {   
40433         this.currencyValue = v;
40434         
40435         if(this.rendered){
40436             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
40437             this.validate();
40438         }
40439     },
40440     
40441     setValue : function(v)
40442     {
40443         v = this.fixPrecision(v);
40444         
40445         v = String(v).replace(".", this.decimalSeparator);
40446         
40447         this.value = v;
40448         
40449         if(this.rendered){
40450             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
40451             this.validate();
40452         }
40453     },
40454     
40455     getRawValue : function()
40456     {
40457         var v = this.inputEl().getValue();
40458         
40459         return v;
40460     },
40461     
40462     getValue : function()
40463     {
40464         return this.fixPrecision(this.parseValue(this.getRawValue()));
40465     },
40466     
40467     parseValue : function(value)
40468     {
40469         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40470         return isNaN(value) ? '' : value;
40471     },
40472     
40473     fixPrecision : function(value)
40474     {
40475         var nan = isNaN(value);
40476         
40477         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40478             return nan ? '' : value;
40479         }
40480         
40481         return parseFloat(value).toFixed(this.decimalPrecision);
40482     },
40483     
40484     decimalPrecisionFcn : function(v)
40485     {
40486         return Math.floor(v);
40487     },
40488     
40489     validateValue : function(value)
40490     {
40491         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
40492             return false;
40493         }
40494         
40495         var num = this.parseValue(value);
40496         
40497         if(isNaN(num)){
40498             this.markInvalid(String.format(this.nanText, value));
40499             return false;
40500         }
40501         
40502         if(num < this.minValue){
40503             this.markInvalid(String.format(this.minText, this.minValue));
40504             return false;
40505         }
40506         
40507         if(num > this.maxValue){
40508             this.markInvalid(String.format(this.maxText, this.maxValue));
40509             return false;
40510         }
40511         
40512         return true;
40513     },
40514     
40515     validate : function()
40516     {
40517         if(this.disabled || this.allowBlank){
40518             this.markValid();
40519             return true;
40520         }
40521         
40522         var currency = this.getCurrency();
40523         
40524         if(this.validateValue(this.getRawValue()) && currency.length){
40525             this.markValid();
40526             return true;
40527         }
40528         
40529         this.markInvalid();
40530         return false;
40531     },
40532     
40533     getName: function()
40534     {
40535         return this.name;
40536     },
40537     
40538     beforeBlur : function()
40539     {
40540         if(!this.castInt){
40541             return;
40542         }
40543         
40544         var v = this.parseValue(this.getRawValue());
40545         
40546         if(v){
40547             this.setValue(v);
40548         }
40549     },
40550     
40551     onBlur : function()
40552     {
40553         this.beforeBlur();
40554         
40555         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40556             //this.el.removeClass(this.focusClass);
40557         }
40558         
40559         this.hasFocus = false;
40560         
40561         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40562             this.validate();
40563         }
40564         
40565         var v = this.getValue();
40566         
40567         if(String(v) !== String(this.startValue)){
40568             this.fireEvent('change', this, v, this.startValue);
40569         }
40570         
40571         this.fireEvent("blur", this);
40572     },
40573     
40574     inputEl : function()
40575     {
40576         return this.el.select('.roo-money-amount-input', true).first();
40577     },
40578     
40579     currencyEl : function()
40580     {
40581         return this.el.select('.roo-money-currency-input', true).first();
40582     }
40583     
40584 });